HTTP Authentication

今天我们来学习一个古老的话题,认证,这可是随着网络出现就有的一个东西,它的主要功能就是知道你是谁。

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

HTTP 协议提供了一种通用的框架来处理认证和访问控制,像从最早时的 HTTP 1.0 协议 RFC 1945Section 11.1 中提出的 Basic Auth, 到后面更新的 HTTP/1.1: Authentication

MDN 上有一个介绍的专题:

网络变的越来越复杂,认证的方式也在不断的升级,在当下,由 OAuth 所提出的 Bearer Authorization 成为了主流。


⭐️ ️️理解 HTTP Authentication

由于 HTTP 是无状态的,对于每一个请求,你是谁就是一个问题,所以需要在每一个请求体上附带一个信息,这个信息的作用就是我是谁。

HTTP Authentication 协议约定在 Header 中加入一个条目,然后约定 Key 和 Value 的格式。

我们把 Key 称为 Request Header Field,而 Value 中的格式又是千变万化的,为此,又有不同的标准出现,于是就有了 Authorization Scheme 来进行区分,而 Value 的格式则为

1
Authorization scheme auth-token

其中 Scheme 常见的有 Basic, Bearer, OAuth 及其它标准。


> Request Header Field:

> 格式

1
Authorization: <type> <credentials>

⭐️ ️️HTTP Authorization Scheme

在HTTP标准验证方案中,我们比较熟悉的是 “Basic” 和 “Digest”,前者将用户名密码使用BASE64编码后作为验证凭证,后者是Basic的升级版,更加安全,因为Basic是明文传输密码信息,而Digest是加密后传输。在前文介绍的Cookie认证属于Form认证,并不属于HTTP标准验证。

The general HTTP authentication framework is used by several authentication schemes. Schemes can differ in security strength and in their availability in client or server software.

The most common authentication scheme is the “Basic” authentication scheme which is introduced in more details below. IANA maintains a list of authentication schemes, but there are other schemes offered by host services, such as Amazon AWS. Common authentication schemes include:

🔅 几种常见的 Scheme


🔅 Bearer - Authentication Scheme

Bearer 认证,只有一点,后面跟的是一个字符串。就像 前面 Basic,也是一个字符串,但它是 username:password b64token 。这里可能只要求是一个字符串,即不关心你是 base64,也不关心有没有意义吧。。。

Bearer 认证

本文要介绍的 Bearer验证 也属于 HTTP 协议标准验证,它随着 OAuth 协议而开始流行,详细定义见: RFC 6570

以下这段文字,让我理解出 Authentication 和 Token 两个概念。有了这两个层次的区分。

Bearer authentication (also called token authentication) is an HTTP authentication scheme that involves security tokens called bearer tokens. The name “Bearer authentication” can be understood as “give access to the bearer of this token.”

The bearer token is a cryptic string, usually generated by the server in response to a login request. The client must send this token in the Authorization header when making requests to protected resources:

1
Authorization: Bearer <token>

The Bearer authentication scheme was originally created as part of OAuth 2.0 in RFC 6750, but is sometimes also used on its own.

但有时也会单独使用。

Similarly to Basic authentication, Bearer authentication should only be used over HTTPS (SSL).

你看 OpenId 中也有用到 Bearer 的概念。。
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#jwt-bearer-sample

好处

那么使用 Bearer 验证有什么好处呢?

  • CORS: Cookies + CORS 并不能跨不同的域名。而 Bearer 验证在任何域名下都可以使用 HTTP header 头部来传输用户信息。
  • 对移动端友好: 当你在一个原生平台 (iOS, Android, WindowsPhone 等) 时,使用 Cookie 验证并不是一个好主意,因为你得和 Cookie 容器打交道,而使用 Bearer 验证则简单的多。
  • CSRF: 因为 Bearer 验证不再依赖于 cookies, 也就避免了跨站请求攻击。
  • 标准:在 Cookie 认证中,用户未登录时,返回一个 302 到登录页面,这在非浏览器情况下很难处理,而 Bearer 验证则返回的是标准的401 challenge。

