oAuth 2.0

0x01. OAuth 2.0 Flow概览

1. Register YourApp

2.1 Get an Access Token with the Client-side flow

Authorization request

GET /authorize?response_type=token&client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]&scope=[SCOPE]&state=[STATE] HTTP/1.1
Host: server.example.com 


HTTP/1.1 302 Found
Location: [REDIRECT_URI]#access_token=[ACCESS_TOKEN]&token_type=[TOKEN_TYPE]&expires_in=[EXPIRES_IN]&scope=[SCOPE]&state=[STATE]

Scope可以定义多个scope, scope之间用空格分隔,而不是逗号.

The parameters must be encoded using the application/x-www-form-urlencoded format.

Example:
https://YourApp.com/callback#access_token=ey6XeGlAMHBpFi2LQ9JHyT6xwLbMxYRvyZAR&token_type=bearer&expires_in=3600

注意access_token前是#,非?. 用#,浏览器会理解为该参数并不是sent to / used by server

Value in URL Fragments are not cached.

HTTP/1.1 302 Found
Location: [REDIRECT_URI]#error=[ERROR_CODE]&error_description=[ERROR_DESCRIPTION]&error_uri=[ERROR_URI]&state=[STATE]

Example:
https://YourApp.com/callback#error=access_denied&error_description=The%20user%20has%20denied%20your%20request

2.2 Get an Access Token with the Server-Side Flow

1). Authorization request

GET /authorize?response_type=code&client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]&scope=[SCOPE]&state=[STATE] HTTP/1.1
Host: server.example.com

Example:
https://www.facebook.com/dialog/oauth?response_type=code&client_id=[YourApp_ID]&redirect_uri=http%3A%2F%2FYourApp.com%2Fcallback&scope=public_profile%20user_posts

HTTP/1.1 302 Found
Location: [REDIRECT_URI]?code=[AUTHORIZATION_CODE]&state=[STATE]

Example:
https://YourApp.com/callback?code=ey6XeGlAMHBpFi2LQ9JHyT6xwLbMxYRvyZAR

OAuth 2.0 Spec建议authorization_code的有效时间为10mins.

注意access_token前是?, 不是#.

Error Response

HTTP/1.1 302 Found
Location: [REDIRECT_URI]#error=[ERROR_CODE]&error_description=[ERROR_DESCRIPTION]&error_uri=[ERROR_URI]&state=[STATE]

Example
https://YourApp.com/callback#error=invalid_request&error_description=The%20user%20has%20denied%20your%20request

2). Access Token request (这一步只在YourApp服务器端进行)

方法1 - 利用Authorization header:

POST /token HTTP/1.1
Host: server.example.com 
Authorization: Basic [ENCODED_CLIENT_CREDENTIALS]
Content-Type: application/x-www-form-urlencoded
	
grant_type=authorization_code&code=[AUTHORIZATION_CODE]&redirect_uri=[REDIRECT_URI]&client_id=[CLIENT_ID]

Authorization header: Base64([CLIENT_ID]:[CLIENT_SECRET])

这就是我们不能泄露client_secret的原因。它是YourApp与service Provider之间做一个认证。让Service Provider确认YourApp的真实身份。

方法2 - 利用Form:

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
	
grant_type=authorization_code&code=[AUTHORIZATION_CODE]&redirect_uri=[REDIRECT_URI]&client_id=[CLIENT_ID]&client_secret=[CLIENT_SECRET]

Success Response

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,
	"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}

Error Response

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
		
{
  "error":"invalid_client"
}

3. Use Your Access Token

Use Your Access Token

方法1 - Authorization Header:

GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM

方法2 - Form:

POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
access_token=mF_9.B5f-4.1JqM

方法3 - URI query parameter (Insecure)

GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1
Host: server.example.com

4. Refresh your access token

Refresh token

POST /token HTTP/1.1 
Host: server.example.com 
Authorization: Basic [ENCODED_CLIENT_CREDENTIALS] 
Content-Type: application/x-www-form-urlencoded 
	
grant_type=refresh_token&refresh_token=[REFRESH_TOKEN]

Success Response

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,
	"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}

Error Response

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
		
{
	"error":"invalid_client"
}

Hybrid Flow
Refresh Token

0x02. Get Access Token Flow 比较

1. Authorization Grant

Service provider返回tag,YourApp利用该tag,换取access_token,而且该tag只能换取access_token一次。

Authorization Grant

Access_token并不会返回到browswer.而是储存在YourApp server中。

2. Implicit Grant

Service provider直接返回access_token, 该access_token的有效时间应尽量短

Implicit Grant

3. Authorization Grant vs Implicit Grant

Simplicity Security Access duration
Server-side flow(authorization code grant flow) More complex: In order to facilitate the secure storage and transmission of confidential data, a backend server and data store must be maintained. More secure: The server-side flow never exposes the key to the browser, and so has a significantly smaller chance of being leaked. Long-term: Because an application using the authorization code grant flow is trusted to store confidential information, it can store properties needed for long-term, even offline, access.
Client-side flow(implicit grant flow) Less complex: Due to the more relaxed requirements around security for untrusted applications, no backend server or data store is required. Everything can happen from the browser. Less secure: The key is passed directly to the browser and so has a much larger chance for this key to be obtained by unauthorized parties. Short-term: Since applications using the implicit grant flow are considered untrusted, they should only be given short-lived tokens due to the increased likelihood of such tokens being leaked.

