
本文介绍如何使用 AES 加密算法对 shell 命令进行加密存储,再于运行时解密并安全执行,涵盖文件读写、AES 加解密(CBC 模式)、os/exec 调用等核心实践。
本文介绍如何使用 aes 加密算法对 shell 命令进行加密存储,再于运行时解密并安全执行,涵盖文件读写、aes 加解密(cbc 模式)、`os/exec` 调用等核心实践。
在 Go 中实现“解密后执行命令”需严格区分安全性与功能性:加密仅保护命令的静态存储,而执行阶段则涉及系统调用风险。以下是一个完整、可运行的端到端示例,采用标准库 crypto/aes + crypto/cipher(CBC 模式)加密命令字符串,并通过 os/exec 安全执行。
? 1. 加密命令并写入文件(encrypt.go)
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"io"
"os"
)
func encryptCommand(command string, key []byte, filename string) error {
block, err := aes.NewCipher(key)
if err != nil {
return err
}
// 使用随机 IV(必须与密文一同保存)
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return err
}
stream := cipher.NewCBCEncrypter(block, iv)
padding := aes.BlockSize - len(command)%aes.BlockSize
padded := append([]byte(command), make([]byte, padding)...)
ciphertext := make([]byte, len(padded))
stream.CryptBlocks(ciphertext, padded)
// 将 IV + 密文写入文件(IV 明文存储,但必需)
data := append(iv, ciphertext...)
return os.WriteFile(filename, data, 0600) // 仅当前用户可读写
}
func main() {
key := []byte("32-byte-long-secret-key-for-aes") // 必须恰好 32 字节(AES-256)
cmd := "echo 'Hello from decrypted command!' && date"
if err := encryptCommand(cmd, key, "cmd.enc"); err != nil {
panic(err)
}
println("✅ Command encrypted and saved to cmd.enc")
}✅ 注意:密钥必须为 16(AES-128)、24(AES-192)或 32(AES-256)字节;生产环境应使用
golang.org/x/crypto/scrypt等派生密钥,而非硬编码。
▶️ 2. 解密并执行命令(decrypt_and_run.go)
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
"os/exec"
"os"
"strings"
)
func decryptAndRun(filename string, key []byte) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to read encrypted file: %w", err)
}
if len(data) < aes.BlockSize {
return fmt.Errorf("invalid encrypted file: too short")
}
iv := data[:aes.BlockSize]
ciphertext := data[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
return err
}
stream := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
stream.CryptBlocks(plaintext, ciphertext)
// 去除 PKCS#7 填充(此处简化:按末字节值截断)
padding := int(plaintext[len(plaintext)-1])
if padding < 1 || padding > aes.BlockSize || len(plaintext) < padding {
return fmt.Errorf("invalid PKCS#7 padding")
}
command := strings.TrimSpace(string(plaintext[:len(plaintext)-padding]))
// ⚠️ 安全关键:避免直接 exec.Command("sh", "-c", command) —— 防止命令注入
// 推荐:解析为明确的命令+参数(如 echo 和参数分离)
args := strings.Fields(command)
if len(args) == 0 {
return fmt.Errorf("empty command after decryption")
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
fmt.Printf("? Executing: %s\n", strings.Join(args, " "))
return cmd.Run()
}
func main() {
key := []byte("32-byte-long-secret-key-for-aes")
if err := decryptAndRun("cmd.enc", key); err != nil {
fmt.Fprintf(os.Stderr, "❌ Execution failed: %v\n", err)
}
}⚠️ 重要安全提醒
-
绝不信任解密内容:解密后的字符串可能含恶意命令(如
; rm -rf /)。生产中应白名单校验命令前缀(如仅允许echo,date,ls),或使用结构化配置(JSON/YAML)替代裸字符串。 - 密钥管理:硬编码密钥仅适用于演示;真实场景应使用环境变量、KMS 或专用密钥服务。
-
文件权限:加密文件(
cmd.enc)和密钥均需设为0600权限,防止未授权读取。 - 错误处理:示例已包含基础校验(IV 长度、填充合法性),但生产环境建议增加完整性校验(如 HMAC-SHA256)。
通过以上两步,你即可在 Go 中构建一个“加密存储 → 运行时解密 → 安全执行”的闭环流程。记住:加密不等于授权——执行前的内容验证,永远比解密本身更重要。

