🔅 Basic - Authentication Scheme

Basic Auth 是最古老的一种认证协议,在 HTTP 1.0 中,就定认了认证的协议:

最初的 RFC 在 HTTP 1.0 里。
https://tools.ietf.org/html/rfc1945#section-11.1

The ‘Basic’ HTTP Authentication Scheme
https://tools.ietf.org/html/rfc7617

https://en.wikipedia.org/wiki/Basic_access_authentication

Basic authentication is a simple authentication scheme built into the HTTP protocol. The client sends HTTP requests with the Authorization header that contains the word Basic word followed by a space and a base64-encoded string username:password. For example, to authorize as demo / p@55w0rd the client would send:

就是用 户名:密码,b64 一下:

1
b64(username:123465) = emhpbWluZzoxMjM0NjU=

use the following header field:

1
Authorization: Basic emhpbWluZzoxMjM0NjU=

https://security.stackexchange.com/questions/108662/why-is-bearer-required-before-the-token-in-authorization-header-in-a-http-re

- Twitter Basic

这是 Twitter 的开发者中心找到的示例:

虽然不知道还有什么情景是需要这种协议的。

1
2
curl -v --compressed -u<email_address>:<password> 
"https://gnip-api.twitter.com/search/30day/accounts/<ACCOUNT-NAME>/prod/counts.json?query=from%3Atwitterdev"

🔅 其它

- Digest - Authentication Scheme

https://en.wikipedia.org/wiki/Digest_access_authentication

后来 Firefox 对此进行了升级。这里是明文。升级为。。。密码型

1
2
3
4
5
6
7
8
9
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"

- HOBA - Authentication Scheme

A new scheme has been registered in the HTTP Authentication Scheme Registry as follows:

1
Authentication Scheme Name: HOBA

- HAWK - Authentication Scheme

1
Authentication Hawk id="131", ts="32", nonce="243", ext="243", mac="mqWeAM8ZP6Dm+7vBBh0qxUKZftLC4iFmYjRnvJfcSAc="

- OAuth - Authentication Scheme

在 OAuth 1 的版本中,定义了 OAuth 的 Auth scheme,目前 Twitter 依然在严格采用。


⭐️ ️️Bearer Tokens 种类

下面是一段很不错的介绍,不把它和 OAuth 联系在一起看,很能说明 Bearer Token 的定义,用途。

A security token with the property that any party in possession of the token (a “bearer”) can use the token in any way that any other party in possession of it can. Using a bearer token does not require a bearer to prove possession of cryptographic key material (proof-of-possession).

The Bearer Token or Refresh token is created for you by the Authentication server. When a user authenticates your application (client) the authentication server then goes and generates for you a Bearer Token (refresh token) which you can then use to get an Access Token.

The Bearer Token is normally some kind of secret value created by the authentication server. It isn’t random; it is created based upon the user giving you access and the client your application getting access.

In order to access an API for example you need to use an Access Token. Access tokens are short lived (around an hour). You use the bearer token to get a new Access token. To get an access token you send the Authentication server this bearer token along with your client id. This way the server knows that the application using the bearer token is the same application that the bearer token was created for. Example: I can’t just take a bearer token created for your application and use it with my application it wont work because it wasn’t generated for me.

Google Refresh token looks something like this: 1/mZ1edKKACtPAb7zGlwSzvs72PvhAbGmB8K1ZrGxpcNM

copied from comment: I don’t think there are any restrictions on the bearer tokens you supply. Only thing I can think of is that its nice to allow more then one. For example a user can authenticate the application up to 30 times and the old bearer tokens will still work. oh and if one hasn’t been used for say 6 months I would remove it from your system. It’s your authentication server that will have to generate them and validate them so how it’s formatted is up to you.

