指导和见解

合作伙伴 API Authorization AI 测试 API 经销商门户

一个实用的测试计划,用于防止合作伙伴 API 中的跨租户访问,这些 API 创建客户、颁发密钥、添加积分、导出使用情况和冻结访问。

经销商门户的

合作伙伴API必须假设请求中的每个对象 ID 都可以被其他集成复制、猜测、重播、记录,或者通过错误的员工工具传递。核心问题不在于客户ID是否不透明。问题是每个操作是否都证明调用者、合作伙伴帐户、目标租户、对象、属性和审批状态属于同一组。

这在 AI API 经销商门户 中最为重要,因为 API 通常会公开影响较大的控制平面对象:下游客户、虚拟 API 密钥、钱包、信用调整、使用情况导出、webhook 目标、员工用户和紧急冻结。一次缺失的授权检查可以让一个客户读取另一位客户的使用情况,让员工令牌在租户之间转移积分,或者让自动化令牌撤销它永远不会看到的密钥。

事实: OWASP API Security 2023 将损坏的对象级别 Authorization 列为 API1,将损坏的对象属性级别 Authorization 列为 API3。实际上,对象级检查回答“这个调用者可以对该对象执行操作吗?”而属性级检查则回答“调用者可以设置或读取该字段吗?”两者都是合作伙伴API设计的首要关注点。

在编写测试之前定义Authorization合同

Authorization 测试仅在针对显式政策协定进行测试时才有用。从一个矩阵开始,描述哪个参与者可以在什么条件下对哪个目标执行哪个操作。请勿依赖端点名称或 UI 角色作为政策。

有用的矩阵包含以下几列:

  • 参与者类型:合作伙伴所有者、合作伙伴管理员、财务操作员、支持操作员、自动化令牌、客户管理员、客户只读用户。
  • 合作伙伴帐户:拥有下游客户关系的经销商或代理帐户。
  • 目标租户:正在创建、计费、检查、暂停或删除的下游客户帐户。
  • 对象类型:客户、虚拟密钥、钱包、分类帐条目、使用情况报告、网络钩子端点、员工用户、定价配置文件、提供商映射。
  • 操作:创建、读取、列出、更新、撤销、充值、退款、导出、冻结、解冻。
  • 金融机构:该操作是否会更改余额、信用额度、保证金、结算状态、发票状态或支出上限。
  • 批准状态:请求是否已预先批准、正在等待批准、双重批准、已过期或仅限紧急情况。

例如,支持操作员可能被允许查看客户的屏蔽密钥元数据并在事件期间冻结密钥,但不得创建积分、更改定价目录、导出完整的使用数据或在未经批准的情况下解冻密钥。财务运营商可以为同一合作伙伴拥有的客户创建信用调整,但仅限在配置的金额内且仅在存在批准记录之后。

建议:将矩阵写入版本化工件,然后使每个授权测试名称引用一行或规则。当政策更改时,差异应同时显示合同更改和测试更改。

使用租户绑定对象引用

不透明 ID 可以降低枚举风险,但不能证明授权。虚拟密钥 ID、钱包 ID、账本条目 ID、使用导出 ID 或游标令牌必须在预期的合作伙伴和租户边界内解析。

安全的查找模式是:

resolve object where
  object.id = request.object_id
  and object.partner_id = actor.partner_id
  and object.customer_id = request.customer_id
  and actor has action_scope for object_type/action
  and object.state allows action

危险模式是:

object = find_by_id(request.object_id)
if actor.has_scope("keys:revoke"):
  revoke(object)

第二个版本授权拥有范围,而不是对目标的权限。当合作伙伴 A 的有效令牌从合作伙伴 B 提交密钥 ID 时,或者当客户 A 的操作员从客户 B 提交钱包 ID 时,它会失败。

租户绑定不应涵盖明显的对象。包括使用报告的游标令牌、导出作业 ID、Webhook 端点 ID、批准 ID、幂等性密钥和分类帐调整 ID。这些物体通常给人“内部”感觉,这正是它们成为弱点的原因。

构建最小跨租户测试装置

