WEB安全认证

概述

WEB服务作为日常生活中广泛使用到的一种服务,其安全性也同时受到关注。一般的WEB服务都有对应的鉴权机制,了解WEB的安全认证机制可以帮助我们知道各种认证的流程,同时也了解到每种认证机制的安全隐患以及规避措施,在开发WEB应用过程中,可以通过合理的使用来避免一些安全问题

常用认证方式

HTTP质询响应认证框架

定义

HTTP提供了一套标准的身份验证框架:服务器可以用来针对客户端的请求发送质询(challenge),客户端根据质询提供身份验证凭证。质询与应答的工作流程如下:服务器端向客户端返回401(Unauthorized,未授权)状态码,并在WWW-Authenticate头中添加如何进行验证的信息,其中至少包含有一种质询方式。然后客户端可以在请求中添加Authorization头进行验证,其Value为身份验证的凭证信息

认证协议

认证协议是在HTTP认证首部中指定的

请求:第一条请求没有认证信息

GET /family/jeff.jpg HTTP/1.1

质询:(服务器端的质询)首部是WWW-Authenticate服务器用401状态拒绝了请求,说明需要用户提供用户名和密码。认证算法是在WWW-Authenticate首部中指定的。

HTTP/1.1 401 Authorization Required

WWW-Authentication: Basic realm=”Family”

授权:首部是Authorization 客户端重新发送请求,但这一次会附加一个Authorization首部,用来说明认证算法,用户名和密码

GET /family/jeff.jpg HTTP/1.1

Authorization: Basic YnssdjJlsdsdsdfsfsdfI

成功:首部是Authentication-Info 如果授权证书是正确的,服务器就会将200 OK文档返回。有些授权算法会在可选的Authentication-Info首部返回一些与授权会话相关的附加信息

HTTP/1.0 200 OK

实现方式

Http Basic Auth

基本认证(Basic access authentication)是一种用来允许网页浏览器或其他客户端程序在请求时提供用户名口令形式的身份凭证的一种登录验证方式

操作步骤

step1:初次访问服务端返回401并且响应头中标明认证方式,浏览器会唤起用户名和秘密出入框

step2:用户输入用户名和密码,浏览器会将用户名和密码经过Base64编码放入请求头中

认证流程

优点:使用简单,几乎所有流行的浏览器都支持HTTP Basic Auth认证模式

缺点:有安全隐患,用户名密码只是简单进过Base64编码后明文传输,特别是如果没有使用SSL/TLS这样的传输层安全的协议,很容易被拦截并破解

使用场景:公网服务一般不使用,一般使用在一些内部服务里

自带Http Basic Auth认证服务:http://www.httpbin.org/basic-auth/admin/adminpwd
 

安全风险
  • 密码作为明文传输,只是经过Base64编码,很容易被截取并破解
  • 假冒服务器欺骗,在不知道来源的情况下可以轻松获取到用户的用户名和密码
  • 服务端每次验证都需要查询数据库,获取用户密码
Digest(摘要认证)

为了弥补HTTP/1.0中的Basic认证的一些问题,在HTTP/1.1中出现了Digest摘要认证,DIGEST 认证同样使用质询 / 响应的方式(challenge/response),但不会像 BASIC 认证那样直接发送明文密码。

认证流程

客户端在首次访问接收到服务端401请求之后,Resoponse里返回需要认证的信息,客户端按照要去加密相关信息现充Authenticate再次请求,服务器收到客户端发来的请求后,根据username,查找出用户的password,用和客户端同样的方法计算出request-digest(response)。然后和收到的request-digest进行对比,如果一致,则验证成功,接受客户端的请求,成功返回结果。并带有Authentication-Info消息头。客户端根据该消息头中的参数进行服务器鉴权

Digest加密算法说明:

  • 算法一般性表示:
    H(data) = MD5(data)
    KD(secret, data) = H(concat(secret, ":", data))
     
  • 与安全信息相关的数据用A1表示
    a) 采用MD5算法:
        A1=(user):(realm):(password)
    b) 采用MD5-sess算法:
        A1=H((user):(realm):(password)):nonce:cnonce
     
  • 与安全信息无关的数据用A2表示
    a) QoP为auth或未定义:
        A2=(request-method):(uri-directive-value)
    b) QoP为auth-int:
        A2=(request-method):(uri-directive-value):H((entity-body))
     
  • 摘要值用response表示
    a) 若qop没有定义:
        response
        = KD(H(A1),<nonce>:H(A2))
        = H(H(A1),<nonce>:H(A2))
    b) 若qop为auth或auth-int:
        response
        = KD(H(A1),<nonce>:<nc>:<cnonce>:<qop>:H(A2))
        = H(H(A1),<nonce>:<nc>:<cnonce>:<qop>:H(A2))

