【云安全】使用Amazon API网关和AWS WAF保护您的API -第2部分
本文由Heitor Lessa提供,AWS专业解决方案架构师- Serverless
在本博客的第1部分中,我们描述了如何使用AWS WAF保护Amazon API Gateway提供的API。在本博客中,我们将展示如何在Amazon CloudFront分发和API网关之间使用API键,以确保除了已经在API网关中设置的首选授权(AuthZ)机制之外,还可以访问API网关中的API。有关API网关中的AuthZ机制的更多信息,请参见使用Amazon Cognito联合身份、Amazon Cognito用户池和Amazon API网关的安全API访问。
我们还扩展了以前用于自动创建此解决方案的以下必要资源的AWS CloudFormation堆栈:
- API网关使用计划——管理专用于CloudFront的API密钥,以及必要时的节流和计量使用
- AWS Lambda函数——更新AWS CloudFormation堆栈参数时间戳并触发API键旋转
- Amazon CloudWatch事件调度作业——在给定的调度中触发Lambda函数
以下是使用API密钥的替代解决方案,具体取决于您的安全需求:
在CloudFront中使用随机生成的HTTP秘密头,并通过API网关请求验证进行验证
使用Lambda@Edge对传入请求进行签名,并使用API网关Lambda授权器进行验证
需求
要继续,您需要完全的权限来通过AWS CloudFormation创建、更新和删除API网关、CloudFront、Lambda和CloudWatch事件。
扩展现有AWSCloudFormation 堆栈
首先,点击这里下载完整的模板。然后按照以下步骤更新现有的AWS云形成堆栈:
- 转到AWS管理控制台并打开AWS CloudFormation控制台。
- 选择您在第1部分中创建的堆栈,右键单击它,然后选择Update stack。
- 对于选项2,选择“选择文件”并选择下载的模板。
- 填写所需的参数,如下图所示。
以下是关于这些参数的更多信息:
- 发送流量的API网关——除了没有URL模式(https://)之外,我们使用与第1部分相同的API网关URL: cxm45444t9a.execute-api.us- east2.amazonaws.com/prod
- 旋转API键——我们定义了Daily并使用2018-04-03作为时间戳值来追加API键名
继续使用AWS CloudFormation控制台完成操作。更新堆栈可能需要几分钟,因为CloudFront需要时间在所有存在点上传播更改。
在示例宠物商店API中启用API键
当堆栈在后台完成时,让我们在CloudFront将发送流量到的API中启用API键。
- 转到AWS管理控制台并打开API网关控制台。
- 选择您在第1部分中创建的API并选择Resources。
- 在/pets下,选择GET,然后选择Method Request。
- 对于所需的API键,选择下拉菜单并选择true。
- 要保存此更改,请选择突出显示的复选标记,如下图所示。
接下来,我们需要部署这些更改,以便在没有API密钥时发送给/pets的请求失败。
- 选择Actions并选择Deploy API。
- 选择Deployment stage下拉菜单,并选择在第1部分中创建的舞台。
- 添加一个部署描述,例如“在/pets下需要API键”,然后选择Deploy。
当部署成功时,您将被重定向到API Gateway Stage页面。在那里,您可以使用Invoke URL来测试以下请求是否由于没有API密钥而失败。
这一失败是预料之中的,并证明我们部署的更改正在工作。接下来,让我们尝试访问相同的API,但这次是通过CloudFront分发。
- 从AWS管理控制台打开AWS Cloudformation控制台。
- 选择您在第1部分中创建的堆栈,并在左下角选择output。
- 在CFDistribution行上,复制URL。在粘贴新浏览器选项卡或窗口之前,请在其中添加' /pets '。
与我们第一次尝试不使用API键相反,我们从PetStore API接收JSON响应。这是因为CloudFront在将请求转发到PetStore API之前注入了一个API密钥。下图演示了这两种测试:
- 通过CloudFront访问API时请求成功
- 通过API的调用URL直接访问API时,请求失败
这是CloudFront和API网关之间的一个秘密,它可以是任何约定的随机秘密,可以像API密钥一样旋转。但是,重要的是要知道API key是一个跟踪或度量API使用者使用情况的特性。它不是一种安全的授权机制,因此只能与API网关授权器一起使用。
旋转的API密钥
API键会根据更新AWS CloudFormation堆栈时选择的时间表(例如,每日或每月)自动旋转。这就不需要您进行维护或干预。在本节中,我们将解释这个过程是如何工作的,以及如果您想手动触发API键旋转,您可以做些什么。
除了第1部分之外,我们下载并用于更新堆栈的AWS CloudFormation模板还执行了以下操作。
引入一个附加到API密钥名的时间戳参数
Parameters:
Timestamp:
Type: String
Description: Fill in this format <Year>-<Month>-<Day>
Default: 2018-04-02
创建一个API网关密钥,API网关使用计划,将新密钥与作为参数给出的API网关关联,并配置CloudFront分发,以便在将流量转发到API网关时发送一个自定义头
CFDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Logging:
IncludeCookies: 'false'
Bucket: !Sub ${S3BucketAccessLogs}.s3.amazonaws.com
Prefix: cloudfront-logs
Enabled: 'true'
Comment: API Gateway Regional Endpoint Blog post
Origins:
-
Id: APIGWRegional
DomainName: !Select [0, !Split ['/', !Ref ApiURL]]
CustomOriginConfig:
HTTPPort: 443
OriginProtocolPolicy: https-only
OriginCustomHeaders:
-
HeaderName: x-api-key
HeaderValue: !Ref ApiKey
...ApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
Description: CloudFront usage only
UsagePlanName: CloudFront_only
ApiStages:
-
ApiId: !Select [0, !Split ['.', !Ref ApiURL]]
Stage: !Select [1, !Split ['/', !Ref ApiURL]]ApiKey:
Type: "AWS::ApiGateway::ApiKey"
Properties:
Name: !Sub "CloudFront-${Timestamp}"
Description: !Sub "CloudFormation API Key ${Timestamp}"
Enabled: trueApiKeyUsagePlan:
Type: "AWS::ApiGateway::UsagePlanKey"
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref ApiUsagePlan
正如在ApiKey资源中所示,我们将给定的时间戳附加到Name中,并在API网关使用计划密钥资源中使用它。这意味着,无论何时时间戳参数发生变化,AWS CloudFormation都会触发资源替换,并更新依赖于该API键的每个资源。在本例中,这包括AWS CloudFront配置和API网关使用计划。
但是在这个例子中,您在本博客开头所选择的轮换计划意味着什么呢?
创建一个计划好的活动来触发给定计划中的Lambda函数
Parameters:
...
ApiKeyRotationSchedule:
Description: Schedule to rotate API Keys e.g. Daily, Monthly, Bimonthly basis
Type: String
Default: Daily
AllowedValues:
- Daily
- Fortnightly
- Monthly
- Bimonthly
- Quarterly
ConstraintDescription: Must be any of the available optionsMappings:
ScheduleMap:
CloudwatchEvents:
Daily: "rate(1 day)"
Fortnightly: "rate(14 days)"
Monthly: "rate(30 days)"
Bimonthly: "rate(60 days)"
Quarterly: "rate(90 days)"Resources:
...
RotateApiKeysScheduledJob:
Type: "AWS::Events::Rule"
Properties:
Description: "ScheduledRule"
ScheduleExpression: !FindInMap [ScheduleMap, CloudwatchEvents, !Ref ApiKeyRotationSchedule]
State: "ENABLED"
Targets:
-
Arn: !GetAtt RotateApiKeysFunction.Arn
Id: "RotateApiKeys"
资源RotateApiKeysScheduledJob显示,在更新AWS CloudFormation堆栈时,通过下拉菜单选择的调度实际上转换为CloudWatch事件规则。这进而触发在相同模板中定义的Lambda函数。
RotateApiKeysFunction:
Type: "AWS::Lambda::Function"
Properties:
Handler: "index.lambda_handler"
Role: !GetAtt RotateApiKeysFunctionRole.Arn
Runtime: python3.6
Environment:
Variables:
StackName: !Ref "AWS::StackName"
Code:
ZipFile: !Sub |
import datetime
import osimport boto3
from botocore.exceptions import ClientErrorsession = boto3.Session()
cfn = session.client('cloudformation')
timestamp = datetime.date.today()
params = {
'StackName': os.getenv('StackName'),
'UsePreviousTemplate': True,
'Capabilities': ["CAPABILITY_IAM"],
'Parameters': [
{
'ParameterKey': 'ApiURL',
'UsePreviousValue': True
},
{
'ParameterKey': 'ApiKeyRotationSchedule',
'UsePreviousValue': True
},
{
'ParameterKey': 'Timestamp',
'ParameterValue': str(timestamp)
},
],
}def lambda_handler(event, context):
""" Updates CloudFormation Stack with a new timestamp and returns CloudFormation response"""
try:
response = cfn.update_stack(**params)
except ClientError as err:
if "No updates are to be performed" in err.response['Error']['Message']:
return {"message": err.response['Error']['Message']}
else:
raise Exception("An error happened while updating the stack: {}".format(err))
return response
这个Lambda函数所做的一切就是通过API触发AWS CloudFormation堆栈更新(与您通过控制台所做的完全相同,但是以编程方式进行的),并更新时间戳参数。因此,它会旋转API键和CloudFront分发配置。
这为您提供了足够的灵活性,可以在任何时候更改API key rotation schedule,而无需维护或编写任何代码。您还可以手动更新堆栈,并通过更新AWS CloudFormation堆栈的时间戳参数来旋转键。
下一个步骤
我们希望你觉得这个博客的信息有帮助。您可以使用它来了解如何创建一种机制,只允许从CloudFront到API网关的通信,并避免绕过第1部分设置的AWS WAF规则。
关于这个解决方案,请记住以下几点:
- 它假设您已经拥有一个由API网关管理的强大AuthZ机制来控制对API的访问。
- 在此解决方案中创建的API网关使用计划和其他资源只适用于在相同帐户中创建的API (ApiUrl参数)。
- 如果您已经使用API键来跟踪API的使用情况,请考虑使用以下任何一种解决方案作为替代:
- 在CloudFront源配置中使用随机HTTP头值,并使用API网关请求模型验证来验证它,而不是单独使用API键。
- 结合Lambda@Edge和API网关自定义授权器,使用只有二者知道的共享秘密对传入的请求进行签名和验证。这是一种更先进的技术。
本文:http://pub.intelligentx.net/protecting-your-api-using-amazon-api-gateway-and-aws-waf-part-2
讨论:请加入知识星球或者小红圈【首席架构师圈】
- 57 次浏览