故意使用较小的固定装置,以便更容易表达错误的访问。目标不是广泛的数据量。目标是明确分离。

Partner P1
  Staff: p1_owner, p1_support, p1_finance
  Token: p1_full_token, p1_support_token, p1_finance_token
  Customer C1A
    Key K1A
    Wallet W1A
    Usage cursor U1A
    Ledger entry L1A
  Customer C1B
    Key K1B
    Wallet W1B
    Usage cursor U1B
    Ledger entry L1B

Partner P2
  Staff: p2_owner
  Token: p2_full_token
  Customer C2A
    Key K2A
    Wallet W2A
    Usage cursor U2A
    Ledger entry L2A

此装置可让您测试两个故障类别。首先,同一合作伙伴跨客户访问,来自 P1 的呼叫者在对 C1A 进行操作的同时尝试接触 C1B。其次,跨合作伙伴访问,其中 P1 令牌尝试接触 P2 的对象。

建议: 针对公共合作伙伴 API 和经销商门户后端运行相同的装置。用户界面限制无法保护直接的 API 客户端、后台作业、浏览器开发工具或受损的集成令牌。

在每个高影响端点上测试对象级Authorization

对于每个接受对象 ID 的端点,创建至少一个肯定测试和多个否定测试。积极的测试证明该夹具是有效的。负面测试证明边界成立。

客户创建和查找

创建客户通常看起来无害,因为目标租户尚不存在。它仍然需要授权,因为调用者可能会尝试在不同的合作伙伴下创建客户、分配禁止的定价配置文件或设置内部元数据。

测试用例:

  • 合作伙伴所有者可以在同一合作伙伴下创建客户。
  • 具有客户配置范围的自动化令牌可以在同一合作伙伴下创建客户。
  • 仅供支持的令牌无法创建客户。
  • P1 令牌无法使用 partner_id=P2.
  • 创建客户
  • 客户级令牌无法列出同级客户。

密钥创建、撤销和轮换

关键生命周期操作影响很大,因为它们授予运行时访问权限并可能中断生产工作负载。提供商控制平面通常通过 API 公开管理对象,例如项目、工作区、服务帐户、成员和 API 密钥。网关或经销商层应同样谨慎地对待自己的虚拟密钥。

测试用例:

  • 当 C1A 的令牌包含密钥写入范围时,P1 自动化只能为 C1A 创建 K1A。
  • 如果支持角色对于密钥是只读的,P1 支持无法创建 K1A。
  • 仅当紧急冻结范围内时,P1 支持才能冻结 K1A。
  • 当请求上下文为 C1A 时,P1 支持无法撤销 K1B。
  • P1完整令牌无法撤销K2A。
  • C1A 的客户管理员无法轮换、透露或撤销 K1B。

对于实施合作伙伴API自动化的团队来说,重要的测试不仅仅是“此令牌是否具有密钥写入?”它是“这个令牌是否有这个合作伙伴、这个客户、这个密钥、这个状态和这个操作的密钥写入?”

积分充值和钱包变更

资金转移操作需要比普通更新更严格的测试。信用充值可能在语法上是正确的,但仍然未经授权,因为钱包属于另一个租户、金额超出权限、批准记录针对另一个客户,或者幂等密钥首先用于不同的操作。

测试用例:

  • P1金融审核通过后可以在限额内为W1A充值。
  • P1 财务无法使用为 W1A 创建的批准为 W1B 充值。
  • P1金融无法充值W2A。
  • P1 支持无法为任何钱包充值。
  • 自动化令牌无法更改信用额度,除非它具有明确的资金转移范围。
  • 重放的幂等密钥无法将之前的 W1A 充值重定向到 W1B。

建议: 将幂等键视为范围为合作伙伴加操作加目标租户加上规范化请求正文。重试应该是安全的,但不应该成为跨租户冲突通道。

使用情况报告和导出游标

使用情况报告很容易受到保护不足,因为它们是只读的。在经销商系统中,使用数据可以揭示客户名称、工作负载、模型选择、成本、最终用户标识符和商业利润。 Cursor 令牌和导出 ID 也必须与租户绑定。

