如何授权 SDK 访问 AWS Api Gateway 后的 API?
结论
Android SDK 不能被视为可信的 machine-to-machine 客户端,也不适合保存长期凭证。APK 安装在用户可以控制的设备上,其中的 client_secret、AWS Access Key、API Key、私钥和固定签名密钥都有可能被提取并滥用。
应根据调用方选择安全方案:
- 游戏开发者有自己的后端时,由开发者后端通过 OAuth 2.0 Client Credentials、AWS IAM SigV4 或 mTLS 调用 API Gateway。这类调用才属于 machine-to-machine。
- SDK 必须直连 API Gateway 时,应使用用户登录态或后端签发的短期会话令牌。没有登录用户时,可结合应用实例注册和 Play Integrity 进行风险校验,但设备证明不足以承担完整的支付授权。
- In-app purchase 结果必须由后端向 Google Play 核验。SDK 上传的购买状态、商品价格和订单数据不能直接作为可信依据。
API Gateway 的 API Key 和 Usage Plan 可用于调用统计、配额管理和限流,不能代替身份认证。
推荐架构
建议采用以下调用链路:
Android 游戏
│
│ 1. 通过 Google Play Billing 发起购买
│ 2. 获得 purchaseToken
▼
游戏后端或业务后端
│
│ 3. 验证调用方身份
│ 4. 向 Google Play 服务端验证 purchaseToken
│ 5. 获取短期 access token 或使用 SigV4
▼
AWS API Gateway
│
│ 6. JWT Authorizer / IAM / Lambda Authorizer
▼
支付业务服务
支付创建、发货和权益确认都应由后端完成。SDK 负责发起购买、收集 Google Play 返回的数据,再将这些数据提交给可信后端。
方案一:由开发者后端使用 Client Credentials
如果接入 SDK 的游戏厂商有后端,可以为每个合作方分配独立的 OAuth 客户端。client_secret 只能保存在合作方的后端,不能打包进 APK。
以支持 OAuth 2.0 Client Credentials 的授权服务器为例,开发者后端先申请令牌:
curl -X POST 'https://auth.example.com/oauth2/token' \
-u "${CLIENT_ID}:${CLIENT_SECRET}" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=payments/start'
授权服务器返回短期令牌:
{
"access_token": "<access-token>",
"token_type": "Bearer",
"expires_in": 3600
}
开发者后端随后调用 API Gateway:
curl -X POST 'https://api.example.com/payments' \
-H 'Authorization: Bearer <access-token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: <unique-request-id>' \
-d '{
"packageName": "com.example.game",
"productId": "coins_100",
"purchaseToken": "<google-play-purchase-token>"
}'
API Gateway 可以配置 JWT Authorizer,校验令牌签名以及 iss、aud、exp 和所需 scope,例如 payments/start。使用 Amazon Cognito User Pools 作为授权服务器时,可以配置 app client、resource server 和 custom scope。带 secret 的 app client 仍然只能由可信后端使用。
每个合作方都应使用单独的客户端凭证,以便分别限流、审计、轮换凭证或吊销访问权限。
方案二:SDK 直接调用 API Gateway
业务要求 SDK 直连 API Gateway 时,应让 SDK 携带后端签发的短期令牌,避免在应用中内置共享密钥。
已有登录体系的业务可以使用 OpenID Connect 或 OAuth 2.0 Authorization Code with PKCE。API Gateway 负责验证 access token,业务服务还要检查用户、游戏、订单和商品是否匹配。
令牌可由宿主游戏提供给 SDK,SDK 无需直接管理用户凭证:
interface AccessTokenProvider {
suspend fun getAccessToken(): String
}
class AuthorizationInterceptor(
private val tokenProvider: AccessTokenProvider
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val token = runBlocking {
tokenProvider.getAccessToken()
}
val request = chain.request()
.newBuilder()
.header("Authorization", "Bearer $token")
.build()
return chain.proceed(request)
}
}
生产代码还需处理令牌过期、并发刷新、请求取消和刷新失败,不能在每个请求中无条件刷新令牌。日志中也不应输出 access token。
没有用户登录时,可以建立匿名会话:
- SDK 向后端申请一次性 challenge。
- SDK 使用 Play Integrity 获取完整性证明,并将 challenge 绑定到证明中。
- 后端验证证明、应用包名、签名证书和请求时效。
- 验证通过后,后端签发有效期较短且权限有限的会话令牌。
- API Gateway 验证令牌,业务服务继续核验购买凭证。
Play Integrity 能增加批量伪造请求的成本,但设备仍可能被控制,应用也可能遭到动态注入或 Hook。因此,完整性证明只能作为风险信号。涉及扣款、发货或权益变更时,后端仍需核验用户和交易状态。
AWS API Gateway 应如何配置
可根据调用场景选择以下方式。
JWT 或 Cognito Authorizer
这种方式适合由 SDK 携带用户 access token,也适合可信后端使用 OAuth 2.0 Client Credentials。API Gateway 负责拒绝无效、过期或 scope 不足的令牌。
业务服务还需执行对象级授权。令牌允许调用 payments/start,不等于调用方有权操作任意游戏、用户或订单。
AWS IAM 和 SigV4
这种方式适合运行在 AWS 上的可信服务,例如 Lambda、ECS 或 EC2。服务通过 IAM Role 获取临时凭证,再使用 SigV4 对请求签名。
Android APK 中不能内置 IAM User 的 Access Key。代码混淆或拆分存储也无法阻止此类凭证被恢复。
如果通过 Cognito Identity Pools 向移动端发放临时 AWS 凭证,应将 IAM 权限限制到必要的 API、Stage、HTTP 方法和资源路径。匿名身份通常不应直接拥有创建支付或发放权益的权限。
Lambda Authorizer
当标准 JWT Authorizer 无法表达令牌格式、合作方签名协议或租户授权规则时,可以使用 Lambda Authorizer。自定义方案仍需采用成熟的签名算法、短期凭证和防重放机制,不应自行设计加密算法。
Google Play 购买必须在服务端核验
Android 客户端上传的 purchaseToken 只是核验所需的输入,不能直接证明支付成功。后端至少要检查:
packageName、productId是否与配置一致;purchaseToken能否通过 Google Play Developer API 验证;- 购买状态、取消状态和确认状态是否符合业务要求;
- 该 token 是否已被其他用户或订单使用;
- 当前用户是否有权为相应游戏账号领取权益;
- 重试请求是否返回同一笔业务结果。
服务端调用 Google Play 所需的凭证必须保存在后端,例如受控的 Secret Manager 或运行环境凭证中,不能随 SDK 一起发布。
不能作为授权方案的做法
以下做法都无法安全识别 SDK:
- 在 APK 中写死 API Key;
- 在 APK 中保存 OAuth
client_secret; - 将 AWS Access Key 和 Secret Access Key 打包进应用;
- 使用固定 HMAC 密钥为请求签名;
- 依赖包名、User-Agent、请求头或代码混淆判断调用方;
- 仅凭 API Gateway Usage Plan 放行支付请求;
- 只检查客户端上传的
purchaseState或success=true。
这些值都可以被复制。攻击者取得凭证后,便能绕过 SDK 直接构造请求。
还需要补上的安全控制
支付接口还应实施以下控制:
- 为 access token 设置较短的有效期,并支持凭证轮换和吊销;
- 通过 scope 和服务端权限检查,限制各客户端可以调用的操作;
- 使用
Idempotency-Key或购买 token,保证支付创建和发货幂等; - 将一次性 challenge 与订单、用户绑定,并限制重放窗口;
- 按合作方、用户、设备和 IP 分层限流;
- 日志中不得记录完整的 access token、
client_secret或purchaseToken; - 价格、币种、商品权益和订单归属以服务端配置为准;
- 针对异常购买频率、重复 token 和账号切换设置风控规则。
长期身份应保存在可信后端。移动端只持有短期、可撤销的会话权限,每笔 Google Play 交易都由服务端独立验证,无需为 SDK 设置可永久保存的”机器密码”。
备注:内容仅供参考。
