HTTP API 认证授权术

https://coolshell.cn/articles/19395.html

HTTP Basic

就是使用 usernamepassword 来进行登录

技术原理

1 拼接username和 password, username:password(用冒号分隔) 

2 Base64编码, Base64("username:password")

3 放到HTTP头中,  Authorization: Basic aGFvZW86Y29vbHNoZWxsCg

4 服务端验证

但是Base64不是加密协议,而是编码协议,所以就算是有HTTPS作为安全保护,给人的感觉还是不放心

Digest Access HTTP 摘要认证

原理

  • 1 调用方发起一个普通的HTTP请求

  • 2 服务端返回错误, 且在HTTP头里的 WWW-Authenticate 包含如下信息

     WWW-Authenticate:  Digest realm="testrealm@host.com",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"
                            
                    nonce 为服务器端生成的随机数
    
  • 3 客户端做加密 HASH1=MD5(MD5(username:realm:password):nonce:cnonce)

    cnonce 客户端生成的随机数

  • 4 如果 qop 中包含了 auth

HASH2=MD5(method:digestURI)

method 就是HTTP的请求方法(GET/POST…),digestURI 是请求的URL
  • 5 如果 qop 中包含了 auth-init

HASH2=MD5(method:digestURI:MD5(entityBody))

entityBody 就是HTTP请求的整个数据体
  • 6 得到 response

` response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2)`

没有 qopresponse = MD5(HA1:nonce:HA2)

  • 7 客户端向服务端请求

    GET /dir/index.html HTTP/1.0
    Host: localhost
    Authorization: Digest username="Mufasa",
                         realm="testrealm@host.com",
                         nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                         uri="%2Fcoolshell%2Fadmin",
                         qop=auth,
                         nc=00000001,
                         cnonce="0a4f113b",
                         response="6629fae49393a05397450978507c4ef1",
                         opaque="5ccc069c403ebaf9f0171e9517f40e41"
    

App Secret Key + HMAC 用于给消息签名的技术

保证消息在传递的过程中没有被人修改, 对消息进行一个MAC算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个MAC字符串

  • HTTP的请求(方法、URI、查询字串、头、签名头,body)打个包叫 CanonicalRequest,作个SHA-256的签名,然后再做一个base16的编码

  • 把上面的这个签名和签名算法 AWS4-HMAC-SHA256、时间戳、Scop,再打一个包,叫 StringToSign

  • 准备签名,用 AWSSecretAccessKey来对日期签一个 DataKey,再用 DataKey 对要操作的Region签一个 DataRegionKey ,再对相关的服务签一个DataRegionServiceKey ,最后得到 SigningKey.

  • 用第三步的 SigningKey来对第二步的 StringToSign 签名。

  • HTTP Request

      Authorization: AWS4-HMAC-SHA256 
                     Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, 
                     SignedHeaders=content-type;host;x-amz-date, 
                     Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
    

    AKIDEXAMPLE 是 AWS Access Key ID

    服务器端会根据这个AppID来查相关的 Secret Access Key,然后再验证签名

JWT – JSON Web Tokens

  • 用户使用用户名口令到认证服务器上请求认证

  • 服务器端生成JWT Token

    认证服务器还会生成一个 Secret Key(密钥)

    对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。

    用密钥对JWT签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+’.’+Base64UrlEncode(JWT-Payload))

  • base64(header).base64(payload).signature 作为 JWT token返回客户端

  • 客户端使用JWT Token向应用服务器发送相关的请求。

这个JWT Token就像一个临时用户权证一样

服务器收到请求后

检查 JWT Token,确认签名

认证服务器有这个用户的Secret Key(密钥),所以,应用服务器得把JWT Token传给认证服务器。

认证服务器通过JWT Payload 解出用户的抽象ID,然后通过抽象ID查到登录时生成的Secret Key,然后再来检查一下签名。

认证服务器检查通过后,应用服务就可以认为这是合法请求了

对称加密,    加密解密都是认证服务器(私钥)
非对称加密,  加密认证服务器(私钥)  解密应用服务器(公钥)

OAuth 1.0 委托授权协议

例子

(User 照片所有者-用户) 使用一个Consumer(第三方照片打印服务)来打印他在Service Provider(照片存储服务)某网站上的照片

用户不想把自己的用户名和口令交给那个第三方的网络打印服务,又想让那个第三方的网络打印服务来访问自己的照片

1 [Consumer] 获取 Request Token

2 [Service Provider] 认证用户, 并授权Consumer

3 [Consumer] 获取 Access Token,  调用API访问用户的照片

详细流程

