如何授权 SDK 访问 AWS API Gateway 后端 API?
明确结论
不能把 Android 游戏中的 SDK 视为可信的 machine-to-machine 客户端,也不能在 SDK 中保存长期密钥、OAuth client_secret 或 AWS Access Key。APK、运行内存和网络流量都可能被分析,随 SDK 分发的共享凭据最终可能被提取并滥用。
推荐使用以下架构:
Android SDK
│ 购买凭证 + 游戏方短期身份凭证
▼
游戏开发者的后端(可信服务)
│ OAuth 2.0 Client Credentials / AWS SigV4
▼
AWS API Gateway
▼
支付后端
如果无法要求接入方部署后端,只能采用设备级风险控制方案,例如短期临时凭证、Play Integrity API、请求签名、防重放和限流。这些措施可以提高攻击成本,但无法让 SDK 成为真正可信的客户端。
为什么 SDK 不能安全保存机器凭据
machine-to-machine 认证通常假设调用方是能够保守秘密的机密客户端,例如服务器、容器或受控工作负载。Android SDK 不具备这一条件:
- APK 可以被反编译;
client_secret、API Key 和 AWS Access Key 可以被静态提取;- Native Library、代码混淆和字符串加密只能延缓提取;
- 攻击者可以 Hook SDK、读取运行时内存或直接重放请求;
- 如果所有游戏共用同一凭据,一次泄露就会影响全部接入方;
- 为每个游戏分配独立凭据只能缩小影响范围,无法解决客户端不能保密的问题。
API Gateway 的 API Key 主要用于识别调用方、配额管理和用量控制,不应作为认证凭据。CORS 也不是移动端 API 的安全边界。
推荐方案:由游戏后端代表 SDK 调用 API
为每个游戏开发者注册一个服务端客户端,并分配独立的 client_id 和 client_secret。这些凭据只部署在游戏开发者的后端,不进入 Android 应用。
典型流程如下:
- Android SDK 从 Google Play Billing 获取
purchaseToken、productId等购买信息。 - SDK 将购买信息和当前游戏会话凭证发送到游戏开发者自己的后端。
- 游戏后端验证玩家会话、请求完整性和业务参数。
- 游戏后端通过 OAuth 2.0 Client Credentials 获取短期 Access Token,或者使用 AWS IAM 凭据对请求进行 SigV4 签名。
- 游戏后端调用 API Gateway。
- 支付后端向 Google Play 服务端验证购买凭证,并进行幂等处理。
- 结果沿原路径返回 SDK。
不能只相信 SDK 上报的“支付成功”状态。最终购买状态应由可信后端向 Google Play 验证。
方案一:OAuth 2.0 Client Credentials
可以使用支持 Client Credentials Grant 的授权服务器签发短期 JWT,再由 API Gateway 的 JWT Authorizer 或 Lambda Authorizer 验证 Token。
能否直接使用 API Gateway 的 JWT Authorizer,取决于 API Gateway 类型、Token 格式和所用的身份提供商。如果条件不满足,可以改用 Lambda Authorizer。
游戏后端获取 Token:
curl -X POST 'https://auth.example.com/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-u "${CLIENT_ID}:${CLIENT_SECRET}" \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=payments:start'
随后调用 API Gateway:
curl -X POST 'https://api.example.com/payments' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 1eab44b1-97a2-43ee-8507-709d60ed6028' \
-d '{
"productId": "coins_100",
"purchaseToken": "token-returned-by-google-play"
}'
Token 的有效期应较短,并通过 scope、aud 和客户端身份限制权限。例如,只允许调用 payments:start,而不是整个 API。
方案二:AWS IAM 与 SigV4
如果调用方后端运行在 AWS 上,可以为其配置 IAM Role,并把 API Gateway 路由设为 AWS_IAM 授权。调用方使用临时 AWS 凭据,通过 SigV4 对请求签名。
Node.js 示例:
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { SignatureV4 } from "@smithy/signature-v4";
import { HttpRequest } from "@smithy/protocol-http";
import { Sha256 } from "@aws-crypto/sha256-js";
const region = "ap-southeast-1";
const url = new URL("https://api.example.com/payments");
const body = JSON.stringify({
productId: "coins_100",
purchaseToken: "token-returned-by-google-play"
});
const signer = new SignatureV4({
credentials: defaultProvider(),
region,
service: "execute-api",
sha256: Sha256
});
const request = new HttpRequest({
method: "POST",
protocol: url.protocol,
hostname: url.hostname,
path: url.pathname,
headers: {
host: url.hostname,
"content-type": "application/json",
"idempotency-key": crypto.randomUUID()
},
body
});
const signedRequest = await signer.sign(request);
const response = await fetch(url, {
method: signedRequest.method,
headers: signedRequest.headers,
body
});
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
console.log(await response.json());
IAM Policy 应遵循最小权限原则,只允许访问所需的 API、Stage、HTTP Method 和 Resource Path。
不要把 IAM User 的长期 Access Key 编译进 SDK。即使通过 STS 下发临时凭据,也必须先解决“谁有资格获得这些凭据”的引导认证问题。
Android SDK 应承担什么职责
SDK 负责采集购买信息并调用可信后端,不应持有服务端机密。例如:
data class PurchaseRequest(
val productId: String,
val purchaseToken: String,
val requestId: String
)
suspend fun submitPurchase(
api: GameBackendApi,
sessionToken: String,
productId: String,
purchaseToken: String
): PurchaseResult {
return api.submitPurchase(
authorization = "Bearer $sessionToken",
request = PurchaseRequest(
productId = productId,
purchaseToken = purchaseToken,
requestId = UUID.randomUUID().toString()
)
)
}
这里的 sessionToken 用于识别当前游戏会话或玩家,并不是保存在 SDK 中的全局秘密。它可以由游戏后端签发,同时设置较短的有效期和有限的权限。
没有游戏后端时的折中方案
如果业务要求 SDK 直接调用 API Gateway,可以采用公开客户端流程:
- SDK 请求 Google Play Integrity Token。
- SDK 将 Integrity Token、应用包名、证书摘要、时间戳和随机数发送到后端。
- 后端验证 Integrity Token,同时检查应用身份和设备风险信号。
- 验证通过后,后端签发一个有效期只有数分钟的受限 Token。
- SDK 使用该 Token 调用 API Gateway。
- 后端根据请求频率、额度、设备、应用和行为实施风控。
请求中可以加入防重放字段:
{
"timestamp": 1787971200,
"nonce": "80b06031-2782-41cf-9556-95431a1b2642",
"productId": "coins_100",
"purchaseToken": "token-returned-by-google-play"
}
服务端需要验证:
timestamp是否在允许的时间窗口内;nonce是否已被使用;- Token 的
iss、aud、exp和权限范围; - 包名和签名证书是否属于已注册应用;
- 购买凭证是否已经处理;
- 请求频率和异常行为是否超过阈值。
Play Integrity 提供的是设备和应用完整性的风险信号,并不等同于不可伪造的客户端身份。Root、Hook、代理转发和真实设备农场仍可能绕过部分限制,因此,高价值支付操作不能只依赖设备证明。
支付接口还需要的保护
无论采用哪种认证方式,还应实施以下控制:
- 由服务端验证 Google Play
purchaseToken; - 使用
purchaseToken、订单号或业务请求 ID 实现幂等; - 每个开发者使用独立的客户端身份,避免共享凭据;
- 缩短 Access Token 的有效期,并限制
audience和scope; - 按客户端、应用、设备和 IP 分层限流;
- 记录客户端 ID、请求 ID 和授权结果,但不要记录完整 Token;
- 支持密钥轮换、吊销,以及对单个开发者进行隔离;
- 使用 HTTPS,不要自行设计加密协议;
- 不要把 API Key、OAuth
client_secret、私钥或 AWS Access Key 放进 APK; - 在服务端校验金额、商品、币种和订单状态,不能信任客户端参数。
SDK 可以证明“它带来了某些信号”,但无法安全保守一个所有安装实例都能使用的秘密。真正的 machine-to-machine 授权应发生在两个受控后端之间。
备注:内容仅供参考。
