
本文介绍如何使用 AES 加密算法对 shell 命令进行加密存储,再在运行时解密并安全执行,涵盖文件读写、AES 加解密(CBC 模式)、os/exec 命令构造与执行等核心实践。
本文介绍如何使用 aes 加密算法对 shell 命令进行加密存储,再在运行时解密并安全执行,涵盖文件读写、aes 加解密(cbc 模式)、`os/exec` 命令构造与执行等核心实践。
在 Go 中实现“解密后执行命令”需严格遵循安全原则:绝不直接执行未经校验的动态字符串,且必须使用强加密(如 AES-CBC 或 AES-GCM)配合密钥管理。以下是一个完整、可运行的端到端示例,包含命令加密、文件持久化、安全解密与带错误处理的执行流程。
? 步骤 1:AES-CBC 加密与文件写入(加密端)
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"io"
"os"
)
func encryptCommand(cmd string, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// PKCS#7 填充(Go 标准库不内置,需手动实现)
padded := pkcs7Pad([]byte(cmd), aes.BlockSize)
ciphertext := make([]byte, aes.BlockSize+len(padded))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCBCEncrypter(block, iv)
stream.CryptBlocks(ciphertext[aes.BlockSize:], padded)
return ciphertext, nil
}
func pkcs7Pad(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padtext := make([]byte, padding)
for i := range padtext {
padtext[i] = byte(padding)
}
return append(data, padtext...)
}
func main() {
command := "echo 'Hello from decrypted command!'"
key := []byte("32-byte-long-secret-key-123456") // ⚠️ 生产环境请使用 crypto/rand 生成并安全存储
encrypted, err := encryptCommand(command, key)
if err != nil {
panic(err)
}
// 写入加密数据到文件(十六进制编码便于查看,生产中建议二进制直写)
if err := os.WriteFile("cmd.enc", []byte(hex.EncodeToString(encrypted)), 0600); err != nil {
panic(err)
}
}
✅ 注意:密钥长度必须为 16(AES-128)、24(AES-192)或 32(AES-256)字节;文件权限设为
0600防止未授权读取。
? 步骤 2:解密并执行命令(执行端)
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"io"
"os/exec"
"strings"
)
func decryptCommand(data []byte, key []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
if len(data) len(data) || padding == 0 {
return nil, fmt.Errorf("invalid padding")
}
return data[:len(data)-padding], nil
}
func main() {
// 读取加密文件(十六进制格式)
encData, err := os.ReadFile("cmd.enc")
if err != nil {
panic(fmt.Sprintf("failed to read encrypted file: %v", err))
}
// 解码 hex → []byte
ciphertext, err := hex.DecodeString(strings.TrimSpace(string(encData)))
if err != nil {
panic(fmt.Sprintf("hex decode failed: %v", err))
}
key := []byte("32-byte-long-secret-key-123456") // 必须与加密端完全一致
cmdStr, err := decryptCommand(ciphertext, key)
if err != nil {
panic(fmt.Sprintf("decryption failed: %v", err))
}
fmt.Printf("Decrypted command: %q\n", cmdStr)
// 安全执行:拆分命令与参数(避免 shell 注入)
parts := strings.Fields(cmdStr)
if len(parts) == 0 {
panic("empty command after decryption")
}
cmd := exec.Command(parts[0], parts[1:]...)
var outBuf, errBuf bytes.Buffer
cmd.Stdout = &outBuf
cmd.Stderr = &errBuf
if err := cmd.Run(); err != nil {
panic(fmt.Sprintf("command execution failed: %v, stderr: %s", err, errBuf.String()))
}
fmt.Printf("Output: %s", outBuf.String())
}
⚠️ 关键安全提醒
-
禁止使用
sh -c执行解密字符串:这会引入严重 shell 注入风险。务必用exec.Command(name, args...)显式解析命令名与参数。 -
密钥不可硬编码:生产环境应通过环境变量、KMS 或专用密钥管理服务注入,并设置
runtime.LockOSThread()防止内存泄露。 -
验证完整性:AES-CBC 不提供认证,建议升级至
crypto/aes+crypto/cipher.NewGCM实现 AEAD(加密+认证),防止密文篡改。 - 最小权限原则:执行命令的进程应以最低必要权限运行(如非 root 用户、受限 seccomp profile)。
通过以上结构化实现,你可在 Go 中构建一条从加密存储 → 安全解密 → 可控执行的完整可信链路。记住:自动化命令执行本质是高危操作,每一步都需纵深防御。










