使用Amazon Verified Permissions快速保护Express应用API

本文介绍如何使用Amazon Verified Permissions和新的Express中间件包,通过Cedar策略语言快速实现细粒度API授权,将授权逻辑从应用代码中解耦,提升安全性和开发效率。

使用Amazon Verified Permissions在几分钟内保护Express应用API

概述

Amazon Verified Permissions发布了@verifiedpermissions/authorization-clients-js开源包,开发者可使用该包在为Express.js Web应用API实现外部细粒度授权,整个过程仅需几分钟。Express是一个极简且灵活的Node.js Web应用框架,为Web和移动应用提供强大功能集。通过使用与Verified Permissions的标准化集成,开发者可以用比编写自定义集成少90%的代码量将授权外部化,从而节省时间精力,并通过减少自定义集成代码量来提升应用安全状况。

为什么需要外部化授权?

传统上,开发者通过在应用代码中直接嵌入授权逻辑来实现授权。这种嵌入式授权逻辑设计用于支持少量权限,但随着应用演进,通常需要逐步更新嵌入式授权逻辑以支持更复杂用例,导致代码复杂且难以维护。随着代码复杂性增加,进一步演进安全模型和执行权限审计变得更加困难,导致应用在其生命周期中越来越难以维护。

通过外部化授权,您可以将授权逻辑与应用解耦。这带来多重好处,包括让开发团队专注于应用逻辑,并简化软件审计。

使用Cedar外部化授权

一种从应用代码外部化授权的方法是使用Cedar。Cedar是一种开源语言和软件开发工具包(SDK),用于为应用编写和执行授权策略。您将细粒度权限指定为Cedar策略,应用通过调用Cedar SDK来授权访问请求。例如,如果您正在构建宠物商店应用,可以使用以下Cedar策略控制只有jobLevel为employee的用户可以访问POST /pets API。

1
2
3
4
5
6
7
permit (
    principal,
    action in [Action::"POST /pets"], 
    resource
) when {
    principal.jobLevel = "employee"
};

使用Cedar的一种选项是自我管理实现;您可以在另一篇文章中找到此模式的示例:使用Cedar在5分钟内保护应用API。

自我管理的Cedar提供了外部化授权的好处,但需要持续的操作管理。组织负责Cedar版本升级、应用安全补丁、管理策略和审计授权。使用Cedar的另一种选项是使用Verified Permissions。Verified Permissions通过为Cedar提供托管服务来消除这些操作需求。Verified Permissions管理扩展,通过支持集中策略管理简化策略治理,并记录策略变更和授权请求以简化审计。

Pet Store应用API概述

Pet Store应用用于管理宠物商店。宠物商店使用Express与Node.js构建,并暴露下表中的API。

API 描述
GET /api/pets 返回可用宠物列表
GET /api/pets/{petId} 返回找到的指定宠物
POST /api/pets 向宠物商店添加宠物
PUT /api/pets/{petId} 更新现有宠物
DELETE /api/pets/{petId} 从宠物商店移除宠物

此应用不允许所有用户访问所有API。相反,它强制执行以下规则:

  • 管理员:对宠物和管理功能的完全访问权限
  • 员工:可以查看、创建和更新宠物
  • 客户:可以查看宠物和创建新宠物

实现Pet Store API的授权

让我们逐步了解如何使用Verified Permissions和新的Express包保护应用API。初始应用(无授权)可在start文件夹中找到;使用此文件夹跟随文章操作。您可以在finish文件夹中找到应用的完成版本。

完成后,您将实现图1中所示的应用架构。一个使用Amazon Cognito进行身份验证的React前端应用。然后,应用将Cognito返回的身份令牌作为授权头包含到Express后端API中。Express后端使用新的Verified Permissions授权中间件包调用Verified Permissions来授权用户请求。

先决条件

开始之前,请确保具备以下先决条件。

步骤1:设置AWS CLI

某些命令需要AWS命令行界面(AWS CLI)。请参阅安装或更新到最新版本的AWS CLI和配置AWS CLI设置。

步骤2:设置OpenID Connect身份提供者和数据库