0x03. Attack Vector

攻击的几个大类别:

  • Impersonate Users
  • Impersonate client applications
  • Grant themselves otherwise unauthorized permissions
  • Gain access to protected data and resources

Best Practices:

  • Use TLS (service provider, client application)
  • Request minimal scopes
  • When using the implicit grant flow, request read-only permissions
  • Keep credentials and tokens out of reach of users
  • Use the authorization code grant flow whenever possible
  • Use the refresh token whenever possible
  • Use native browsers instead of embedded browsers
  • Do not use third-party scripts in the redirection endpoint
  • Rotate your client credentials

1. Redirection URI manipulation

当在service provider中注册你的程序时,service provider并不需要你提供redirect_uri,或者允许使用wildcard redirect URI, 如www.yourapp.com/*,

黑客就有可能

  • Set up a fake link to log into YourApp, 但redirect uri为www.evil.com/callback
  • 山寨YourApp,但redirect uri为www.yourapp.com/evil
  1. Redirect URI 定义为黑客控制的server
  2. 当用户被redirect到黑客控制的server时,他就可以获取access token或者authorization code

Actual Attacks - 1

Google/Ms oAuth integration

注册的redirect_url 本应是 https://plus.google.com/c/auth, 不过却错误定义为 https://plus.google.com. 于是 attacker 在可以在 google plus 上发布一个 public post, 如 https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w. Post 中包含另一个attacker own 的网址.

attacker 让用户点击以下地址
https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=000000004404170C&scope=wl.emails,wl.basic,wl.contacts_emails,wl.offline_access&redirect_uri=https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w

最后用户认证之后, 应该会得到https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w?code=e8e0dc1c-2258-6cca-72f3-7dbe0ca97a0b

当用户点击 post 中的网址(https://asanso.github.io), code=e8e0dc1c-2258-6cca-72f3-7dbe0ca97a0b将通过 referer 出现在attacker 的 server log 中.

Attack 2 - Paypal

Into the symmetry: All your Paypal OAuth tokens belong to me - localhost for the win

redirect_url惊人地居然支持 localhost,于是localhost.attacker.com就能通过.

Attack 3

给予一个错误的scope, authorization server redirect到attacker.com

Into the symmetry: Open redirect in rfc6749 aka 'The OAuth 2.0 Authorization Framework'

2. CSRF

The user accesses resources on behalf of the attacker.

经典例子:

  1. stackflow允许用户关联他的social account, 用户可以用它们在stackflow注册的用户登录,也可以用其关联的social account登录。利用csrf, 黑客将他的social account关联到用户的stackflow帐户下,之后黑客就可以用它们的social account登录到用户的stackflow
  2. The user may upload private items to an attacker''s resources.

Flow:

  1. 黑客往redirect URI发送他从service provider中获得的auth_code或者access_token
  2. YourApp以后就允许该token访问受限页面

3. Phishing

4. Client and user impersonation

  1. Client (APP) impersonation
    假如YourApp注册时,service provider提供的YourApp 的client ID和client secret遭泄露,黑客就可以扮成YourApp,诱导用户访问,并获取service provider中信息。

  2. User impersonation
    Access_Token遭泄露

0x04. cURL

1. The authorization grant request

1). Initiate

GET /authorize?response_type=code&client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]&scope=[SCOPE]&state=[STATE] HTTP/1.1
Host: server.example.com
curl --request POST
    –u [CLIENT_ID]:[CLIENT_SECRET]
    --data-urlencode "grant_type=authorization_code"
    --data-urlencode "code=[AUTHORIZATION_CODE]"
    --data-urlencode "redirect_uri=[REDIRECT_URI]"
    --data-urlencode "client_id=[CLIENT_ID]"
    [AUTH_ENDPOINT]

2). Exchange an access token

POST /token HTTP/1.1
Host: server.example.com 
Authorization: Basic [ENCODED_CLIENT_CREDENTIALS]
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code& code=[AUTHORIZATION_CODE]&redirect_uri=[REDIRECT_URI]&client_id=[CLIENT_ID]
curl --request POST
    –u [CLIENT_ID]:[CLIENT_SECRET]
    --data-urlencode "grant_type=authorization_code"
    --data-urlencode "code=[AUTHORIZATION_CODE]"
    --data-urlencode "redirect_uri=[REDIRECT_URI]"
    --data-urlencode "client_id=[CLIENT_ID]"
    [AUTH_ENDPOINT]

3). Access a protected resource

方法1:

curl -H "Authorization: Bearer [ACCESS_TOKEN]" http://www.example.com

方法2:

curl --request POST
    --data-urlencode "access_token=[ACCESS_TOKEN]"
    --data-urlencode "method=get" 
    http://www.example.com

方法3:

curl "https://server.example.com?access_token=[ACCESS_TOKEN]"

2. The refresh token flow

curl --request POST
    –u [CLIENT_ID]:[CLIENT_SECRET]
    --data-urlencode "grant_type=refresh_token"
    --data-urlencode "refresh_token=[REFRESH_TOKEN]"
    [TOKEN_ENDPOINT]

0x05. 参考

  1. Mastering OAuth 2.0
  2. Choosing an SSO Strategy: SAML vs OAuth2
  3. BH - OAuth User Profile Attack
  4. Attacking the OAuth Protocol - Dhaval Kapil
Show Comments