Response = MD5(HA1:nonce:nc:cnonce:qop:HA2)

优点:有效的保护了用户密码,使用nonce避免了重放攻击

缺点:服务端每次验证都需要查询数据库,获取用户密码

安全风险
  • 离线字典攻击:攻击者可以监听认证过程,知道加密方式,可以截取密文使用字典进行暴力破解
  • 中间人攻击:Digest认证属于单向认证,客户端无法认证网络,中间人可以伪造成一个Proxy或者Register服务器,对用户进行下一步欺骗,达到中间人攻击的目的

Bearer认证(令牌认证)

Bearer认证也是http协议中的标准认证方式,在Bearer认证中的凭证称为Bearer_token 或者Access_token。该种方式的优点就是灵活方便,因为凭证的生成和验证完全由开发人员设计和实现。

认证流程

JWT

Bearer认证过程中最重要的是access_token的生成方式,目前最流行也是最常用的token编码方式就是 JSON Web Token (JWT),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的

JWTBearer流程:

JWT原理

JWT组成部分:一个 JWT token 是一个字符串,它由三部分组成,头部、载荷与签名,中间用 . 分隔,例如:xxxxx.yyyyy.zzzzz

  • 头部(header)
    头部通常由两部分组成:令牌的类型(即 JWT)和正在使用的签名算法(如 HMAC SHA256 或 RSA.)
  • 载荷(Payload)
    Payload用来存放实际需要传输的数据,除了几个官方字段之外还可以自定义一些字段,但是这部分数据只经过Base64加密,所以不能用来传输敏感信息
  • 签名(Signature)
    获取Header和Payload的内容,使用Header里面指定的加密方式,用私钥进行加密,得到最终的签名

Token验证:

服务端获取每个段的数据,采用相同的方法得到签名,如果匹配上说明没有被修改,同时如果payload里有自定义的一些校验字段,也可以增加校验逻辑,比如客户端IP

Token在网络中是直接传输的,可以轻易被截取,所以在构建Payload部分需要加上客户端的标识(例如客户端IP),服务端在验证的时候可以校验请求方和token里包含的客户端IP是同一个,防御token泄露的风险

优点:

  • 用户信息安全性,即保证重要信息不被泄露和恶意破解

  • 可以预防CSRF
  • 更方便的适应更多的应用场景,主要体现在在服务端不需要凭证状态保存,分布式场景中,不需要状态共享

  • 支持跨域,单点登录友好

缺点:

  •  JWT 是无状态的,所以它的有效期完全由自己决定,要考虑注销和续期问题

常见问题及解决办法:

  • 注销登录等场景下JWT还有效
    JWT 一旦派发出去,如果后端不增加其他逻辑的话,它在失效之前都是有效的

    1、将 JWT 存入内存数据库

    将 JWT 存入 DB 中,Redis 内存数据库在这里是不错的选择。如果需要让某个 JWT 失效就直接从 Redis 中删除这个 JWT 即可。但是,这样会导致每次使用 JWT 发送请求都要先从 DB 中查询 JWT 是否存在的步骤,而且违背了 JWT 的无状态原则。

    2、修改密钥 (Secret) :

    我们为每个用户都创建一个专属密钥,如果我们想让某个 JWT 失效,我们直接修改对应用户的密钥即可。但是,这样相比于前两种引入内存数据库带来了危害更大:

    • 如果服务是分布式的,则每次发出新的 JWT 时都必须在多台机器同步密钥。为此,你需要将密钥存储在数据库或其他外部服务中,这样和 Session 认证就没太大区别了。
    • 如果用户同时在两个浏览器打开系统,或者在手机端也打开了系统,如果它从一个地方将账号退出,那么其他地方都要重新进行登录,这是不可取的。

    3、保持令牌的有效期限短并经常轮换

    很简单的一种方式。但是,会导致用户登录状态不会被持久记录,而且需要用户经常登录。

    另外,对于修改密码后 JWT 还有效问题的解决还是比较容易的。说一种我觉得比较好的方式:使用用户的密码的哈希值对 JWT 进行签名。因此,如果密码更改,则任何先前的令牌将自动无法验证
     

  • JWT续期问题
    JWT 有效期一般都建议设置的不太长,那么 JWT 过期后如何认证,如何实现动态刷新 JWT,避免用户经常需要重新登录?

    1、类似于 Session 认证中的做法

    这种方案满足于大部分场景。假设服务端给的 JWT 有效期设置为 30 分钟,服务端每次进行校验时,如果发现 JWT 的有效期马上快过期了,服务端就重新生成 JWT 给客户端。客户端每次请求都检查新旧 JWT,如果不一致,则更新本地的 JWT。这种做法的问题是仅仅在快过期的时候请求才会更新 JWT ,对客户端不是很友好。

    2、每次请求都返回新 JWT

    这种方案的的思路很简单,但是,开销会比较大,尤其是在服务端要存储维护 JWT 的情况下。

    3、JWT 有效期设置到半夜

    这种方案是一种折衷的方案,保证了大部分用户白天可以正常登录,适用于对安全性要求不高的系统。

    4、用户登录返回两个 JWT

    第一个是 accessJWT ,它的过期时间 JWT 本身的过期时间比如半个小时,另外一个是 refreshJWT 它的过期时间更长一点比如为 1 天。客户端登录后,将 accessJWT 和 refreshJWT 保存在本地,每次访问将 accessJWT 传给服务端。服务端校验 accessJWT 的有效性,如果过期的话,就将 refreshJWT 传给服务端。如果有效,服务端就生成新的 accessJWT 给客户端。否则,客户端就重新登录即可。