Pet Store应用使用OpenID Connect(OIDC)身份提供者管理用户。对于此示例,您使用名为PetStoreUserPool的Amazon Cognito用户池,其中包含三个用户:一个管理员、一个员工和一个客户。

应用还使用Amazon DynamoDB数据库存储宠物。

您可以通过在/start目录中运行以下命令在AWS账户中设置Amazon Cognito和DynamoDB。

1
./scripts/setup-infrastructure.sh

设置脚本将提示您为三个用户设置密码(密码必须至少8个字符,并需要至少一个数字、一个大写字母和一个小写字母)。

注意运行此脚本的输出,因为您将在集成Verified Permissions的步骤5中使用它们。

注意:在您自己的应用中,您可以按照Amazon Cognito控制台中创建新应用的说明设置Amazon Cognito,或者您可以自带OIDC身份提供者。

步骤3(可选):运行应用

现在基础设施已设置,您可以运行应用。在两个单独的终端中,在/start目录中运行以下命令:

1
2
./scripts/run-backend-dev.sh
./scripts/run-frontend-dev.sh

通过创建一些宠物来测试应用。

集成Verified Permissions

具备先决条件后,下一步是集成Verified Permissions。Verified Permissions可以通过六个步骤集成到Express应用中:

  1. 创建Verified Permissions策略存储
  2. 添加Cedar和Verified Permissions授权中间件包
  3. 创建和部署Cedar模式
  4. 创建和部署Cedar策略
  5. 将Verified Permissions策略存储连接到OIDC身份提供者
  6. 更新应用代码以调用Verified Permissions授权API访问

Verified Permissions集成发生在Express Web应用后端。本节中的所有命令应在/start/backend目录中运行。

步骤1:创建Verified Permissions策略存储

使用AWS CLI通过运行以下命令在Verified Permissions中创建策略存储:

1
aws verifiedpermissions create-policy-store --validation-settings "mode=STRICT"

示例成功命令输出:

1
2
3
4
5
6
{
    "policyStoreId": "AAAAbbbbCCCCdddd",
    "arn": "arn:aws:verifiedpermissions::111122223333:policy-store/AAAAbbbbCCCCdddd",
    "createdDate": "2025-06-05T19:30:37.896119+00:00",
    "lastUpdatedDate": "2025-06-05T19:30:37.896119+00:00"
}

保存命令输出中的policyStoreId值以在步骤3中使用。

步骤2:添加Cedar和Verified Permissions授权中间件包

运行以下命令添加两个新依赖项:@verifiedpermissions/authorization-clients-js和@cedar-policy/authorization-for-expressjs

1
2
npm i --save @verifiedpermissions/authorization-clients-js
npm i --save @cedar-policy/authorization-for-expressjs

步骤3:创建和部署Cedar模式

Cedar模式定义应用的授权模型,包括应用中的实体类型和允许用户执行的操作。您将模式附加到Verified Permissions策略存储,当添加或修改策略时,服务会自动根据模式验证策略。

@cedar-policy/authorization-for-expressjs包可以分析应用的OpenAPI规范并生成Cedar模式。具体来说,您的规范中需要OpenAPI模式中的paths对象。

如果您没有OpenAPI规范,可以使用您选择的工具生成一个。有几个开源库可用于为Express执行此操作;您可能需要在应用中添加一些代码,生成OpenAPI规范,然后删除代码。或者,一些基于生成式AI的工具(如Amazon Q Developer CLI)在生成OpenAPI规范文档方面很有效。无论您如何生成规范,请确保验证工具的正确输出。

对于示例应用,已包含一个名为openapi.json的OpenAPI规范文档。

运行以下命令生成Cedar模式。

1
npx @cedar-policy/authorization-for-expressjs generate-schema --api-spec schemas/openapi.json --namespace PetStoreApp --mapping-type SimpleRest

示例成功命令输出:

1
2
3
Cedar schema successfully generated. Your schema files are named: v2.cedarschema.json, v4.cedarschema.json.
v2.cedarschema.json is compatible with Cedar 2.x and 3.x
v4.cedarschema.json is compatible with Cedar 4.x and required by the nodejs Cedar plugins.

接下来,格式化Cedar模式以与AWS CLI一起使用。所需的特定格式在文档Amazon Verified Permissions策略存储模式中描述。要格式化Cedar模式,运行以下命令。