User,Consumer 和 Service Provide,又叫 3-legged flow,三脚流程

  • Consumer(第三方照片打印服务)需要先上Service Provider获得开发的 Consumer KeyConsumer Secret

    (申请有效的开发者信息)

  • User 访问 Consumer 时,ConsumerService Provide 发起请求请求 Request Token (需要对HTTP请求签名)

    用户授权第三方平台开始

  • Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)Request Token Secret (oauth_token_secret)

    Service Provider验证第三方开发者信息的有效性

  • Consumer 收到 Request Token 后,使用HTTP GET 请求把 User 切到 Service Provide 的认证页上(其中带上Request Token),让用户输入他的用户和口令。

    跳转 Service Provider 的用户登录认证

  • Service Provider 认证 User 成功后,跳回 Consumer,并返回 Request Token (oauth_token)Verification Code(oauth_verifier)

    回调, 获取 Verification Code

  • 接下来就是签名请求,用Request TokenVerification Code 换取 Access Token (oauth_token)Access Token Secret (oauth_token_secret)

    请求 access_token

  • 最后使用Access Token 访问用户授权访问资源

OAuth 2.0

Digest Access,AppID+HMAC,JWT,再到OAuth 1.0,这些API认证都是要向Client发一个密钥(或是用密码)然后用HASH或是RSA来签HTTP的请求, 安全签名机制

OAuth 2.0 完全不同于1.0, 依赖于TLS/SSL的链路加密技术(HTTPS) 放弃了签名的方式

Authorization Code Flow (3 legged flow)

最常使用的OAuth 2.0的授权许可类型

流程:

  • 用户(Resource Owner)访问第三方应用(Client)的时候,第三方应用会把用户带到认证服务器(Authorization Server)上去 , 请求的是 /authorize API

      https://login.authorization-server.com/authorize?
              client_id=6731de76-14a6-49ae-97bc-6eba6914391e
              &response_type=code
              &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
              &scope=read
              &state=xcoiv98CoolShell3kch
    
    
    
      client_id               为第三方应用的App ID
    
      response_type=code      为告诉认证服务器,我要走Authorization Code Flow。
    
      redirect_uri            意思是我跳转回第三方应用的URL
    
      scope                   意是相关的权限
    
      state                   是一个随机的字符串,主要用于防CSRF攻击。
    
  • Authorization Server收到这个URL请求后,检查 client_id redirect_uriscope是否合法

如果合法,则弹出一用户认证个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。

 认证 + 授权
  • 用户授权同意访问以后,Authorization Server 会跳转回 Client ,并以其中加入一个 Authorization Code

      添加 auth  code,  回调 (redirect_uri) client第三方应用
    
    
      https://example-client.com/callback?
          code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
          &state=xcoiv98CoolShell3kch
    
  • Client 就使用 Authorization Code 获得 Access Token

      获取token
    
      POST /oauth/token HTTP/1.1
      Host: authorization-server.com
    
      code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
      &grant_type=code
      &redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
      &client_id=6731de76-14a6-49ae-97bc-6eba6914391e
      &client_secret=JqQX2PNo9bpM0uEihUPzyrh
    
  • 获得 token 信息

      {
        "access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
        "refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
        "token_type": "bearer",
        "expires": 3600,
        "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
      }
    
    
      access_token        就是访问请求令牌了
    
      refresh_token       用于刷新 access_token
    
      id_token            是JWT的token,其中一般会包含用户的OpenID
    
  • 请求资源数据

Client Credential Flow (2 legged flow)

简化版的API认证, 认证服务器服务器的调用, 没有用户参与

流程

  • Client用自己的 client_idclient_secretAuthorization Server 要一个 Access Token

      POST /token HTTP/1.1
      Host: server.example.com
      Content-Type: application/x-www-form-urlencoded
    
      grant_type=client_credentials
      &client_id=czZCaGRSa3F0Mzpn
      &client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
    
  • 使用Access Token访问相关的资源

     {
       "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
       "token_type":"bearer",
       "expires_in":3600,
       "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
       "scope":"create"
     }
    

注意 tips

  • 编码Base64Encode签名HMAC加密RSA

    编码 为了更的传输,等同于明文,

    签名 为了信息不能被篡改,

    加密 为了不让别人看到是什么信息

  • 应该遵循HTTP的规范,把认证信息放在 Authorization HTTP 头中

  • 不要使用GET的方式在URL中放入secret之类的东西,因为很多proxy或gateway的软件会把整个URL记在Access Log文件中

  • 认证授权服务器(Authorization Server)和应用服务器(App Server)最好分开

  • 密钥Secret相当于Password,用来加密的,最好不要在网络上传输,如果要传输,最好使用TLS/SSL的安全链路

原文 –> HTTP API 认证授权术

Buy me a 肥仔水!