测试用例:

  • C1A 管理员可以读取 C1A 使用存储桶。
  • C1A 管理员无法通过更改 customer_id.
  • 来读取 C1B 使用情况
  • 仅当导出范围存在时,P1 只读分析令牌才能导出 P1 客户的使用情况。
  • P1 令牌无法使用复制的游标恢复 P2 使用情况导出。
  • 除非明确允许,否则为关键级报告创建的游标不能重复用于获取租户范围内的使用情况。

如果门户公开AI API客户使用计量,请测试摘要视图和可下载导出。导出路径通常会绕过交互式页面使用的相同控制器或策略代码。

在 PATCH 和更新端点上测试属性级 Authorization

损坏的对象属性级别 Authorization 当允许调用者更新对象但也可以设置应属于内部、特权或由其他工作流程拥有的字段时,就会出现。

对于合作伙伴 API,敏感字段通常包括:

  • partner_id、customer_id 和父所有权字段。
  • provider_project_id、provider_workspace_id和上游关键引用。
  • pricing_catalog_version、margin_tier 和折扣覆盖。
  • wallet_balance、credit_limit、settlement_status 和发票状态。
  • fraud_status、risk_score 和合规性标志。
  • role、scopes 和审批绕过标志。

使用允许名单,而不是拒绝名单。例如,客户资料更新端点可能允许 display_name、billing_email 和 metadata.external_reference。即使调用者可以更新客户对象,它也应该拒绝或忽略 margin_tier、credit_limit 和 provider_project_id。

PATCH /partner/customers/C1A
{
  "display_name": "Acme Support Team",
  "margin_tier": "internal_zero_margin",
  "provider_project_id": "provider-project-owned-by-C1B"
}

预期结果应该是确定性的。首选验证错误来识别不允许的字段而不泄露敏感的内部值。不要默默地接受特权字段,然后忽略它们,除非您的 API 合约已经一致地使用该模式。

使拒绝成为非枚举

负面测试应检查状态代码和响应形状。如果 P1 令牌请求 K2A,则响应不应显示 K2A 是否存在于其他合作伙伴之下。

常见规则是:

  • 当调用者未经身份验证时使用401。
  • 当调用方已通过身份验证但其自身边界中缺少操作范围时,请使用403。
  • 当对象位于调用者的边界之外且不应泄露其存在时,请使用 404 或通用未找到响应。

确切的约定可能有所不同,但行为必须一致。拒绝主体应避免诸如“密钥属于另一个客户”或“钱包存在于合作伙伴 P2 下”之类的消息。这些信息对攻击者有用,但对合法客户端却很少有用。

权衡:通用拒绝会让合作伙伴开发者的调试变得更加困难。通过请求 ID、结构化审核日志和支持工具来解决这个问题,而不是通过在公共 API 错误中泄露跨租户的存在。

记录Authorization决策,而非敏感负载

Authorization 测试还应该断言否认会产生有用的内部证据。您需要足够的详细信息来重建事件期间发生的情况,而无需存储原始提示、机密或不必要的客户数据。

日志字段应包括:

  • request_id 和跟踪相关 ID。
  • actor_id、参与者类型、令牌 ID 和角色模板。
  • partner_id 来自经过身份验证的演员。
  • 请求的目标租户和解析的目标租户(如果允许解析)。
  • 对象类型和操作。
  • 政策版本和决策结果。
  • 拒绝原因代码,例如missing_scope、tenant_mismatch、approval_required或field_not_allowed.

请勿记录完整的 API 密钥、上游提供商机密、提示、原始使用负载或完整导出内容。对于被拒绝的跨租户请求,请注意内部日志是访问控制的;当安全团队仍然有足够的证据时,公众的反应可以是笼统的。

自动化 CI 和暂存中的线束

测试工具应在三个位置运行:策略功能的单元测试、API 端点的集成测试以及针对已部署的经销商门户后端的暂存探测。每层都会捕获不同的故障。

实用的 CI 结构:

authz-tests/
  fixtures/
    partners.json
    customers.json
    roles.json
    tokens.json
  matrix/
    keys.yaml
    wallets.yaml
    usage_exports.yaml
    customers.yaml
  tests/
    test_keys_authz.ts
    test_wallet_authz.ts
    test_usage_authz.ts
    test_patch_allowlists.ts