1
../scripts/prepare-cedar-schema.sh v2.cedarschema.json v2.cedarschema.forAVP.json

示例成功命令输出:

1
2
Cedar schema prepared successfully: v2.cedarschema.forAVP.json
You can now use it with AWS CLI:

格式化模式后,运行以下命令将模式上传到Verified Permissions。注意,您需要将替换为实际的策略存储ID,该ID由步骤1中的命令输出提供。

1
aws verifiedpermissions put-schema --definition file://v2.cedarschema.forAVP.json --policy-store-id <policy store id>

示例成功命令输出:

1
2
3
4
5
6
7
8
{
    "policyStoreId": "AAAAbbbbCCCCdddd",
    "namespaces": [
        "PetStoreApp"
    ],
    "createdDate": "2025-06-03T20:19:33.480528+00:00",
    "lastUpdatedDate": "2025-06-05T19:42:45.198325+00:00"
}

步骤4:创建和部署Cedar策略

如果未配置策略,Cedar会拒绝授权请求。下一步是创建策略,允许特定用户组访问特定资源。Express框架集成通过基于先前生成的模式生成示例策略来帮助引导此过程。然后,您可以根据用例自定义这些策略。

运行以下命令生成示例Cedar策略。

1
npx @cedar-policy/authorization-for-expressjs generate-policies --schema v2.cedarschema.json

示例成功命令输出:

1
2
Cedar policy successfully generated in policies/policy_1.cedar
Cedar policy successfully generated in policies/policy_2.cedar

在/policies目录中生成了两个示例策略:policy_1.cedar和policy_2.cedar。policy_1.cedar为admin用户组中的用户提供对任何资源执行任何操作的权限。

1
2
3
4
5
6
7
// policy_1.cedar
// 允许admin用户组访问所有内容
permit (
    principal in PetStoreApp::UserGroup::"admin",
    action,
    resource
);

policy_2.cedar为Cedar模式中定义的单个操作提供更多访问权限,并为特定组提供占位符。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// policy_2.cedar
// 允许更细粒度的用户组控制,根据需要更改操作
permit (
    principal in PetStoreApp::UserGroup::"ENTER_THE_USER_GROUP_HERE",
    action in
        [PetStoreApp::Action::"GET /pets",
         PetStoreApp::Action::"POST /pets",
         PetStoreApp::Action::"GET /pets/{petId}",
         PetStoreApp::Action::"PUT /pets/{petId}",
         PetStoreApp::Action::"DELETE /pets/{petId}"],
    resource
);

注意,如果您在OpenAPI规范中指定了operationId,Cedar模式中定义的操作名称将使用该operationId而不是默认的 /格式。在这种情况下,请确保Cedar策略中的操作命名与Cedar模式中的操作命名匹配。例如,如果您想将操作称为AddPet而不是POST /pets,您可以将OpenAPI规范中的operationId设置为AddPet。生成的Cedar策略中的操作将是PetStoreApp::Action::“AddPet”。

创建第三个策略文件policy_3.cedar,然后用以下策略替换每个文件的内容。将每个策略中的替换为之前复制的Cognito用户池ID。

注意:在实际用例中,考虑根据内容重命名Cedar策略文件,例如allow_customer_group.cedar。

1
2
3
4
5
6
// 定义允许的管理员用户组操作
permit (
    principal in PetStoreApp::UserGroup::"<userPoolId>|administrator",
    action,
    resource
);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 定义的允许员工用户组操作
permit (
    principal in PetStoreApp::UserGroup::"<userPoolId>|employee",
    action in
        [PetStoreApp::Action::"GET /pets",
         PetStoreApp::Action::"POST /pets",
         PetStoreApp::Action::"GET /pets/{petId}",
         PetStoreApp::Action::"PUT /pets/{petId}"],
    resource
);
1
2
3
4
5
6
7
8
9
// 定义的允许客户用户组操作
permit (
    principal in PetStoreApp::UserGroup::"<userPoolId>|customer",
    action in
        [PetStoreApp::Action::"GET /pets",
         PetStoreApp::Action::"POST /pets",
         PetStoreApp::Action::"GET /pets/{petId}"],
    resource
);