关于 bear token,参看 RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage , 目前国内各大网站都是用不同的token,也没说必须使用bear token,只有twitter明确说明的是使用 bear token。

OAuth 2.0 (RFC 6749) 定义了 Client 如何取得 Access Token 的方法。Client 可以用 Access Token 以 Resource Owner 的名义来向 Resource Server 取得 Protected Resource ,例如我 (Resource Owner) 授权一個手机 App (Client) 以我 (Resource Owner) 的名义去 Facebook (Resource Server) 取得我的朋友名单 (Protected Resource)。OAuth 2.0 定义Access Token 是 Resource Server 用来认证的唯一方式,有了这个, Resource Server 就不需要再提供其他认证方式,例如账号密码。

然而在 RFC 6749 里面只定义抽象的概念,细节如 Access Token 格式、怎么传到 Resource Server ,以及 Access Token 无效时, Resource Server 怎么处理,都没有定义。所以在 RFC 6750 另外定义了 Bearer Token 的用法。Bearer Token 是一种 Access Token ,由 Authorization Server 在 Resource Owner 的允许下核发给 Client ,Resource Server 只要认在这个 Token 就可以认定 Client 已经获取 Resource Owner 的许可,不需要用密码学的方式来验证这个 Token 的真伪。关于Token 被偷走的安全性问题,另一篇再说。

Bearer Token 的格式

1
Authorization: Bearer <BEARER_TOKEN>

其中 BEARER_TOKEN 的格式 b64token ,ABNF 的定義:

1
b64token = 1*( ALPHA / DIGIT / “-” / “.” / “_” / “~” / “+” / “/” ) *”=”

写成 Regular Expression 即是:

1
/[A-Za-z0-9/-/._~/+//]+=*/

Bearer验证中的凭证称为 BEARER_TOKEN,或者是 access_token,它的颁发和验证完全由我们自己的应用程序来控制,而不依赖于系统和Web服务器,Bearer 验证的标准请求方式如下:

1
Authorization: Bearer <BEARER_TOKEN>

- 大厂标准

Weibo, Facebook, Wechat 签发出来的 token 用了什么标准了吗?是没有一个标准的。每个大厂在这一块,都是乱来的。

🔅JWT (JSON Web Token)

上面介绍的 Bearer 认证,其核心便是 BEARER_TOKEN,而最流行的 Token 编码方式便是:JWT/JSON WEB TOKEN。

JSON web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

- JSON Web Token 是如何工作的?

在身份验证中,当用户成功地使用它们的密码登录后,一个 JWT 就会被服务器返回,且必须存储于客户端本地(通常存于 localStorage),而不是传统做法中在服务器创建一个session,并返回一个cookie。

无论何时用户想要访问一个受保护的路由或资源,客户端应该随请求一起发送JWT。JWT通常在 Authentication HTTP头部中,使用 Bearer 格式(schema)存放。HTTP头部的内容可能长这样:

1
Authorization: Bearer <token>

这是一个无状态(stateless)的认证机制,因为用户状态并未存放在服务器内存中。服务器的受保护路由会在Authentication头部中检查是否存在合法的JWT,如果存在,用户就被允许访问受保护的资源。JWT是自包含的,所以相关的信息都在里面,减少了多次查询数据库的必要。

这让你充分可以依赖无状态的数据API,甚至向下游服务发出请求。哪个域名提供API无所谓,所以跨域资源共享(CORS,Cross-Origin Resource Sharing)不会引起麻烦,因为 JWT 不使用 cookie。

- 为什么不创建新的 Authorization Scheme

像这样

1
Authorization: JWT <token>

- 我的理解

Bearer 定义面向的是更高层级,简单的说,只是定义了后面字符串符合 base64。

Bearer token 可以有很多形式:

  • JWT
  • Plain - 之前的 oauth access_token

- Scheme 的作用

Scheme 的作用就是当拿到一个 Token 时,可以根据 Scheme 知道它是哪种形式的 Token,然后找到对应的解析器。

如果都用 Bearer 的话,那就无从知道了,只能靠双方约定。

这样,Scheme 就不具备更清晰的语义了,使用价值也会小很多。

综上,完全不理解,为什么要用 Bearer,而不是另起 JWT。

就算是 JWT,在 OAuth 上使用也是可以的呀,OAuth 只是一种授权框架,它又不关心 Token 类型。

🔅SWT (Simple Web Token)

🔅SAML 2.0 (Security Assertion Markup Language)


⭐️ 参考

以下是 Twitter 的 OAuth 使用详情:

1
2
3
4
HTTP POST
https://api.twitter.com/1.1/saved_searches/list.json

Authorization OAuth oauth_consumer_key="kxBVHz34SYKxZmdV3tjbA",oauth_token="51027998-aX4ggPDBL1KbcbrfaFpHP1WCc0rwtpDsJHXHEl8UK",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1450240474",oauth_nonce="wg3OPf",oauth_version="1.0",oauth_signature="bAbflN%2F8iyRscuagVqzNjJnDqws%3D"
1
2
3
4
5
6
7
8
9
10
11
12
13
POST /1.1/statuses/update.json?include_entities=true HTTP/1.1
User-Agent: OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Authorization:
OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
oauth_version="1.0"

status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21

Github

1
2
3
4
5
6
7
8
# Basic authentication
curl -u "username" https://api.github.com

# OAuth2 token (sent in a header)
curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com

# OAuth2 token (sent as a parameter)
curl https://api.github.com/?access_token=OAUTH-TOKEN

weibo

1
2
3
4
5
6
7
https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE

{
"access_token": "SlAV32hkKG",
"remind_in": 3600,
"expires_in": 3600
}

接下来所有的东西都走 access_token 了。

wechat

1
2
3
4
5
http://open.wechat.com/cgi-bin/newreadtemplate?t=overseas_open/docs/mobile/login/token#login_token

HTTP request: GET

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID

jira

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
https://developer.atlassian.com/cloud/jira/software/oauth-2-jwt-bearer-token-authorization-grant-type/

POST /oauth2/token HTTP/1.1
Host: auth.atlassian.io
Accept: application/json
Content-Length: {length of the request body}
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=READ+WRITE&assertion={your-signed-jwt}


HTTP/1.1 200 OK
Status: 200 OK
Content-Type: application/json; charset=utf-8
...
{
"access_token": "{your access token}",
"expires_in": {15 minutes expressed as seconds},
"token_type": "Bearer"
}

Using an access token in a request

Set the Authorization header to Bearer {your access token} and make your request:

1
2
3
4
5
GET /rest/api/latest/myself HTTP/1.1

Host: {your-registered-instance}.atlassian.net
Accept: application/json
Authorization: Bearer {your-access-token}

https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5


⭐️ ️️我的QA

> OAuth 2 和 scheme

https://tools.ietf.org/html/rfc6749#section-2.3

可以看到,OAuth 2 在 Client Authentication 上,是不限定哪种 scheme 的,它在 core rfc 上,只是提到了 Basic。

于是它后来又专门出了个 RFC : 6750,来写 bearer

2.3.2. Other Authentication Methods

The authorization server MAY support any suitable HTTP authentication scheme matching its security requirements. When using other authentication methods, the authorization server MUST define a mapping between the client identifier (registration record) and authentication scheme.

> Bearer 可以做到 和 OAuth 没有关系吗?

就是我自己写个验证,也用 Bearer?或者说有多个 Auth 是用 Bearer 的???

我现在的理解就是 虽然 Bearer是在 OAuth 中提出的,但也可以用在其它的验证框架中。
它只是个关键字。如何解析它,需要框架的支持。而 OAuth 就可能支持。

> Bearer token 可以由多少种方式构成。

很多:像 SWT/JWT。或者是自定义,只要满足格式的标准 base64。


⭐️ ️️REF