认证与授权
认证与授权是计算机安全方面的两个基础概念。
简单的说,认证解决你是谁的问题,授权解决你能干什么的问题。计算机系统解决你是谁的问题,是依靠识别与人绑定的某种凭证来做判断的,比如通过判定预设的用户名和密码是否匹配,或者读取人持有的IC卡、RFID\NFC等信息来识别人的身份,当然随着科技的发展计算机的外设已经可以识别生物特征比如指纹识别、虹膜、人脸等特征。当然,计算机系统的用户不仅仅是人,也可能是其他的机器或系统,所以有必要对各种认证的手段做一个抽象,基于声明(claims-based)的认证就是这样一种抽象。
authentication 一般包含两个步骤,第一步,用户需要安装服务提供的授权证书,或者用户需要使用API服务中已经存储的某个账户,也可以创建一个;第二步,每次发送请求到API服务时需要带上证书,因为RESTful API 是不会记录客户端与服务端的会话,无状态限制。
有些认证技术还涉注册,客户端需要安装证书,并且按需要安装用户个人的证书,客户端需要将客户端的证书和用户证书一起携带发送请求。
认证之Basic Authentication
HTTP基本身份验证是一种简单的用户名/密码方案,将用户名与密码进行Base64转码,但这种转码是可逆的。某些爬虫工具可能会获取这些请求信息,直接获取用户的账号和密码,如果采用HTTPS方式发送请求,每次请求和响应会被SSL加密,爬虫无法获取这些信息。另一个问题,由于API通常不能信任用户使用的客户端,如果用户在多个设备(平板、电脑、手机)中登录了这个API服务,其中一个设备出现安全问题,需要修改密码,那么其他设备也需要重新登录才行。为了解决第二个问题,需要对每个设备给予不同的证书。
但是因为basic authentication这种形式的不安全性,所以OAuth标准出现了。
OAuth的出现就是为了解决访问资源的安全性以及灵活性。OAuth使得第三方应用对资源的访问更加安全。
认证之OAuth
OAuth 不是一个API或者服务,而是一个验证授权(Authorization)的开放标准,所有人都有基于这个标准实现自己的OAuth。更具体来说,OAuth是一个标准,app可以用来实现secure delegated access
(安全委托访问)。 OAuth基于HTTPS,以及APIs,Service应用使用access token
来进行身份验证。
OAuth主要有OAuth 1.0a和OAuth 2.0两个版本,并且二者完全不同,且不兼容。OAuth2.0 是目前广泛使用的版本,我们多数谈论OAuth时,为OAuth2.0。OAuth2 相比OAuth 1,可以在PC端、移动端设备上使用。
OAuth的中心组件
OAuth 主要下面中心组件构成 (Central Components), 接下来会依次介绍如下这些组件。
Scopes and Consent
Actors
Clients
Tokens
Authorization Server
Flows
OAuth Scopes
Scopes即Authorizaion时的一些请求权限,即与access token绑定在一起的一组权限。OAuth Scopes将授权策略(Authorization policy decision)与授权执行分离开来。并会很明确的表示OAuth Scopes将会获得的权限范围。
OAuth Actors
OAuth的流程中,主要有如下四个角色。其关系如下图所示:
Resource Owner: 用户拥有资源服务器上面的数据。例如:我是一名Facebook的用户,我拥有我的Facebook 个人简介的信息。
Resource Server: 存储用户信息的API Service
Client: 想要访问用户的客户端
Authorization Server: OAuth的主要引擎,授权服务器,获取token
OAuth Tokens
Access token: 即客户端用来请求Resource Server(API). Access tokens通常是短暂的。access token是短暂的, 因此没有必要删除它, 只需要等待access token过期即可。
Refresh token: 当access token过期之后refresh token可以用来获取新的access token。refresh token是长期的。refresh token可以被删除。
Token从Authorization server上的不同的端点获取。主要两个端点为authorize endpoint
(授权端点)和token endpoint
(令牌端点). 授权端点主要用来获得来自用户的许可和授权,并将用户的授权信息传递给token endpoint
(令牌端点)。令牌端点对用户的授权信息,处理之后返回access token
和refresh token
。 当access token过期之后,可以使用refresh token去请求token endpoint获取新的token。(开发者在开发端点时,需要维护token的状态,refresh token rotate)
OAuth有两个流程,1.获取Authorization,2. 获取Token。这两个流程发送在不同的channel,Authorization发生在Front Channel(发生在用户浏览器)而Token发生在Back Channel。
Front Channel: 客户端通过浏览器发送Authorization请求,由浏览器重定向到Authorization Server上的Authorization Endpoint,由Authorization Server返回对话框,并询问“是否允许这个应用获取如下权限”。Authorization通过结束后通过浏览器重定向到回调URL(Callback URL)。
Back Channel: 获取Token之后,token应有由客户端应用程序使用,并与资源服务器(Resource Service)进行交互。
下面就以实际的OAuth authorization code模式结合HTTP请求来说明Front Channel和Back Channel。
Front channel
客户机应用向授权服务器的授权接口GET https://www.amazon.com/ap/oa发送申请:
GET https://www.amazon.com/ap/oa ?response_type=code &redirect_uri=http://localhost/oauth_callback &scope=profile &state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW &client_id=9c21477eb0a5e2191342
传参:
response_type:必选,希望授权服务器采用哪种Oauth 2.0流程来响应,code代表授权码流程。redirect_uri:必选,授权服务器回传授权码等信息给客户机应用的重定向接口,通常要与客户机应用注册时提供的信息保持一致。scope:必选,申请获取的资源权限。client_id:必选,客户机应用向授权服务器注册的client_id。state:推荐,不透明字符串,当授权服务器重定向到redirect_uri时,会原样返回给客户机应用,用于防止跨站请求伪造攻击(CSRF、 XSRF)。由于授权服务器会原样返回此参数,可将state值与用户在客户机应用最后浏览的URI绑定,便于授权完成后将用户重定向回最后浏览的页面。
2、授权服务器校验收到的传参response_type、redirect_uri、scope、client_id:(1)如果校验失败,则返回RFC 6749规定的错误响应。(2)如果校验通过,则要求用户输入账密,对其身份进行认证(授权服务器自行选择认证机制,OAuth 2.0不指定),用户选择是否同意对客户机应用授权,并指定允许其获取哪些资源权限,此权限范围可以大于客户机应用申请的权限,授权服务器最终只会对客户机应用批准其申请的权限。
3、授权服务器签发授权码,通过浏览器重定向到客户机应用提供的重定向接口http://localhost/oauth_callback(即步骤1的传参redirect_uri),即返回响应:
HTTP/1.1 302 Found Location: http://localhost/oauth_callback ?code=SplxlOBeZQQYbYS6WxSbIA &state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
传参:
code:必选,授权服务器签发给客户机应用的授权码。state:如果客户机应用在步骤1中提供了state参数,则授权服务器需原样返回,客户机应用没提供就不返回此参数。
Back channel
客户机应用使用收到的授权码在后台向授权服务器的令牌接口POST https://api.amazon.com/auth/o2/token请求访问令牌。发出请求时,客户机应用需提供其在授权服务器注册的client_id、client_secret,从而使授权服务器能对客户机应用的身份进行认证(OAuth 2.0列举了两种认证机制:Basic认证、POST请求体传参,但是不建议使用后者,允许授权服务器自行选择其他认证机制),即发送以下请求:
POST https://api.amazon.com/auth/o2/token
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=http://localhost/oauth_callback
传参:
grant_type:必选,客户机应用提供的凭证类型,authorization_code代表提供的是授权码。code:必选,授权服务器在步骤3中回传的授权码。redirect_uri:必选,客户机应用在步骤1中提供给授权服务器的redirect_uri。
5、授权服务器对客户机应用的身份进行认证,并校验授权码:(1)如果校验失败,则返回RFC 6749规定的错误响应。(2)如果校验通过,则签发access_token,即返回响应:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"scope":"profile",
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
传参:
access_token:必选,授权服务器签发给客户机应用的短期有效的访问令牌。token_type:必选,令牌类型,详见下文access token的具体用法(Bearer token)。expires_in:推荐,令牌有效期,单位为秒,3600秒即1小时。scope:授权服务器批准的资源权限,如果与客户机应用申请的权限不同(即批准权限小于申请权限),则必须返回此参数,如果相同,可以不用返回。refresh_token:可选,授权服务器签发给客户机应用的较长有效期的更新令牌。由于access_token有效期很短,到期后客户机应用可以使用refresh_token向授权服务器申请新的access_token,从而避免重复找用户要授权。
6、当获取到access token之后,就可以在Authorization header中使用token,进行对资源服务器的请求访问
GET https://api.amazon.com/user/profile
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
如何使用refresh_token换发新的access_token?
1、在授权服务器签发了refresh_token的情况下,如果客户机应用发现access_token到期,或者权限不足,可以使用refresh_token向授权服务器的令牌接口POST https://api.amazon.com/auth/o2/token请求新的access_token。发出请求时,客户机应用同样需提供其在授权服务器注册的client_id、client_secret,从而使授权服务器能对客户机应用的身份进行认证。
POST https://api.amazon.com/auth/o2/token
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
传参:
grant_type:必选,客户机应用提供的凭证类型,refresh_token代表提供的是更新令牌。refresh_token:必选,授权服务器在授权码流程步骤5中回传的refresh_token。scope:可选,当客户机应用要申请有更多资源权限的access_token时,使用此参数。如果只是更新已过期的access_token,则不传。
2、授权服务器对客户机应用的身份进行认证,并校验refresh_token:(1)如果校验失败,则返回RFC 6749规定的错误响应。(2)如果校验通过,且客户机应用只是要更新已过期的access_token,则签发新的access_token,返回响应与授权码流程步骤5的响应相同。(3)如果校验通过,且客户机应用要申请有更多资源权限的access_token,则检查其申请的权限是否在用户已指定的权限范围内(用户在授权码流程步骤2中指定的)。如果在此范围内,则签发新的access_token,返回响应与授权码流程步骤5的响应相同。如果超出了此范围,则询问用户是否同意授权,即重复授权码流程步骤2。
客户机应用如何把access token传递给资源服务器?
OAuth 2.0在补充规范RFC 6750 中定义了三种传递access token的方式。
1、放在请求头中传递:在请求头Authorization字段中使用Bearer这一关键字传递。资源服务器必须支持这种传递方式。
GET https://api.amazon.com/user/profile
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
2、放在表单请求体中传递:资源服务器自行选择是否支持这种传递方式。
POST https://api.amazon.com/user/profile
Content-Type: application/x-www-form-urlencoded
access_token=2YotnFZFEjr1zCsicMWpAA
3、放在URI的查询参数中传递(不建议):RFC 6750 不建议采用这种方式,如果实在无法使用前两种方式,再来考虑这种。资源服务器自行选择是否支持这种传递方式。
GET https://api.amazon.com/user/profile?access_token=2YotnFZFEjr1zCsicMWpAA
Cache-Control: no-store
access token的值是什么?
OAuth 2.0没有规定access token值的内容、格式,只要求access token对客户机应用不透明。实操中常见的格式分为两类:
1、读库令牌:access token值是一个随机生成的标识符,在数据库中存储有效期,谁颁发的,颁发给谁的,权限范围等元数据信息。校验时需先读库,优点是当授权服务器想主动撤销已签发的access token时非常方便。2、自包含令牌(例如JWT):access token值是把上述元数据信息编码、签名、加密后得到的值,优点是校验时无需读库,缺点是当授权服务器想主动撤销已签发的access token时会比较棘手。
资源服务器如何校验access token?
OAuth 2.0没有规定资源服务器如何校验access token,只是说资源服务器与授权服务器之间自己协调。实操中一般采用以下方法校验:
对于小型Web应用:资源服务器通常与授权服务器同为一体,自然能够通过读库来校验。
对于大型Web应用:授权服务器和资源服务器通常是独立部署的,有三种方式校验:
1.使用读库令牌作为access token,在数据库存储层面做共享,使得资源服务器能够通过读库来校验。
2.使用读库令牌作为access token,由授权服务器提供一个令牌校验接口,资源服务器请求该接口来校验。OAuth2.0在补充规范RFC 7662 中定义了令牌校验接口(Introspection Endpoint)的相关标准。
3.使用自包含令牌作为access token,资源服务器和授权服务器双方约定好自包含令牌的结构、签名密钥、加密方法,资源服务器按照约定规则自行校验。
客户机应用如何申请撤销token?
OAuth 2.0在补充规范RFC 7009中定义了一个由授权服务器提供的撤销接口(Revocation Endpoint)来供客户机应用申请撤销access_token或refresh_token。
当用户在客户机应用退出登录、更换账号、注销账号,或卸载了客户机应用时,客户机应用需要通知授权服务器自己不再需要该用户的令牌,授权服务器将清除与该令牌相关的授权信息。 这样可以防止被遗弃令牌的滥用,并改善用户体验,因为失效的授权将不再出现在授权服务器展示给用户的已授权客户机应用列表中。
授权服务器如何令已签发的access token失效?
在以下情景中,授权服务器可能需要撤销客户机应用对用户资源的访问权限,即令已签发的access token失效:
如上述,客户机应用申请撤销某用户的令牌。
用户明确要求取消对某客户机应用的授权。
客户机应用开发人员下架了他们的应用。
授权服务器已确定某个客户机应用被攻击了或本身是恶意的,要禁用它。
如果使用读库令牌作为access token,授权服务器在数据库中直接删除即可。如果使用自包含令牌作为access token,则比较棘手。当资源服务器只依赖自包含令牌提供的信息来校验access_token时(真正的无状态令牌验证机制),授权服务器无法影响资源服务器对access_token的校验,只能删除自己数据库中的refresh_token,当客户机应用使用此refresh_token申请新的access_token时,授权服务器将不予签发,而对于之前已签发的access_token,只能等待其自行到期。因此,自包含令牌的access_token通常有效期极短。
OAuth Flows
implicit flow: 也称之为 2 Legged OAuth 所有OAuth的过程都在浏览器中完成,且access token通过authorization request (front channel only) 直接返回。不支持refresh token。安全性不高。
Authorization code: 也称之为 3 Legged OAuth。使用front channel和back channel。front channel负责authorization code grant。back channel负责将authorization code换成(exchange)access token以及refresh token。
Client Credential flow: 对于server-to-server的场景。通常使用这种模式。在这种模式下要保证client secret不会被泄露。
Resource Owner Password Flow:类似于直接用户名,密码的模式,不推荐使用。
下图即为Authorization code模式下的主要流程图
安全性建议
使用CSRF token。state参数保证整个流程的完整性
重定向URL(redirect URIs)要在白名单内
通过client ID将authorization grant和token request确保在同一个client上发生
对于保密的client(confidential client),确保client secret不被泄露。不要将secret随代码一起发布
认证之OpenID Connect
OpenID Connect 是在OAuth2.0 协议基础上增加了身份验证层 (identity layer)。OAuth 2.0 定义了通过access token去获取请求资源的机制,但是没有定义提供用户身份信息的标准方法。OpenID Connect作为OAuth2.0的扩展,实现了Authentication的流程。OpenID Connect根据用户的id_token
来验证用户,并获取用户的基本信息。
id_token
通常是JWT(Json Web Token),JWT 是一种以Base64编码json对象的token,加密,紧凑且自成一体(self-contained),用于在网络中两个节点之间传递信息。开源标准JWT有三部分组成:
Header, 定义加密算法类型(例如:HS256)、定义类型(JWT)
Payload, 定义token携带的主要信息
Signature, 创建token的签名,
header主要用来声明使用的算法,声明claim在body中,并且签名在signature中。OpenID Connection 在OAuth2.0 的基础上额外增加了UserInfo的Endpoint。id_token
作为访问UserInfo Endpoint的凭证来获取用户的基本信息(profile,email,phone),并验证用户。
OpenID Connect流程主要涉及如下几个步骤:
1.发现获取OIDC metadata
2.执行OAuth流程,获取
id_token
和access_token
。例如:在Authorization code
模式下即为通过code来换取id_token
和access_token
。3.获取JWT签名(signature key)并且可选的动态的注册客户端应用
4.基于日期签名来本地验证JWT
id_token
,或者将id_token
发给后端backend进行验证5.根据
id_token
通过UserInfo Endpoint获取用户信息,根据access_token
获取用户其他资源信息
来源:freebuf.com 2021-05-17 11:45:02 by: Litbaizhang
请登录后发表评论
注册