阿里云 ECS 云助手敏感数据加密传输实践
核心痛点与场景分析
在运维自动化场景中,通过 RunCommand API 向 ECS 实例下发指令是常见操作。然而,该过程会在多个链路留下痕迹,包括 DescribeInvocation、ActionTrail 审计日志以及云服务网关和应用层日志。这意味着,拥有相应权限的 RAM 用户、实例内部用户甚至云平台运维人员,理论上均可追溯命令明文。
当命令中包含高敏感信息(如重置 Root 密码、注入 API Key)时,这种全链路可见性构成了安全风险。例如,直接执行 echo "root:SecretPass" | chpasswd 会导致 SecretPass 暴露在日志中。为解决此问题,可利用云助手 Agent 内置的临时密钥对机制,实现"密文传输、本地解密"的安全闭环。
实施步骤详解
1. 生成临时非对称密钥对
首先,需要在目标 ECS 实例上触发云助手 Agent 生成一对临时的 RSA 密钥。私钥仅驻留在实例内存或受保护的进程空间中,公钥则用于后续的数据加密。
执行以下命令获取公钥及密钥 ID:
aliyun-service data-encryption -g --json
参数说明:
-g: Generate,生成新密钥对。--json: 以 JSON 格式输出结果,便于脚本解析。- 可选参数
-i <id>自定义密钥标识,-t <seconds>设置过期时间(默认 60 秒)。
返回示例:
{
"id": "tmp-key-8f9a2b",
"createdTimestamp": 1675309078,
"expiredTimestamp": 1675309138,
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...[省略]...\n-----END PUBLIC KEY-----\n"
}
记录返回的 id 和 publicKey,这是后续流程的关键凭证。
2. 客户端加密敏感数据
在发送命令前,使用上一步获取的公钥对敏感字符串进行 RSA-OAEP (SHA-256) 加密,并将密文转换为 Base64 编码。以下是不同语言环境的实现逻辑重构:
Python 实现方案
利用 cryptography 库或 PyCrypto 模块完成加密。注意处理 PEM 格式的换行符兼容性问题。
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA256
def encrypt_sensitive_data(pub_key_pem: str, plain_text: str) -> str:
"""
使用 RSA OAEP-SHA256 对文本进行加密并返回 Base64 字符串
"""
# 导入公钥
key = RSA.import_key(pub_key_pem)
# 初始化加密器
cipher = PKCS1_OAEP.new(key, hashAlgo=SHA256)
# 执行加密并编码
encrypted_bytes = cipher.encrypt(plain_text.encode('utf-8'))
return base64.b64encode(encrypted_bytes).decode('utf-8')
# 调用示例
public_key_content = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----"""
secret_value = "MySuperSecretPassword123!"
ciphertext = encrypt_sensitive_data(public_key_content, secret_value)
print(f"Encrypted Payload: {ciphertext}")
Node.js / TypeScript 实现方案
使用原生 crypto 模块。需确保 Node.js 版本支持 createPublicKey。
import * as crypto from 'crypto';
async function rsaEncryptWithOAEP(pemKey: string, data: string): Promise<string> {
// 标准化 PEM 头尾,防止格式错误
const normalizedPem = pemKey.replace(/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----/g, '').trim();
const fullPem = `-----BEGIN PUBLIC KEY-----\n${normalizedPem}\n-----END PUBLIC KEY-----`;
const publicKey = crypto.createPublicKey({
key: fullPem,
format: 'pem',
type: 'spki'
});
const bufferData = Buffer.from(data, 'utf8');
const encryptedBuffer = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256'
}, bufferData);
return encryptedBuffer.toString('base64');
}
// 调用示例
const pubKey = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...\n-----END PUBLIC KEY-----";
rsaEncryptWithOAEP(pubKey, "SecureTokenXYZ").then(res => console.log(res));
浏览器端 JavaScript 实现方案
适用于 Web 控制台集成场景,使用 Web Crypto API。
function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
async function webEncryptSensitive(publicKeyPem, plaintext) {
// 清理 PEM 格式中的换行和头部尾部,转为纯 Base64 DER
const cleanPem = publicKeyPem
.replace(/-----BEGIN PUBLIC KEY-----/, '')
.replace(/-----END PUBLIC KEY-----/, '')
.replace(/\s+/g, '');
const binaryDerString = atob(cleanPem);
const binaryDer = Uint8Array.from(binaryDerString, c => c.charCodeAt(0));
const cryptoSubtle = window.crypto.subtle;
// 导入 SPKI 格式的公钥
const keyObject = await cryptoSubtle.importKey(
'spki',
binaryDer.buffer,
{ name: 'RSA-OAEP', hash: 'SHA-256' },
true,
['encrypt']
);
// 执行加密
const encodedText = new TextEncoder().encode(plaintext);
const encryptedArrayBuffer = await cryptoSubtle.encrypt(
{ name: 'RSA-OAEP' },
keyObject,
encodedText
);
return arrayBufferToBase64(encryptedArrayBuffer);
}
3. 构建解密与执行的 Shell 脚本
将加密后的密文嵌入到最终的 RunCommand 脚本中。脚本逻辑分为三步:调用 Agent 解密、立即销毁临时密钥、执行原始业务命令。
假设变量定义如下:
KP_ID: 第一步生成的密钥 ID (t-hy03a65fmrd1zpc)ENC_VAL: 第二步生成的 Base64 密文
最终下发的命令内容模板:
#!/bin/bash
# 配置变量
KEY_PAIR_ID="t-hy03a65fmrd1zpc"
ENCRYPTED_DATA="{{BASE64_ENCRYPTED_STRING}}"
# 1. 请求云助手 Agent 使用私钥解密
# 注意:此操作仅在实例本地内存中进行,不会上传明文到网络
DECRYPTED_SECRET=$(aliyun-service data-encryption -d -i "$KEY_PAIR_ID" -T "$ENCRYPTED_DATA")
if [ $? -ne 0 ] || [ -z "$DECRYPTED_SECRET" ]; then
echo "Error: Decryption failed." >&2
exit 1
fi
# 2. 安全加固:立即删除临时密钥对,防止重放攻击或后续泄露
aliyun-service data-encryption --remove-keypair -i "$KEY_PAIR_ID"
# 3. 执行实际业务逻辑(例如重置密码)
echo "root:${DECRYPTED_SECRET}" | chpasswd
# 可选:清除环境变量中的敏感信息
unset DECRYPTED_SECRET
unset ENCRYPTED_DATA
安全性评估与风险管控
该架构通过非对称加密实现了数据在传输链路中的机密性保护。
防护能力:
- API 层隔离:外部监控者(包括管理员和其他 RAM 用户)在
ActionTrail或DescribeInvocation中只能看到 Base64 密文,无法还原明文。 - 日志脱敏:由于解密发生在实例内部进程间通信(IPC),云平台的管控日志和网关日志均不包含明文密码。
剩余风险与缓解措施:
- 实例内窥视:若攻击者已登录该 ECS 实例,且在密钥过期前截获了正在执行的 Shell 进程,可能通过读取
/proc/<pid>/environ或调试内存获取明文。
缓解策略:严格限制key_pair_timeout为极短值(如 10-30 秒),并确保脚本执行完毕后立即调用--remove-keypair。 - 侧信道攻击:虽然概率极低,但理论上存在通过时序分析破解 RSA 的可能性。
缓解策略:使用标准的 RSA-OAEP 填充模式而非简单的 PKCS#1 v1.5,并在生产环境中结合 IAM 最小权限原则,限制能查看实例内部日志的用户范围。