OAuth2
在移动端实践是不安全的,有不少的产品(包括某些上亿用户量的App),也会出现将client_secret
保存在客户端的情况。钥匙丢了、锁却不能换,对于项目而言无疑是很被动的。
因此需要在终端或者整体方案设计上,考虑规避这类问题。
Sign In With Apple
在WWDC2019推出后,今年几乎是强制性的接入(发觉大小公司并没有什么抵抗?)。尽管晚了一点,但作为苹果生态的重要一环,终究是出场了。要知道,微信、微博成长为大平台,早期支持授权登录是非常重要的。
与「使用微信登录」、「使用微博登录」一样,Sign In With Apple
其实也是基于OAuth2
和OpenID Connect
的规范来实现授权登录的。那么面对千万级别以上的开发者、全球的用户量,苹果怎么从技术方案上保证数据安全呢?其中的一个方式便是使用JSON Web Token
。
JSON Web Token概念
JSON Web Token
(简称JWT
)是一个规范,规范性文件请看RFC-7519。
看看IETF的对JWT的简介:
JSON Web Token (JWT) is a compact, URL-safe means of representing
claims to be transferred between two parties. The claims in a JWT
are encoded as a JSON object that is used as the payload of a JSON
Web Signature (JWS) structure or as the plaintext of a JSON Web
Encryption (JWE) structure, enabling the claims to be digitally
signed or integrity protected with a Message Authentication Code
(MAC) and/or encrypted.
claim是一种对所有权的宣示。JWT主要是通过对claims做签名,来保证这种所有权的有效性。这从下面介绍的JWT
的结构我们可以知道。
JSON Web Token结构
看一个例子:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 |
可以看到JWT令牌是一个由.
点号分隔为3段的字符串(无分行),并且每段是Base64编码的。
Base64解码后可以看到:
第一部分是:(Header)
1 | { |
第二部分是:(Payload)
1 | { |
第三部分是:(Signature)
1 | fͅhZ#Snp!xH΅]$ |
事实上,一个JWT就是由三部分组成:头部、有效载荷、签名。格式是Header.Payload.Signature
,三部分Base64编码后通过点号连接。
其中Header中指定了签名的算法,alg
为算法的缩写,通常的比如RS256
、HS256
。例子中是HS256
,也就是HMAC SHA256)。typ
为类型的缩写,可选的有[JWS] 与 [JWE]——这两个也是常用的JWT的两种应用。
Header
也可以有额外的字段,比如对于非对称加密签名的算法,可能有kid
字段(也就是秘钥id)等。
Payload是包含声明的有效载荷。声明是关于实体(比如用户)与其他元数据。
声明可以分为三大类,Registered Claims、Public Claims和Private Claims。其中Registered Claims是标准规定的有特定的含义的声明字段,也是最常用到的,规范里的声明字段有:
1 | "iss" (Issuer) Claim |
一般sub
主题是主要的声明。
Public Claims则可以理解为保留字段,比如OpenID
的工作组就定义了一组字段,可以参考IANA JWT。对于实际应用上,如果不考虑外部交互,拓展一般用私有声明字段即可。
也可以有自定义的声明字段。
Signature签名部分,根据header中指定的算法进行签名。比如例子中的HS256
,是这样的:
1 | HMACSHA256( |
签名算法一般是HS256
、RS256
或ES256
。HS256
是HMAC SHA256
的摘要散列算法,RS256
或ES256
则是使用非对称密钥进行签名校验。
JWS与JWE
JWT
是规范,而JWS
和JWE
就是两个实现了。JWS
主要明确签名的细节,关心的是数据防篡改,但数据是可能被窥探的;JWE
则关心数据的私密安全性,主要明确数据加解密的细节。
JWS
支持的签名算法前面提到的HMAC SHA
、RSA SHA
、ECDSA SHA
系列,每个系列根据密钥长度有不同安全强度。JWT
也是可以不签名的(header中alg为none),不签名的JWT
被称为不安全的JWT
,签名的其实就是JWS
了。JWS
签名需要用到公钥或者共享密钥来验签,这个密钥被称为JSON Web Key
,也就是JWK
。
为了支持公钥验签,JWS
会有额外的header声明。比如若使用私有部署的证书来签名,则可以声明kid
来提供jwk
的索引。苹果也是这么干的,苹果提供了服务地址来下载它用来验签的公钥。苹果下发到前端的identityToken就是一个使用苹果的私钥签名的JWT
,后面有机会再介绍苹果的授权流程。除了kid
,JWS
还支持X509证书相关的字段,比如:
- x5u: 指向X509公共证书的URL
- x5c: X509证书链
- x5t:X509证书的SHA-1指纹
- x5t#S256: X509证书的SHA-256指纹
JWE
的结构稍微有些差异,JWE
令牌由五个关键组件构建,主要包括:JOSE头、JWE加密密钥、JWE初始化向量、JWE密文和JWE身份验证标签。
- The protected header,类似于JWS的头部;
- The encrypted key,用于加密密文和其他加密数据的对称密钥;
- The initialization vector,初始IV值,有些加密方式需要额外的或者随机的数据;
- The encrypted data,密文数据;
- The authentication tag,由算法产生的附加数据,来防止密文被篡改
关注JWE
的头部的一些参数字段:
alg:定义用于加密 Content Encryption Key(CEK)的算法,必须为RSA-OAEP
enc:定义用于加密载荷数据以及提供认证标签,必须为A128CBC-HS256
cty:定义载荷的Content Type,必须为“JWT”
kid:可选,密钥索引号
zip:可选,定义内容明文压缩算法
具体的加解密流程可以参考文档说明。
JSON Web Token的潜在风险
JWT
的规范不是作为一个数据加密的用途的,比如JWS
的头部及有效载荷实际是明文传输的,主要是验证数据的有效性,避免被篡改。因此会有些潜在的问题需要注意的:
- 敏感信息泄露
- JWT的重放风险
- 算法修改攻击
- 对称加密签名密钥破解
数据的传输阶段,有效载荷是明文的,所以不要传输敏感信息。而作为身份验证的一种手段,JWT存在伪造身份恶意攻击的风险。比如JWT的重放风险,虽然这个用其他的身份验证方式同样存在的。另一种问题是算法修改攻击,校验方应该限定Header中的sig
,谨慎使用JWT库。如果JWT支持none算法,可能直接跳过签名的认证;或者攻击时将RS256修改为HS256,同样有签名验证的漏洞。可以参考这篇文章。
Author: Jason
Permalink: http://blog.knpc21.com/ios/json-web-token/
文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。
Comments