在現代 Web 應用程式中,API 安全性至關重要。本文介紹如何利用 Amazon Cognito 和 API Gateway 建構安全的 API 授權機制。首先,我們會建立一個 Cognito User Pool 並設定 API Gateway 以使用該 User Pool 進行驗證,確保只有經過驗證的使用者才能存取 API。接著,我們將示範如何使用 AWS CLI 建立使用者、進行註冊和身份驗證,並取得必要的 Token。最後,我們會使用 CloudFormation 範本自動化佈署 API Gateway 和 Cognito 的整合,並示範如何使用取得的 Token 呼叫 API。文章也包含如何設定 SMS 和 MFA 驗證以強化安全性,以及建立必要的 IAM 角色和政策,允許 Cognito 傳送 SMS 訊息。
使用 Amazon Cognito 進行應用程式安全:第 4 章
設定 API Gateway 與 Cognito 整合
為了確保 API Gateway 的安全性,我們將使用 Amazon Cognito User Pools 作為授權者。首先,我們需要建立一個 Cognito User Pool 並設定 API Gateway 以使用該 User Pool 進行驗證。
建立 Cognito User Pool Client
使用以下命令建立一個 User Pool Client:
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_fYsb1Gyec \
--client-name my-user-pool-client \
--explicit-auth-flows USER_PASSWORD_AUTH \
--profile admin
建立使用者並進行註冊
使用以下命令建立一個新使用者並進行註冊:
aws cognito-idp sign-up \
--client-id 45l9ureterrdqt0drbphk4q3pd \
--username testuser5 \
--password Passw0rd$ \
--user-attributes Name=given_name,Value=Heartin
然後,使用以下命令確認使用者註冊:
aws cognito-idp admin-confirm-sign-up \
--user-pool-id us-east-1_fYsb1Gyec \
--username testuser5 \
--profile admin
進行身份驗證並取得 Token
使用以下命令進行身份驗證並取得 Token:
aws cognito-idp initiate-auth \
--client-id 45l9ureterrdqt0drbphk4q3pd \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters USERNAME=testuser5,PASSWORD=Passw0rd$
成功後,該命令將傳回 Access Token、ID Token 和 Refresh Token。
設定 API Gateway
建立 API Gateway REST API
首先,建立一個 REST API:
Resources:
MyFirstRestAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Name: Greeting API
Description: API for greeting an user
FailOnWarnings: true
建立 Cognito Authorizer
建立一個 Cognito Authorizer:
CustomCognitoAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: FirstCognitoAuthorizer
RestApiId: !Ref MyFirstRestAPI
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::Sub:
- arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId}
- UserPoolId: !ImportValue MyFirstUserPoolId
IdentitySource: method.request.header.Authorization
設定 Method 與 Integration
設定 Method 與 Integration:
MyMockMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref CustomCognitoAuthorizer
HttpMethod: GET
Integration:
Type: MOCK
IntegrationHttpMethod: GET
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: "{\"message\": \"Welcome $context.authorizer.claims.given_name\" }"
RequestTemplates:
application/json: "{\"statusCode\": 200}"
ResourceId: !Ref GreetingResource
RestApiId: !Ref MyFirstRestAPI
MethodResponses:
- StatusCode: 200
使用 Token 呼叫 API
最後,使用取得的 ID Token 呼叫 API。在 REST Client(如 Postman)中,將 Authorization Type 設定為 Bearer Token,並將 ID Token 值複製到 Token 欄位中。
CloudFormation 範本
以下是完整的 CloudFormation 範本:
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Building Cognito API with AWS CloudFormation
Resources:
MyFirstRestAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Name: Greeting API
Description: API for greeting an user
FailOnWarnings: true
CustomCognitoAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: FirstCognitoAuthorizer
RestApiId: !Ref MyFirstRestAPI
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::Sub:
- arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId}
- UserPoolId: !ImportValue MyFirstUserPoolId
IdentitySource: method.request.header.Authorization
GreetingResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref MyFirstRestAPI
ParentId: !GetAtt MyFirstRestAPI.RootResourceId
PathPart: 'greeting'
MyMockMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref CustomCognitoAuthorizer
HttpMethod: GET
Integration:
Type: MOCK
IntegrationHttpMethod: GET
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: "{\"message\": \"Welcome $context.authorizer.claims.given_name\" }"
RequestTemplates:
application/json: "{\"statusCode\": 200}"
ResourceId: !Ref GreetingResource
RestApiId: !Ref MyFirstRestAPI
MethodResponses:
- StatusCode: 200
MyFirstDeployment:
DependsOn: MyMockMethod
Type: AWS::ApiGateway::Deployment
Properties:
Description: 'First Deployment'
RestApiId: !Ref MyFirstRestAPI
StageDescription:
Description: 'Dev Stage'
StageName: 'dev'
Output:
SampleEndpoint:
Description: 'Sample Endpoint'
Value: !Sub
- 'https://${RestApiId}.execute-api.${AWS::Region}.amazonaws.com/dev/greeting'
- RestApiId: !Ref MyFirstRestAPI
程式碼解析:
此 CloudFormation 範本定義了一個 REST API,並使用 Cognito User Pool 進行授權。範本中包含以下資源:
MyFirstRestAPI:定義了一個 REST API。CustomCognitoAuthorizer:定義了一個 Cognito Authorizer,使用指定的 User Pool 進行驗證。GreetingResource:定義了一個資源,用於處理/greeting路徑的請求。MyMockMethod:定義了一個 Method,使用 MOCK Integration 傳回一個歡迎訊息。該 Method 需要使用有效的 ID Token 進行授權。MyFirstDeployment:定義了一個 Deployment,將 API 部屬到devStage。
此範本演示瞭如何使用 CloudFormation 設定 API Gateway 與 Cognito 的整合,以實作安全的 API 存取控制。
使用 Amazon Cognito 進行應用程式安全 Chapter 4
將 Amazon Cognito 與 API Gateway 整合
在前面的章節中,我們建立了一個支援使用者註冊和驗證的 Amazon Cognito 使用者池。在本章節中,我們將會示範如何將 Amazon Cognito 與 API Gateway 整合,以確保只有經過驗證的使用者才能夠存取我們的 API。
建立 API Gateway 和 Cognito 使用者池整合
首先,我們需要建立一個 API Gateway,並設定一個 COGNITO_USER_POOLS 型別的授權者。接著,我們需要將這個授權者與我們的 API Gateway 整合。
aws cognito-idp sign-up
aws cognito-idp admin-confirm-sign-up
aws cognito-idp initiate-auth
在完成上述步驟後,我們可以使用 REST 客戶端(例如 Postman)來測試我們的 API。我們需要在 Postman 中選擇 Bearer Token 作為授權型別,並將 initiate-auth 請求中收到的 ID token 值複製到 Token 欄位中。
宣告式身份驗證
宣告式身份驗證是一種驗證方法,其中存取權杖包含驗證所需的存取金鑰資訊,以及其他關於使用者的屬性(宣告)。這種權杖被稱為身份權杖。宣告式身份驗證允許使用者使用單一權杖登入多個網站,這被稱為單一登入。
它是如何工作的
在本章節中,我們建立了一個 API Gateway API 和一個 COGNITO_USER_POOLS 型別的授權者,並將它們整合在一起。我們還示範瞭如何使用 context.authorizer.claims 從 ID token 中擷取額外的使用者資訊。
更多資訊
您可以參考以下資源以瞭解更多資訊:
- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
- https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
使用 SMS 驗證和 MFA 的使用者註冊
在前面的章節中,我們示範瞭如何使用文字使用者名稱和電子郵件驗證進行使用者註冊。在本章節中,我們將建立一個支援 SMS 和 MFA 驗證的使用者池,並示範如何使用 SMS 和 MFA 驗證進行使用者註冊。
建立支援 SMS 驗證的使用者池
首先,我們需要建立一個支援使用電話號碼作為使用者名稱的使用者池,並啟用 SMS 和 MFA 驗證。
建立允許 Cognito 傳送 SMS 訊息的角色
- 建立一個具有
sns:publish動作的政策 JSON 檔案:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sns:publish"
],
"Resource": [
"*"
]
}
]
}
將此檔案儲存為 sns-publish-policy.txt。
內容解密:
上述 JSON 檔案定義了一個 IAM 政策,允許 Cognito 傳送 SMS 訊息給使用者。其中,sns:publish 動作允許 Cognito 傳送訊息到 SNS 主題,而 * 表示允許存取所有 SNS 主題。
- 建立政策:
aws iam create-policy \
--policy-name cognito_sns_iam_policy \
--policy-document file://resources/sns-publish-policy.txt \
--profile admin
內容解密:
上述命令建立了一個名為 cognito_sns_iam_policy 的 IAM 政策,並將其與 sns-publish-policy.txt 檔案中定義的政策檔案相關聯。
- 建立信任關係檔案:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cognito-idp.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
將此檔案儲存為 assume-role-trust-relationship-policy-document.txt。
內容解密:
上述 JSON 檔案定義了一個信任關係檔案,允許 Cognito 服務承擔角色。其中,cognito-idp.amazonaws.com 是 Cognito 服務的 Principal,而 sts:AssumeRole 動作允許 Cognito 承擔角色。
- 建立角色:
aws iam create-role \
--role-name cognito_sns_iam_role \
--assume-role-policy-document file://resources/assume-role-trust-relationship-policy-document.txt \
--profile admin
內容解密:
上述命令建立了一個名為 cognito_sns_iam_role 的 IAM 角色,並將其與 assume-role-trust-relationship-policy-document.txt 檔案中定義的信任關係檔案相關聯。
- 將政策附加到角色:
aws iam attach-role-policy \
--role-name cognito_sns_iam_role \
--policy-arn arn:aws:iam::<account_id>:policy/cognito_sns_iam_policy \
--profile admin
內容解密:
上述命令將 cognito_sns_iam_policy 政策附加到 cognito_sns_iam_role 角色上。
此圖示說明瞭建立支援 SMS 驗證的使用者池的流程:
@startuml
skinparam backgroundColor #FEFEFE
skinparam sequenceArrowThickness 2
title API Gateway 整合 Cognito 安全驗證
actor "客戶端" as client
participant "API Gateway" as gateway
participant "認證服務" as auth
participant "業務服務" as service
database "資料庫" as db
queue "訊息佇列" as mq
client -> gateway : HTTP 請求
gateway -> auth : 驗證 Token
auth --> gateway : 認證結果
alt 認證成功
gateway -> service : 轉發請求
service -> db : 查詢/更新資料
db --> service : 回傳結果
service -> mq : 發送事件
service --> gateway : 回應資料
gateway --> client : HTTP 200 OK
else 認證失敗
gateway --> client : HTTP 401 Unauthorized
end
@enduml此圖示呈現了建立支援 SMS 驗證的使用者池的步驟,包括建立政策、建立信任關係檔案、建立角色、將政策附加到角色和建立使用者池。