原来是因为我们使用的是Base64编码,而不是Base64Url编码,我们来转换一下吧:
bash$ node const base64url = require(\'base64url\'); base64url.fromBase64("EkN+DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W/A4K8ZPJijNLis4EZsHeY559a4DFOd50/OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k/4zM3O+vtd1Ghyo4IbqKKSy6J9mTniYJPenn5+HIirE=");这就和上面的RS256签名一模一样了。
这说明我们理解了RS256 JWT签名,如果需要,我们可以自己排查问题了。
总结一下,RS256 JWT 签名就是 使用RSA算法加密经过SHA256哈希后的头和载体。
那么我们知道RS256是如何工作的,它为啥比HS256要好呢?
RS256 vs HS256 - 为什么选择 RS256使用RS256,攻击者可以轻松得到经过哈希后的头和载体。但是到这里想要重新得到签名,需要暴力破解RSA,这对于一个足够长度的键是不可能的,这里有说明。
但是这不是我们选择RS256的原因。
使用RS256,只有认证服务器有私钥,可以加签token,这大大降低了私钥丢失的风险,所以更加安全。
但是让我们选择RS256的最大原因是:便捷的更新秘钥
怎么实现秘钥更新我们知道用来验证token的公钥是公开的,并且攻击者也不能用它来做什么。因为攻击者不需要校验一个token,而是想要伪造token。
所以,我们可以自主在一个服务上发布公钥。
应用服务只需要连接这个服务器来获取公钥,然后周期性的获取,以防变化,或是周期性的变化。
所以应用服务器和认证服务器不需要同时停机更新秘钥。
那么公钥是怎么发布的呢?这里有一种方式:
JWKS 端点发布公钥的方式有很多种,但是这种会比较熟悉:JWKS是Json web key set的简称。
有很多简单的npm包可以消费端点并验证JWTs,我们可以在本主体的第二篇文章中看到。
有很多端点可以发布一些列的公钥,而不仅仅是一个。
如果你想知道端点是怎么样的,你可以看看这个在线例子,下面是我们通过HTTP GET 请求得到的例子:
{ "keys": [ { "alg": "RS256", "kty": "RSA", "use": "sig", "x5c": [ "MIIDJTCCAg2gAwIBAgIJUP6A/iwWqvedMA0GCSqGSIb3DQEBCwUAMDAxLjAsBgNVBAMTJWFuZ3VsYXJ1bml2LXNlY3VyaXR5LWNvdXJzZS5hdXRoMC5jb20wHhcNMTcwODI1MTMxNjUzWhcNMzEwNTA0MTMxNjUzWjAwMS4wLAYDVQQDEyVhbmd1bGFydW5pdi1zZWN1cml0eS1jb3Vyc2UuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUvZ+4dkT2nTfCDIwyH9K0tH4qYMGcW/KDYeh+TjBdASUS9cd741C0XMvmVSYGRP0BOLeXeaQaSdKBi8uRWFbfdjwGuB3awvGmybJZ028OF6XsnKH9eh/TQ/8M/aJ/Ft3gBHJmSZCuJ0I3JYSBEUrpCkWjkS5LtyxeCPA+usFAfixPnU5L5lyacj3t+dwdFHdkbXKUPxdVwwkEwfhlW4GJ79hsGaGIxMq6PjJ//TKkGadZxBo8FObdKuy7XrrOvug4FAKe+3H4Y5ZDoZZm5X7D0ec4USjewH1PMDR0N+KUJQMRjVul9EKg3ygyYDPOWVGNh6VC01lZL2Qq244HdxRwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRwgr0c0DYG5+GlZmPRFkg3+xMWizAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBACBV4AyYA3bTiYWZvLtYpJuikwArPFD0J5wtAh1zxIVl+XQlR+S3dfcBn+90J8A677lSu0t7Q7qsZdcsrj28BKh5QF1dAUQgZiGfV3Dfe4/P5wUaaUo5Y1wKgFiusqg/mQ+kM3D8XL/Wlpt3p804dbFnmnGRKAJnijsvM56YFSTVO0JhrKv7XeueyX9LpifAVUJh9zFsiYMSYCgBe3NIhIfi4RkpzEwvFIBwtDe2k9gwIrPFJpovZte5uvi1BQAAoVxMuv7yfMmH6D5DVrAkMBsTKXU1z3WdIKbrieiwSDIWg88RD5flreeTDaCzrlgfXyNybi4UTUshbeo6SdkRiGs=" ], "n": "wUvZ-4dkT2nTfCDIwyH9K0tH4qYMGcW_KDYeh-TjBdASUS9cd741C0XMvmVSYGRP0BOLeXeaQaSdKBi8uRWFbfdjwGuB3awvGmybJZ028OF6XsnKH9eh_TQ_8M_aJ_Ft3gBHJmSZCuJ0I3JYSBEUrpCkWjkS5LtyxeCPA-usFAfixPnU5L5lyacj3t-dwdFHdkbXKUPxdVwwkEwfhlW4GJ79hsGaGIxMq6PjJ__TKkGadZxBo8FObdKuy7XrrOvug4FAKe-3H4Y5ZDoZZm5X7D0ec4USjewH1PMDR0N-KUJQMRjVul9EKg3ygyYDPOWVGNh6VC01lZL2Qq244HdxRw", "e": "AQAB", "kid": "QzY0NjREMjkyQTI4RTU2RkE4MUJBRDExNzY1MUY1N0I4QjFCODlBOQ", "x5t": "QzY0NjREMjkyQTI4RTU2RkE4MUJBRDExNzY1MUY1N0I4QjFCODlBOQ" } ] }kid属性是一个秘钥识别码,x5c代表了一个特定的公钥。
这种方式最好的特点是他是标准的,所以我们只需要访问url,和一个消费JWKS的库,我们就可以后的验证JWTs的公钥,而不需要安装任何东西。
JWTs通常被用在公网和通用登录解决方案中,那么内部网络和私用应用怎么使用呢?
企业应用JWTs实战JWTs 也可应用于企业中,作为一种典型前置认证设置方案,这被称为安全责任。
很多公司使用的前置认证方案中,应用服务是在一个私有的网络代理后,仅仅需要在HTTP头中指明用户即可。
HTTP头中的用户身份通常是被网络中的统一模块设置,这个统一的登录页被安装在代理服务器上用来管理用户会话。
会话过期后,服务器会阻止进入,并重定向到登录页面,验证。
验证通过后,应用服务器会对在HTTP 头中设置了用户标识的请求全部进行处理。
问题是:在这种设置之下,网络中的任何人都可以通过设置HTTP 头的方式模拟一个用户。
对于这种问题有解决方案,像在应用服务器端设置ip白名单或是使用客户端正数的方式,但是大多数公司没有实施这些措施。
一个更好前置认证方案前置的认证是一个很好的方案,因为应用开发者不需要实现认证的功能,也就节省了时间,也减少了bug的产生。
那么在私有网络中,我们有更方便的方式既可以保证安全,又让应用服务器可以很便捷的开发吗
我们可以考虑一下JWT:我们放置token在HTTP 头中,而不是用户的标识。然后我们把用户的其他信息放在签名后的JWT的载体中。
应用服务器首先需要验证JWT,然后在获取用户信息:
如果签名通过,用户请求被处理
不同不通过,则可以拒绝处理