本頁面說明如何使用已簽署的 IAP 標頭保護應用程式。設定完成後,Identity-Aware Proxy (IAP) 會使用 JSON Web Token (JWT) 確認對應用程式的要求已獲得授權。這樣可保護應用程式不受下列風險的影響:
- IAP 意外停用;
- 防火牆設定有誤;
- 專案內的存取。
欲正確保護您的應用程式,所有應用程式類型都必須使用簽署標頭。
或者,若您有 App Engine 標準環境應用程式,則可使用 Users API。
請注意,Compute Engine 和 GKE 健康狀態檢查不包含 JWT 標頭,且 IAP 不會處理健康狀態檢查。如果健康狀態檢查傳回存取錯誤,請確認是否在 Google Cloud 主控台中正確設定健康狀態檢查,以及 JWT 標頭驗證是否允許健康狀態檢查路徑。詳情請參閱「建立健康狀態檢查例外狀況」一文。
事前準備
如要使用簽署標頭保護應用程式,請先準備好下列項目:
- 您想讓使用者連線的應用程式。
- 適用於您語言的第三方 JWT 程式庫,且該 JWT 程式庫支援
ES256
演算法。
使用 IAP 標頭確保應用程式安全
如要使用 IAP JWT 保護應用程式,請驗證 JWT 的標頭、酬載和簽名。JWT 位於 HTTP 要求標頭 x-goog-iap-jwt-assertion
中。如果攻擊者略過 IAP,他們可能會假造 IAP 未簽署的身分識別標頭 x-goog-authenticated-user-{email,id}
。IAP JWT 可提供更安全的替代方案。
如果有人略過 IAP,已簽署標頭會提供第二重安全防護。請注意,啟用 IAP 後,當要求經過 IAP 服務基礎架構時,IAP 會移除用戶端提供的 x-goog-*
標頭。
驗證 JWT 標頭
驗證 JWT 的標頭是否符合下列限制:
JWT 標頭憑證附加資訊 | ||
---|---|---|
alg |
演算法 | ES256 |
kid |
金鑰 ID |
必須對應至 IAP 金鑰檔案中列出的其中一個公開金鑰,金鑰可採用兩種不同的格式:https://www.gstatic.com/iap/verify/public_key 與 https://www.gstatic.com/iap/verify/public_key-jwk |
確認 JWT 已由憑證 kid
憑證附加資訊對應的私密金鑰簽署,如要執行此操作,請先從以下其中一個位置抓取公開金鑰:
https://www.gstatic.com/iap/verify/public_key
。這個網址包含 JSON 字典,藉以將kid
憑證附加資訊對應至公開金鑰值。https://www.gstatic.com/iap/verify/public_key-jwk
。這個網址包含 JWK 格式的 IAP 公開金鑰。
擁有公開金鑰之後,請使用 JWT 程式庫驗證簽名。
驗證 JWT 酬載
驗證 JWT 的酬載是否符合下列限制:
JWT 酬載憑證附加資訊 | ||
---|---|---|
exp |
到期時間 | 必須是未來的日期。時間以秒為單位,從世界標準時間指定期間開始計算。允許 30 秒的偏移。 憑證的最大生命週期為 10 分鐘 + 2 * 偏移。 |
iat |
核發時間 | 必須是過去的時間。時間以秒為單位,從世界標準時間指定期間開始計算。允許 30 秒的偏移。 |
aud |
目標對象 |
必須是包含下列值的字串:
|
iss |
核發單位 |
必須為 https://cloud.google.com/iap 。 |
hd |
帳戶網域 |
如果帳戶屬於代管網域,系統會提供 hd 憑證附加資訊,藉以區分與帳戶關聯的網域。 |
google |
Google 憑證附加資訊 |
如果將一或多個存取層級套用至要求,其名稱會做為字串陣列,儲存在 google 憑證附加資訊 JSON 物件的 access_levels 金鑰下。當您指定裝置政策,且機構有權存取裝置資料時, |
您可以存取Google Cloud 主控台,或使用 gcloud 指令列工具,取得上述 aud
字串的值。
如要從 Google Cloud 主控台取得 aud
字串值,請前往專案的 Identity-Aware Proxy 設定,按一下負載平衡器資源旁邊的「More」,然後選取「Signed Header JWT Audience」。這時出現的「Signed Header JWT」(已簽署標頭的 JWT) 對話方塊會顯示已選取資源的 aud
憑證附加資訊。
如果您要使用 gcloud CLI gcloud 指令列工具取得 aud
字串值,則需要知道專案 ID。您可以在 Google Cloud 主控台的「Project info」資訊卡中找到專案 ID,然後針對每個值執行下列指定指令。
專案編號
如要使用 gcloud 指令列工具取得專案編號,請執行下列指令:
gcloud projects describe PROJECT_ID
指令會傳回類似以下的輸出內容:
createTime: '2016-10-13T16:44:28.170Z' lifecycleState: ACTIVE name: project_name parent: id: '433637338589' type: organization projectId: PROJECT_ID projectNumber: 'PROJECT_NUMBER'
服務 ID
如要使用 gcloud 指令列工具取得服務 ID,請執行下列指令:
gcloud compute backend-services describe SERVICE_NAME --project=PROJECT_ID --global
指令會傳回類似以下的輸出內容:
affinityCookieTtlSec: 0 backends: - balancingMode: UTILIZATION capacityScaler: 1.0 group: https://www.googleapis.com/compute/v1/projects/project_name/regions/us-central1/instanceGroups/my-group connectionDraining: drainingTimeoutSec: 0 creationTimestamp: '2017-04-03T14:01:35.687-07:00' description: '' enableCDN: false fingerprint: zaOnO4k56Cw= healthChecks: - https://www.googleapis.com/compute/v1/projects/project_name/global/httpsHealthChecks/my-hc id: 'SERVICE_ID' kind: compute#backendService loadBalancingScheme: EXTERNAL name: my-service port: 8443 portName: https protocol: HTTPS selfLink: https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/my-service sessionAffinity: NONE timeoutSec: 3610
擷取使用者身分識別資訊
如果上述所有驗證都成功,就可以擷取使用者身分識別資訊。ID 權杖的酬載包含下列使用者資訊:
ID 憑證酬載使用者身分識別資訊 | ||
---|---|---|
sub |
主旨 |
使用者的專屬固定 ID。請使用此值,而非 x-goog-authenticated-user-id 標頭。 |
email |
使用者電子郵件地址 | 使用者電子郵件地址。
|
以下程式碼範例可使用已簽署的 IAP 標頭保護應用程式:
C#
Go
Java
Node.js
PHP
Python
Ruby
測試驗證程式碼
如果您使用 secure_token_test
查詢參數前往應用程式,IAP 會納入無效的 JWT。請利用這個方法來確認您的 JWT 驗證邏輯能處理各種失敗案例,並檢視您應用程式收到無效 JWT 時的因應方式。
建立健康狀態檢查例外狀況
如上所述,Compute Engine 和 GKE 健康狀態檢查不會使用 JWT 標頭,且 IAP 不會處理健康狀態檢查。您需要設定健康狀態檢查與應用程式,藉以允許健康狀態檢查存取。
設定健康狀態檢查
如果您尚未設定健康狀態檢查的路徑,請使用Google Cloud 主控台為健康狀態檢查設定非機密路徑。請確保此路徑未由其他任何資源共用。
- 前往 Google Cloud 控制台的「Health checks」頁面。
前往「Health checks」(健康狀態檢查) 頁面 - 按一下您要針對應用程式使用的健康狀態檢查,然後按一下 [Edit] (編輯)。
- 在「Request path」(要求路徑) 下,新增非機密路徑名稱。此會指定 Google Cloud 傳送健康狀態檢查要求時使用的網址路徑。如果省略,健康狀態檢查要求會傳送至
/
。 - 按一下 [儲存]。
設定 JWT 驗證
在呼叫 JWT 驗證常式的程式碼中,新增條件來為健康狀態檢查路徑提供 200 HTTP 狀態。例如:
if HttpRequest.path_info = '/HEALTH_CHECK_REQUEST_PATH' return HttpResponse(status=200) else VALIDATION_FUNCTION
外部身分的 JWT
如果您使用 IAP 搭配外部身分,IAP 仍會在每個經過驗證的請求上發出已簽署的 JWT,就像使用 Google 身分一樣。但兩者之間還是有些差異。
供應者資訊
使用外部身分時,JWT 酬載會包含名為 gcip
的憑證附加資訊。這個權利要求包含使用者相關資訊,例如電子郵件和相片網址,以及任何其他提供者專屬屬性。
以下是使用者透過 Facebook 登入的 JWT 範例:
"gcip": '{
"auth_time": 1553219869,
"email": "facebook_user@gmail.com",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"facebook_user@gmail.com"
],
"facebook.com": [
"1234567890"
]
},
"sign_in_provider": "facebook.com",
},
"name": "Facebook User",
"picture: "https://graph.facebook.com/1234567890/picture",
"sub": "gZG0yELPypZElTmAT9I55prjHg63"
}',
email
和 sub
欄位
如果使用者已通過 Identity Platform 的驗證,JWT 的 email
和 sub
欄位會在前面加上 Identity Platform 權杖發出者和使用的租用戶 ID (如有)。例如:
"email": "securetoken.google.com/PROJECT-ID/TENANT-ID:demo_user@gmail.com", "sub": "securetoken.google.com/PROJECT-ID/TENANT-ID:gZG0yELPypZElTmAT9I55prjHg63"
使用 sign_in_attributes
控管存取權
雖然 IAM 不支援與外部身分搭配使用,但您可以改用 sign_in_attributes
欄位內嵌的宣告來控制存取權。舉例來說,假設使用者使用 SAML 供應器登入:
{
"aud": "/projects/project_number/apps/my_project_id",
"gcip": '{
"auth_time": 1553219869,
"email": "demo_user@gmail.com",
"email_verified": true,
"firebase": {
"identities": {
"email": [
"demo_user@gmail.com"
],
"saml.myProvider": [
"demo_user@gmail.com"
]
},
"sign_in_attributes": {
"firstname": "John",
"group": "test group",
"role": "admin",
"lastname": "Doe"
},
"sign_in_provider": "saml.myProvider",
"tenant": "my_tenant_id"
},
"sub": "gZG0yELPypZElTmAT9I55prjHg63"
}',
"email": "securetoken.google.com/my_project_id/my_tenant_id:demo_user@gmail.com",
"exp": 1553220470,
"iat": 1553219870,
"iss": "https://cloud.google.com/iap",
"sub": "securetoken.google.com/my_project_id/my_tenant_id:gZG0yELPypZElTmAT9I55prjHg63"
}
您可以在應用程式中加入類似下方程式的邏輯,限制存取權限,只允許具有有效角色的使用者存取:
const gcipClaims = JSON.parse(decodedIapJwtClaims.gcip);
if (gcipClaims &&
gcipClaims.firebase &&
gcipClaims.firebase.sign_in_attributes &&
gcipClaims.firebase.sign_in_attribute.role === 'admin') {
// Allow access to admin restricted resource.
} else {
// Block access.
}
您可以使用 gcipClaims.gcip.firebase.sign_in_attributes
巢狀宣稱,存取 Identity Platform SAML 和 OIDC 提供者的其他使用者屬性。
IdP 要求大小限制
使用者透過 Identity Platform 登入後,其他使用者屬性會傳播至無狀態 Identity Platform ID 權杖酬載,並安全地傳送至 IAP。接著,IAP 會發出自己的無狀態不透明 Cookie,其中也包含相同的宣告。IAP 會根據 Cookie 內容產生已簽署的 JWT 標頭。
因此,如果工作階段以大量權利要求啟動,可能會超過允許的最大 Cookie 大小,在大多數瀏覽器中,這個大小通常為 4 KB。這會導致登入作業失敗。
請務必確保 IdP SAML 或 OIDC 屬性中只會傳播必要的權利要求。另一個做法是使用封鎖函式,篩除授權檢查不需要的宣告。
const gcipCloudFunctions = require('gcip-cloud-functions');
const authFunctions = new gcipCloudFunctions.Auth().functions();
// This function runs before any sign-in operation.
exports.beforeSignIn = authFunctions.beforeSignInHandler((user, context) => {
if (context.credential &&
context.credential.providerId === 'saml.my-provider') {
// Get the original claims.
const claims = context.credential.claims;
// Define this function to filter out the unnecessary claims.
claims.groups = keepNeededClaims(claims.groups);
// Return only the needed claims. The claims will be propagated to the token
// payload.
return {
sessionClaims: claims,
};
}
});