策略需要格式化以便与AWS CLI for Verified Permissions一起使用。特定格式在AWS CLI Verified Permissions文档中描述。运行以下命令格式化策略。

1
../scripts/convert_cedar_policies.sh

示例成功命令输出:

1
2
3
4
5
6
7
Converting policies/policy_1.cedar to policies/json/policy_1.json
Created policies/json/policy_1.json
Converting policies/policy_2.cedar to policies/json/policy_2.json
Created policies/json/policy_2.json
Converting policies/policy_3.cedar to policies/json/policy_3.json
Created policies/json/policy_3.json
Conversion complete. JSON policy files are in ../policies/json/

格式化的策略将输出到backend/policies/json/目录。 格式化策略后,运行以下三个命令(每个策略一个)将它们上传到Verified Permissions。策略存储ID在完成步骤2后返回。将替换为实际的策略存储ID。

1
2
3
aws verifiedpermissions create-policy --definition file://policies/json/policy_1.json --policy-store-id <policy store id>
aws verifiedpermissions create-policy --definition file://policies/json/policy_2.json --policy-store-id <policy store id>
aws verifiedpermissions create-policy --definition file://policies/json/policy_3.json --policy-store-id <policy store id>

示例成功命令输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "policyStoreId": "AAAAbbbbCCCCdddd",
    "policyId": "8AmzZYMw6Ux5DGBoX7w24m",
    "policyType": "STATIC",
    "principal": {
        "entityType": "PetStoreApp::UserGroup",
        "entityId": "<userPoolId>|administrator"
    },
    "createdDate": "2025-06-05T19:46:45.848602+00:00",
    "lastUpdatedDate": "2025-06-05T19:46:45.848602+00:00",
    "effect": "Permit"
}

或者,您也可以在AWS管理控制台中将Cedar策略复制粘贴到Verified Permissions中。

步骤5:将Verified Permissions策略存储连接到OIDC身份提供者

默认情况下,Verified Permissions授权器中间件读取API请求授权头中提供的JSON Web令牌(JWT)以获取用户信息。Verified Permissions可以验证令牌,除了执行授权策略评估外。

为此,在Verified Permissions策略存储中创建身份源。为简化AWS CLI命令中的格式化,我们已在identity-source-configuration.txt中定义了身份源配置。根据运行先决条件步骤2中的setup-infrastructure.sh脚本的输出,替换以下代码块中的参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// identity-source-configuration.txt
{
    "cognitoUserPoolConfiguration": {
        "userPoolArn": "<userPoolArn>",
        "clientIds":["<clientId>"] ,
        "groupConfiguration": {
              "groupEntityType": "PetStoreApp::UserGroup"
        }
    }
}

更新文件后,运行以下命令更新Verified Permissions策略存储。将替换为实际的策略存储ID。

1
aws verifiedpermissions create-identity-source --configuration file://identity-source-configuration.txt --policy-store-id <policy store id> --principal-entity-type PetStoreApp::User

示例成功命令输出:

1
2
3
4
5
6
{
    "createdDate": "2025-06-05T20:02:53.992782+00:00",
    "identitySourceId": "DTLvwdiKfdPmk2RWzSVfu2",
    "lastUpdatedDate": "2025-06-05T20:02:53.992782+00:00",
    "policyStoreId": "AAAAbbbbCCCCdddd"
}

步骤6:更新应用代码以调用Verified Permissions授权API访问

您现在需要更新应用以使用@verifiedpermissions/authorization-clients-js和@cedar-policy/authorization-for-expressjs依赖项。这将允许应用调用Verified Permissions来授权API请求。

通过将以下代码块添加到backend/app.ts的第13行(直接在import语句之后)来添加依赖项并定义CedarAuthorizerMiddleware和AVPAuthorizer。将以下代码块中的替换为您的实际Verified Permissions策略存储ID。

1
2
3
4
5
6
const { ExpressAuthorizationMiddleware } = require('@cedar-policy/authorization-for-expressjs');

const { AVPAuthorizationEngine } = require('@verifiedpermissions/authorization-clients-js');

const avpAuthorizationEngine = new AVPAuthorizationEngine({
    policy
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计