每个矩阵行应定义参与者、请求、预期决策和预期非枚举行为。

- name: p1_support_cannot_revoke_p2_key
  actor: p1_support_token
  method: POST
  path: /partner/customers/C2A/keys/K2A/revoke
  expected_status: 404
  expected_error_code: not_found
  expected_audit_reason: tenant_mismatch

添加每次交换一个标识符的突变样式变体:客户 ID、密钥 ID、钱包 ID、批准 ID、游标、幂等密钥和 Webhook 端点 ID。这些测试在设计上是重复的。当能够证明每个标识符的作用域是独立的时,重复是可以接受的。

操作清单

在运送合作伙伴 API 端点之前,请使用此清单:

  • 端点在政策矩阵中具有指定的授权规则。
  • 每个对象查找都包含合作伙伴和租户边界条件。
  • 每个列表端点在分页之前都会按参与者边界进行过滤。
  • 每个游标、导出 ID、网络钩子 ID、批准 ID 和幂等性密钥都有范围。
  • PATCH 端点按角色和操作使用属性许可名单。
  • 资金转移操作需要明确的范围和批准状态。
  • 紧急冻结与解冻和信用授权是分开的。
  • 拒绝可避免跨租户存在泄漏。
  • 审核日志记录授权决策和政策版本。
  • 针对 API 和门户后端路径运行相同的负面测试。

明确决定的权衡

细粒度的范围可以减少影响范围,但会增加合作伙伴加入的复杂性。默认角色模板可帮助:所有者、财务、支持、分析、配置自动化和紧急响应。让高级合作伙伴根据这些模板进行自定义,而不是从原始范围开始。

严格的媒体资源许可名单会减慢功能的推出速度,因为每个新的可编辑字段都需要更改政策。这种摩擦对于计费、定价、提供商映射和密钥管理领域很有用。对于低风险配置文件字段,流程可以更轻松。

审批流程可减少意外或恶意信用更改,但会增加合法支持操作的延迟。保持紧急冻结快速、可逆且范围单独。请勿让同一个紧急令牌添加积分、更改保证金、导出用途和解冻帐户。

预测: 经销商门户将继续通过合作伙伴 API 公开更多管理操作,因为客户期望自助服务配置、报告和自动化。老化良好的系统将是具有授权合同和负面测试的系统,而不是仅依赖于路由命名、隐藏的 UI 控件或不透明 ID 的系统。

可行的结论

在合作伙伴 API 变得足够大以至于每个端点都有自己的异常之前构建授权工具。从两个合作伙伴、一个合作伙伴下的两个客户、另一个合作伙伴下的一个客户以及一小组代币开始。然后测试对您影响最大的操作:密钥创建、密钥撤销、信用充值、使用情况导出、钱包更新、批准重用、幂等性重播和 PATCH 字段。

合同很容易说明:调用者必须在正确的合作伙伴下拥有正确的范围、正确的租户、对象、属性、操作、金额和批准状态。实施工作正在每个端点(包括无聊的端点)上实现这一点。

相关阅读

FAQ

常见问题

不透明 ID 是否足以阻止跨租户合作伙伴 API 访问?
不透明的 ID 可以减少猜测和枚举风险,但每个请求仍然需要绑定参与者、合作伙伴、租户、对象、操作和状态的授权检查。
授权测试是否应该仅针对公共 API 运行?
不需要。针对公共合作伙伴 API 和经销商门户后端运行它们。 UI 检查不保护直接 API 客户端、后台工作人员、受损令牌或备用后端路由。
哪些合作伙伴 API 端点首先需要否定授权测试?
从高影响力的端点开始:客户创建、密钥创建和撤销、钱包充值、信用限额、使用导出、审批记录、幂等性密钥、Webhook 目标以及客户或计费对象的 PATCH 端点。
当令牌引用另一个租户的对象时,API 应如何响应?
使用非枚举响应约定。当对象位于调用者边界之外时,许多 API 会返回通用的未找到响应,同时在内部审核日志中记录确切的拒绝原因。