mpaya5/aws_kms_lambda_bsc

GitHub: mpaya5/aws_kms_lambda_bsc

基于 AWS KMS 与 Lambda 的 BSC 链交易签名服务,确保后端应用在不接触原始私钥的前提下安全完成 Web3 交易签名。

Stars: 1 | Forks: 0

# 适用于 BSC 的 AWS KMS Lambda 签名器 基于 AWS KMS/Lambda 的签名基础设施,适配于 **BSC**,专注于**安全的 Web3 后端交易工作流**。 核心目标:**避免在应用服务中暴露原始私钥**。您的后端构建未签名的交易,并将其委托给调用 **AWS KMS**(`ECC_SECG_P256K1`)的独立 Lambda 函数进行签名。后端的私钥数据绝不会出现在环境变量、请求 payload 或应用程序内存中。 本仓库**扩展**了上游 AWS Ethereum 示例以支持 BSC。它不是一个被动的 fork:它适配了 BSC 后端签名工作流的链配置、交易组装和安全文档。请参阅 [AWS-README.md](AWS-README.md) 了解原始的 Ethereum 深入解析。 上游:[aws-samples/aws-kms-ethereum-accounts](https://github.com/aws-samples/aws-kms-ethereum-accounts)。 ## 我的贡献 / BSC 适配 - 添加了 BSC / Chapel 链配置(`ETH_NETWORK`、`CHAIN_ID`,主网 `56` / 测试网 `97`)。 - 为 BSC 的 Gas 模型适配了传统交易组装(`gas` + `gasPrice`,无 EIP-1559 type-2)。 - 针对防重放保护调整了 EIP-155 签名和 `chainId` 处理。 - 添加了关于 BSC 签名的演示示例和测试(`examples/`、`tests/test_signing_core.py`)。 - 记录了后端签名工作流的威胁模型和安全假设。 - 移除了传统的 `privateKey` / `SKEYS` payload 模式,全面采用仅通过 KMS 进行签名。 ## 本仓库不包含什么 - **不**是托管的资产托管解决方案。 - **未**经过审计。 - **不**是钱包。 - **不**旨在绕过运维控制。 - 在没有 IAM 加固、监控和密钥生命周期管理的情况下,**不**具备生产环境可用性。 ## 快速开始(测试) ``` git clone && cd aws_kms_lambda_bsc python3 -m venv .venv && source .venv/bin/activate pip install -r requirements-dev.txt pytest -v ``` 预期结果:在 `tests/test_signing_core.py` 中 **11 项通过**(EIP-2、EIP-155、chain id 解析、tx 验证、拒绝包含秘密字段)。 ## 架构 ``` sequenceDiagram participant Backend as Application backend participant Lambda as Lambda signer participant KMS as AWS KMS (CMK) participant RPC as BSC RPC node Backend->>Lambda: unsigned tx (to, nonce, gas, gasPrice, data, value) Lambda->>KMS: GetPublicKey KMS-->>Lambda: DER public key Lambda->>Lambda: derive BSC address, hash unsigned tx Lambda->>KMS: Sign(digest) KMS-->>Lambda: ECDSA (r, s) Lambda->>Lambda: recover v, assemble EIP-155 signed tx Lambda-->>Backend: signed_tx (rawTransaction, hash, r, s, v) Backend->>RPC: eth_sendRawTransaction (optional) ``` | 组件 | 职责 | |-----------|----------------| | **Backend** | 业务逻辑、nonce 管理、tx 组装 —— **无私钥** | | **Lambda 签名器** | 对未签名的 tx 进行哈希处理,协调 KMS 签名,应用 EIP-155 `v` | | **AWS KMS CMK** | 持有 secp256k1 密钥对;仅向 Lambda 暴露 `Sign` / `GetPublicKey` | | **BSC RPC** | 广播已签名的原始交易(建议在测试网上进行演示) | ## 针对 BSC 的更改 上游 AWS 示例针对的是 Ethereum。此 fork 保留了 **KMS 签名模型**并调整了特定于链的交易参数: | 主题 | Ethereum(典型) | BSC(本仓库) | |-------|-------------------|-----------------| | **chainId** | `1` 主网,`5` Goerli(历史),`11155111` Sepolia | `56` 主网,`97` Chapel 测试网 | | **Gas 模型** | L1 上通常为 EIP-1559(`maxFeePerGas`、`maxPriorityFeePerGas`) | 传统 `gas` + `gasPrice`(PoSA 链) | | **Tx 组装** | 用于 EIP-1559 交易(tx)的 Type-2 字段 | 通过 `serializable_unsigned_transaction_from_dict` 进行传统 RLP 编码 | | **地址生成** | KMS 公钥的 Keccak-256 哈希(相同的 secp256k1 曲线) | 完全相同 —— BSC 使用与 Ethereum 相同的账户模型 | | **重放保护** | EIP-155:`v = recovery + chainId * 2 + 35` | 使用 BSC `chainId` 的相同公式 | 在部署时使用 `ETH_NETWORK`(`bsc`、`bsc-testnet`、`chapel`)配置网络,或使用 `CHAIN_ID` 进行覆盖。 ## 威胁模型 | 威胁 | 此设计中的缓解措施 | 残余风险 | |--------|---------------------------|---------------| | 从后端窃取私钥 | 后端从不持有或传输密钥材料 | 被攻破的后端如果能够调用 Lambda,仍然可以**请求任意签名** | | 从 Lambda 提取密钥 | 密钥保留在 KMS HSM 中;Lambda 仅接收签名 | Lambda 代码漏洞或过度的 IAM 权限可能会削弱隔离性 | | 未经授权的签名 | 通过 IAM 限制 `lambda:InvokeFunction` 和 `kms:Sign` | 配置错误的资源策略是常见的故障模式 | | 跨链重放 | EIP-155 将 `chainId` 嵌入签名中 | 错误的 `CHAIN_ID` 环境变量可能会产生有效但非预期链的 tx | | 日志泄露 | Lambda 仅记录事件**键名**;默认 `LOG_LEVEL=WARNING` | 提高日志级别或自定义处理程序可能仍会泄露 payload | | 内部滥用 | KMS CloudTrail + Lambda 调用审计 | 需要此处未包含的运维监控 | ## 安全假设 1. **KMS 是签名密钥的唯一托管方**(`ECC_SECG_P256K1`、`SIGN_VERIFY`)。 2. **只有签名器 Lambda** 对 CMK 拥有 `kms:Sign` 和 `kms:GetPublicKey` 权限。 3. **后端调用方**已经过身份验证(IAM、API Gateway 授权方、VPC 等)—— 本演示中未完全实现。 4. **交易意图**在调用前已进行链下验证(接收者、数值、合约白名单)。 5. 假定使用**传统 Gas 交易**(`gas` + `gasPrice`);EIP-1559 type-2 交易(tx)不在范围内。 6. **BSC PoSA 共识**和 RPC 端点仅受信任用于广播;签名不依赖 RPC。 7. 运维人员接受**演示级别**的错误处理、可观测性和密钥生命周期实践。 ## 部署 (AWS CDK) 前置条件:AWS 账户、CDK CLI、Python 3.9+、Node.js。 ``` python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt cdk bootstrap # once per account/region cdk deploy ``` CDK 预置内容: - KMS CMK (`ECC_SECG_P256K1`) - 带有 `KMS_KEY_ID` 和 `ETH_NETWORK` 环境变量的 Python 3.9 Lambda - IAM 授权:Lambda → `kms:GetPublicKey`、`kms:Sign` 完成后的销毁操作: ``` cdk destroy ``` 对于测试网演示,请使用 Chapel 进行部署: ``` # app.py — 实例化 stack 时传入 eth_network AwsKmsLambdaEthereumStack(app, "aws-kms-lambda-ethereum", eth_network="bsc-testnet") ``` ## 本地演示 / 仅限测试网 **所有本地和 CI 实验请使用 BSC Chapel 测试网(`chainId` 97)。** 1. 部署堆栈(首选 `eth_network="bsc-testnet"`)。 2. 从 Lambda 日志读取 KMS 生成的地址,或者通过使用空运行辅助程序进行调用来读取。 3. 通过 [Chapel 水龙头](https://testnet.bnbchain.org/faucet-smart)为该地址充值。 4. 复制 [`.env.example`](.env.example) → `.env` 并设置 `AWS_LAMBDA_FUNCTION_NAME` 和 `AWS_REGION`。 5. 运行示例客户端: ``` pip install -r requirements-dev.txt python examples/lambda_signer_client.py ``` 6. 使用 `eth_sendRawTransaction` 针对 Chapel RPC(`.env.example` 中的 `BSC_RPC_URL`)广播 `signed_tx.rawTransaction`。 **未经**全面安全审查,请勿将此设置用于主网资产。 ## 后端集成(安全客户端) 后端仅发送**未签名的交易字段**。密钥材料(`privateKey`、助记词、种子、`SKEYS`)绝**不能**出现在 payload 中 —— 签名器会在客户端和 Lambda 上同时拒绝它们。 ### 示例:未签名输入 → 已签名输出 **Lambda 调用 payload(仅限未签名 tx):** ``` { "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "nonce": 3, "gas": 21000, "gasPrice": 10000000000, "value": 0, "data": "0x" } ``` 没有 `privateKey`。调用者也不提供 `chainId` —— 它是在服务端由 `ETH_NETWORK` / `CHAIN_ID` 设置的。 **Lambda 响应(已清理):** ``` { "signed_tx": { "rawTransaction": "0xf86c...a1b2", "hash": "0x9c3e...4f80", "r": 1234567890123456789012345678901234567890123456789012345678901234, "s": 9876543210987654321098765432109876543210987654321098765432109, "v": 230, "from": "0xAb12...cD34", "chainId": 97 } } ``` 十六进制值为了文档说明进行了截断。在 Chapel 测试网上使用 `rawTransaction` 和 `eth_sendRawTransaction`。 ``` import boto3, json, os client = boto3.client("lambda", region_name=os.getenv("AWS_REGION")) payload = { "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0", "nonce": 0, "gas": 21000, "gasPrice": 10_000_000_000, "value": 0, "data": "0x", } response = client.invoke( FunctionName=os.environ["AWS_LAMBDA_FUNCTION_NAME"], InvocationType="RequestResponse", Payload=json.dumps(payload), ) result = json.load(response["Payload"]) signed = result["signed_tx"] ``` 带有更完善防护机制的完整客户端位于 [`examples/lambda_signer_client.py`](examples/lambda_signer_client.py)。 ### Lambda 事件 schema | 字段 | 必需 | 描述 | |-------|----------|-------------| | `to` | 是 | 接收方地址 | | `nonce` | 是 | 账户 nonce | | `gas` | 是 | Gas 限制 | | `gasPrice` | 是 | 传统 Gas 价格 (单位为 wei) | | `data` | 是 | 调用数据(转账时为 `0x`) | | `value` | 否 | 发送的 wei 数量(默认为 `0`) | `chainId` 取自 Lambda 环境(`ETH_NETWORK` / `CHAIN_ID`),而不是来自调用方 —— 防止不受信任的输入造成跨链混淆。 ## 环境变量 请参阅 [`.env.example`](.env.example)。Lambda 端的变量由 CDK 设置: | 变量 | 设置者 | 用途 | |----------|--------|---------| | `KMS_KEY_ID` | CDK | 用于签名的 CMK | | `ETH_NETWORK` | CDK | `bsc`、`bsc-testnet` 或 `chapel` | | `CHAIN_ID` | 可选覆盖 | 显式的 EIP-155 chain id | | `LOG_LEVEL` | CDK / 运维 | 日志详细级别 | ## 测试 运行完整的单元测试套件: ``` python3 -m venv .venv && source .venv/bin/activate pip install -r requirements-dev.txt pytest -v # 或:pytest tests/test_signing_core.py -v ``` `tests/test_signing_core.py` 中的覆盖范围: - EIP-2 低 `s` 标准化 - EIP-155 `v` 值计算(BSC 主网 / Chapel 测试网) - Chain id 解析(`bsc`、`bsc-testnet`、`chapel`、`CHAIN_ID` 覆盖) - Tx 参数验证(必填字段,无秘密字段) - 拒绝传入事件中的 `privateKey` / `SKEYS` Lambda 集成测试需要 Python 3.9 以及部署时捆绑的 `eth_client` 依赖项。 ## 项目布局 ``` app.py # CDK entrypoint aws_kms_lambda_ethereum/ aws_kms_lambda_ethereum_stack.py _lambda/functions/eth_client/ lambda_function.py # handler lambda_helper.py # KMS signing orchestration signing_core.py # Pure helpers (chain id, EIP-155, tx params) examples/ lambda_signer_client.py # backend client (no private keys) tests/ test_signing_core.py ``` ## 延伸阅读 - [AWS 博客 —— KMS Ethereum 账户(第 1 部分)](https://aws.amazon.com/blogs/database/part1-use-aws-kms-to-securely-manage-ethereum-accounts/) - [AWS 博客 —— KMS Ethereum 账户(第 2 部分)](https://aws.amazon.com/blogs/database/part2-use-aws-kms-to-securely-manage-ethereum-accounts/) - [BSC Chapel 测试网](https://testnet.bnbchain.org/) ## 许可证 MIT-0 —— 请参阅 [LICENSE](LICENSE)。
标签:AWS KMS, BSC, Web3, 交易签名, 区块链, 后端开发, 逆向工具