Session认证

在用户第一次访问时为用户生成sesssion,利用浏览器的cookie技术保存sessionId,用户的后续所有访问都会自动带上cookie信息

认证流程

优点:用户信息存放在服务端,存储方式更加丰富

缺点:依赖cookie,分布式环境下需要设计session共享,同时cookie有被盗用的风险

安全隐患:回话信息存放在客户端的cookie里,无法避免CSRF攻击

OAuth认证

OAuth(Open Authorization)是一个关于授权(authorization)的开放网络标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,授予应用对 API 的访问权限(delegate access to APIs)。OAuth 设定了对于 API 访问的 scope 的权限,以及支持多种授权方式,以及使用场景。OAuth 提供了更好的安全性以及便利,简化了软件系统的复杂性

协议特点

  • 简单:不管是 OAuth 服务提供者还是应用开发者,都很易于理解与使用
  • 安全:没有涉及到用户密钥等信息,更安全更灵活
  • 开放:任何服务提供商都可以实现 OAuth,任何软件开发商都可以使用 OAuth

应用场景

  • 原生 app 授权:app 登录请求后台接口,为了安全认证,所有请求都带 token 信息,需要登录验证、请求后台数据
  • 前后端分离单页面应用:前后端分离框架,前端请求后台数据,需要进行 OAuth 2.0 安全认证,比如使用 vue、react 或者 h5 开发的 app
  • 第三方应用授权登录,比如 QQ,微博,微信的授权登录

认证流程

四种角色:

  • Resource owner:资源所有者,也叫用户
  • Resource server:资源服务器,服务提供商用来存储资源,以及处理对资源的请求的服务器
  • Client:客户端,也叫第三方应用,通过获取用户的授权,继而访问用户在资源服务器上的资源
  • Authorization server:认证服务器,服务提供商用来处理认证的服务器,物理上与资源服务器可以是同一台服务器

优点:安全性有保障,授权码模式下,获取token都是后端请求操作,相比于前端请求不容易被截取,安全性更高。同时提供refresh_token刷新机制

缺点:请求次数较多,客户端和认证服务器需要经过多次交互

安全风险

  • redirect_url 回调域名欺骗

    风险:redirect_url会被伪造成第三方欺诈域名,直接导致服务器返回Code被泄露

    防御:服务端必须验证clientid注册的应用与redirect_url是对应的,保证redirect_url是客户端真实有效的,服务器生成的临时Code必须是一次有效,客户端使用一次后立即失效并且有效期很短,一般推荐30s有效期,由于Code是通过redirect_url浏览器回调传输容易被截取,可以保证临时Code被客户端正常消费  后不会被再次使用
     

  • redirect_url XSS跨域攻击

    风险:攻击者给认证服务器发送的redirect_uri中有恶意脚本参数,服务器如果不校验会直接跳转

    防御:服务器端需要对redirect_url进行检查禁止特殊字符输入,并且对redirect_url进行全匹配,不做模糊匹配可以杜绝XSS攻击
     

  • State 防止CSRF

    风险:攻击者在认证服务器上注册新账号,登录第三方网站走授权拿到含有授权码的redirect_uri,发送给已在第三方网站登录的用户,用户点击就会拿到token(攻击者的),第三方网站会将攻击者在服务商的账号和用户的网站账号绑定,攻击者下次授权登录就看到的是用户在网站的信息

    防御:客户端每次请求生成唯一字符串在请求中放到state参数中,服务端认证成功返回Code会带上state参数,客户端验证state是否是自己生成的唯一串,可以确定这次请求是有客户端真实发出的,不是黑客伪造的请求
     

  • Access_Token泄露

    风险:token是存在客户端的cookie中,存在泄露风险

    防御:维持refresh_token和第三方应用的绑定,刷新失效机制的设计不允许长期有效的token存在

参考文章:

1、OAuth认证原理:Authing Share|Spring Security 集成 OAuth 2.0 认证(二) - Authing身份云

2、JWT原理介绍:JWT 超详细分析 | Laravel China 社区

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值