freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

JWT的认识与攻击
2018-05-01 17:44:11

0x01 初识JWT

JWT ( JSON Web Token 的缩写)是一串带有声明信息的字符串,由服务端用加密算法对信息签名来保证其完整性和不可伪造。Token里可以包含所有必要信息,这样服务端就无需保存任何关于用户或会话的信息,JWT可用于身份认证、会话状态维持、信息交换等。

JWT 由三部分构成,分别称为 headerpayloadsignature ,各部分用. 相连构成一个完整的Token,形如xxxxx.yyyyy.zzzzz

分别看下各个部分:

header

使用一个JSON格式字符串声明token的类型和签名用的算法等,形如{"alg": "HS256", "typ": "JWT"} 。该字符串经过Base64Url编码后形成JWT的第一部分xxxxx

Base64Url编码可以用这段代码直观理解:

  
from base64 import *
def base64URLen(s):
t0=b64encode(s)
t1=t0.strip('=').replace('+','-').replace('/','_')
return t1

def base64URLde(s):
t0=s.replace('-','+').replace('_','/')
t1=t0+'='*(4-len(t0)%4)%4
return b64decode(t1)

payload :

使用一个JSON格式字符串描述所要声明的信息,分为 registeredpublic 、 和 private 三类,形如{"name": "John Doe", "admin": true} ,具体信息可参考 RFC7519 的 JWT claims 部分。

同样的,该字符串经过Base64Url编码形成JWT的第二部分yyyyy

signature :

xxxxx.yyyyy 使用alg 指定的算法加密,然后再Base64Url编码得到JWT的第三部分zzzzz 。所支持的算法 类型取决于实现,但HS256none 是强制要求实现的。

0x02 简单应用

在本地运行起简单的基于Express的可发放和处理JWT的服务。

  1. 安装Node.js。Node.js是JavaScript运行时环境,采用轻量高效的事件驱动、无阻塞I/O模型,拥有最大的开源库生态nmp。

  Windows平台可在 https://nodejs.org/en/download/ 下载安装包 
*nix 平台可根据 https://nodejs.org/en/download/package-manager/ 提示使用包管理器安装
  1. 安装Express,一款基于Node.js的快速、灵活、极简的Web框架。

  # http://expressjs.com/en/starter/installing.html
mkdir D:\myapp && cd D:\myapp
(全部回车,保持默认配置)
npm init
npm install express --save
  1. 运行本地服务

新建 index.js ,内容如下

  const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => console.log('Example app listening on port 3000!'))

运行node index.js ,就可从本地访问 http://localhost:3000

  1. 安装必要模块: 适用于Node.js的JWT编解码模块 node-jwt-simple 和 cookie解析模块 cookie-parser

  # https://github.com/hokaccha/node-jwt-simple
npm install jwt-simple
npm install cookie-parser
  1. 一个简单的本地demo

  //index.js
//http://expressjs.com/en/4x/api.html
//https://github.com/hokaccha/node-jwt-simple

//一些初始化的工作
const express = require('express')
var jwt = require('jwt-simple')
var cookieParser = require('cookie-parser')
var jwt_secret = "this is a secret for jwt"
const app = express()
app.use(cookieParser())

app.get('/',(req,res)=>res.redirect('/help'))
app.get('/help', (req, res) => {
var RequstedURL=req.protocol+'://'+req.get('Host')
res.send([
'GET '+RequstedURL+'/login?user=name&pass=passwd to get your JSON Web Token ' ,
'GET '+RequstedURL+'/whoami to identify yourself'
].join('<br>'))
})

app.get('/login',(req,res)=>{
var users={
"admin":"admin_password_is_hard_to_guess",
"test":"test123"
}
var payload = {"name":req.query.user}
if(users[req.query.user]===req.query.pass){
res.cookie('jwt',jwt.encode(payload,jwt_secret))
res.send(req.query.user +' logged in')
}
else{res.send('login failed!')}
})

app.get('/whoami',(req,res)=>{
try {res.send("you are logged in as :<br>" +jwt.decode(req.cookies.jwt,jwt_secret)['name'])}
catch(err) {res.send("your JWT is :<br>"+req.cookies.jwt)}
})

app.listen(3000, () => {
console.log('Example app listening on port 3000!');
console.log('Example app listening on port 3000!')
})

0x03 攻击面

发现敏感信息

JWT中的headerpayload 虽然看起来不可读,但实际上都只经过简单编码,开发者可能误将敏感信息存储在里面。使用上述工具可以方便地解码JWT中前两部分的信息。

指定算法为none

上面提到算法 none 是JWT规范中强制要求实现的,但有些实现JWT的库直接将使用none 算法的token视为已经过校验。这样攻击者就可以设置algnone ,使signature 部分为空,然后构造包含任意payload 的JWT来欺骗服务端。


1525158849660.png

将签名算法从非对称类型改为对称类型

使用非对称加密算法(主要基于RSA、ECDSA,如S256)分发JWT的过程是使用私钥(private)加密生成JWT,使用公钥(public)解密验证。

使用对称加密算法(主要基于HMAC,如HS256)分发JWT的过程是使用同一个密钥(secret)生成和验证JWT。

如果服务端期待收到的算法类型为RS256,然后以RS256和public去验证JWT,而实际上收到的算法类型是HS256,那么服务端就可能尝试把public当作secret,然后用HS256算法解密验证JWT。

由于RS256的public人人都可获得,攻击者可以预先以public为密钥,用HS256算法伪造包含任意payload 的JWT,从而成功通过服务端的验证。


1525160523495.png

爆破密钥

JWT的安全性依赖于密钥的保密性,任何拥有密钥的人都可以构造任何内容的合法token。

当一个JSON Web Token 被分发出去,如果密钥不够强壮就存在被爆破的风险,而且整个爆破过程可以离线进行。

已经有人写了一些工具,推荐如下:

伪造密钥

有时JWT采用header 中的kid 字段关联校验算法的密钥,这个密钥可能是对称加密的密钥,也可能是非对称加密的公钥。如果能够猜测kid 和 密钥的关联性,攻击者就可能修改kid 来欺骗服务端,使其校验时使用攻击者可控的密钥,于是攻击者就可以伪造任意内容的可通过校验的JWT。

一个例子:2017 HITB Pasty

0x04 安全建议

  • 验证函数应忽略JWT中的algo 字段,预先就明确JWT使用的算法,如果需要使用多种算法,可以在header 中使用表示"key ID" 的kid 字段,查询每个kid 对应的算法。

  • JWT/JWS 标准应该移除 header 中的algo 字段。JWT的许多安全缺陷都来自于开发者依赖这一客户端可控的字段。

  • 开发者应升级相应库到最新版本,因为旧版本可能存在致命缺陷。

0x05 参考文章

# JWT
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
hign&amp;hard
  • 0 文章数
  • 0 关注者
文章目录