关于 security-first-2025
security-first-2025 是面向 Bash 脚本开发者的安全技能,涵盖输入校验、命令安全、文件操作、机密信息与权限管理,并提供检查清单和 ShellCheck 扫描建议。
主要功能
- 01输入校验模式
- 02命令注入防护
- 03路径与临时文件安全
- 04机密信息与权限指南
- 05ShellCheck 与安全静态检查
适用场景
- 01部署前审查 Bash 脚本
- 02处理不可信路径和输入
- 03构建 Shell 脚本安全检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill security-first-2025安装后在 Claude Code 中输入/security-first-2025确认已加载。
详细介绍
🚨 关键准则
Windows 文件路径要求
强制要求:在 Windows 上始终使用反斜杠表示文件路径
在 Windows 上使用 Edit 或 Write 工具时,文件路径 MUST 使用反斜杠(\),而不是正斜杠(/)。
示例:
- ❌ 错误:
D:/repos/project/file.tsx - ✅ 正确:
D:\repos\project\file.tsx
适用范围:
- Edit 工具的 file_path 参数
- Write 工具的 file_path 参数
- Windows 系统上的所有文件操作
文档准则
除非用户明确要求,否则绝不创建新的文档文件。
- 优先级:更新现有的 README.md 文件,而不是创建新文档
- 保持仓库整洁:保持仓库根目录整洁——除非用户另有要求,否则只保留 README.md
- 风格:文档应简洁、直接、专业——避免 AI 生成的语气
- 用户偏好:只有在用户明确要求文档时,才创建其他 .md 文件
安全优先的 Bash 脚本编写(2025)
概述
2025 年的安全评估显示,60% 以上遭利用的自动化工具缺乏充分的输入清理措施。本技能提供了强制性的安全模式。
关键安全模式
1. 输入验证(不可妥协)
每项输入在使用前 MUST 经过验证:
#!/usr/bin/env bash
set -euo pipefail
# ✅ REQUIRED: Validate all inputs
validate_input() {
local input="$1"
local pattern="$2"
local max_length="${3:-255}"
# Check empty
if [[ -z "$input" ]]; then
echo "Error: Input required" >&2
return 1
fi
# Check pattern
if [[ ! "$input" =~ $pattern ]]; then
echo "Error: Invalid format" >&2
return 1
fi
# Check length
if [[ ${#input} -gt $max_length ]]; then
echo "Error: Input too long (max $max_length)" >&2
return 1
fi
return 0
}
# Usage
read -r user_input
if validate_input "$user_input" '^[a-zA-Z0-9_-]+$' 50; then
process "$user_input"
else
exit 1
fi
2. 防止命令注入
绝不对用户输入使用 eval 或动态执行:
# ❌ DANGEROUS - Command injection vulnerability
user_input="$(cat user_file.txt)"
eval "$user_input" # NEVER DO THIS
# ❌ DANGEROUS - Indirect command injection
grep "$user_pattern" file.txt # If pattern is "-e /etc/passwd"
# ✅ SAFE - Use -- separator
grep -- "$user_pattern" file.txt
# ✅ SAFE - Use arrays
grep_args=("$user_pattern" "file.txt")
grep "${grep_args[@]}"
# ✅ SAFE - Validate before use
if [[ "$user_pattern" =~ ^[a-zA-Z0-9]+$ ]]; then
grep "$user_pattern" file.txt
fi
3. 防止路径遍历
清理并验证所有文件路径:
#!/usr/bin/env bash
set -euo pipefail
# Sanitize path components
sanitize_path() {
local path="$1"
# Remove dangerous patterns
path="${path//..\/}" # Remove ../
path="${path//\/..\//}" # Remove /../
path="${path#/}" # Remove leading /
echo "$path"
}
# Validate path is within allowed directory
is_safe_path() {
local file_path="$1"
local base_dir="$2"
# Resolve to absolute paths
local real_path real_base
real_path=$(readlink -f "$file_path" 2>/dev/null) || return 1
real_base=$(readlink -f "$base_dir" 2>/dev/null) || return 1
# Check path starts with base
[[ "$real_path" == "$real_base"/* ]]
}
# Usage
user_file=$(sanitize_path "$user_input")
if is_safe_path "/var/app/uploads/$user_file" "/var/app/uploads"; then
cat "/var/app/uploads/$user_file"
else
echo "Error: Access denied" >&2
exit 1
fi
4. 安全的临时文件
切勿使用可预测的临时文件名:
# ❌ DANGEROUS - Race condition vulnerability
temp_file="/tmp/myapp.tmp"
echo "data" > "$temp_file" # Can be symlinked by attacker
# ❌ DANGEROUS - Predictable name
temp_file="/tmp/myapp-$$.tmp" # PID can be guessed
# ✅ SAFE - Use mktemp
temp_file=$(mktemp)
chmod 600 "$temp_file" # Owner-only permissions
echo "data" > "$temp_file"
# ✅ SAFE - Automatic cleanup
readonly TEMP_FILE=$(mktemp)
trap 'rm -f "$TEMP_FILE"' EXIT INT TERM
# ✅ SAFE - Temp directory
readonly TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT INT TERM
chmod 700 "$TEMP_DIR"
5. 密钥管理
绝不硬编码密钥或将其暴露:
# ❌ DANGEROUS - Hardcoded secrets
DB_PASSWORD="supersecret123"
# ❌ DANGEROUS - Secrets in environment (visible in ps)
export DB_PASSWORD="supersecret123"
# ✅ SAFE - Read from secure file
if [[ -f /run/secrets/db_password ]]; then
DB_PASSWORD=$(< /run/secrets/db_password)
chmod 600 /run/secrets/db_password
else
echo "Error: Secret not found" >&2
exit 1
fi
# ✅ SAFE - Use cloud secret managers
get_secret() {
local secret_name="$1"
# AWS Secrets Manager
aws secretsmanager get-secret-value \
--secret-id "$secret_name" \
--query SecretString \
--output text
}
DB_PASSWORD=$(get_secret "production/database/password")
# ✅ SAFE - Prompt for sensitive data (no echo)
read -rsp "Enter password: " password
echo # Newline after password
6. 权限管理
遵循最小权限原则:
#!/usr/bin/env bash
set -euo pipefail
# Check not running as root
if [[ $EUID -eq 0 ]]; then
echo "Error: Do not run as root" >&2
exit 1
fi
# Drop privileges if started as root
drop_privileges() {
local target_user="$1"
if [[ $EUID -eq 0 ]]; then
echo "Dropping privileges to $target_user" >&2
exec sudo -u "$target_user" "$0" "$@"
fi
}
# Run specific command with minimal privileges
run_privileged() {
local command="$1"
shift
# Use sudo with minimal scope
sudo --non-interactive \
--reset-timestamp \
"$command" "$@"
}
# Usage
drop_privileges "appuser"
7. 环境变量清理
执行前清理环境:
#!/usr/bin/env bash
set -euo pipefail
# Clean environment
clean_environment() {
# Unset dangerous variables
unset IFS
unset CDPATH
unset GLOBIGNORE
# Set safe PATH (absolute paths only)
export PATH="/usr/local/bin:/usr/bin:/bin"
# Set safe IFS
IFS=$'\n\t'
}
# Execute command in clean environment
exec_clean() {
env -i \
HOME="$HOME" \
USER="$USER" \
PATH="/usr/local/bin:/usr/bin:/bin" \
"$@"
}
# Usage
clean_environment
exec_clean /usr/local/bin/myapp
8. 使用绝对路径(2025 年最佳实践)
始终使用绝对路径,以防止 PATH 劫持:
#!/usr/bin/env bash
set -euo pipefail
# ❌ DANGEROUS - Vulnerable to PATH manipulation
curl https://example.com/data
jq '.items[]' data.json
# ✅ SAFE - Absolute paths
/usr/bin/curl https://example.com/data
/usr/bin/jq '.items[]' data.json
# ✅ SAFE - Verify command location
CURL=$(command -v curl) || { echo "curl not found" >&2; exit 1; }
"$CURL" https://example.com/data
重要原因:
- 防止执行用户 PATH 中的恶意二进制文件
- 企业环境中的标准做法
- 安全敏感脚本的必要要求
9. 历史记录文件保护(2025 年安全实践)
执行凭据操作时禁用历史记录:
#!/usr/bin/env bash
set -euo pipefail
# Disable history for this session
HISTFILE=/dev/null
export HISTFILE
# Or disable specific commands
HISTIGNORE="*password*:*secret*:*token*"
export HISTIGNORE
# Handle sensitive operations
read -rsp "Enter database password: " db_password
echo
# Use password (not logged to history)
/usr/bin/mysql -p"$db_password" -e "SELECT 1"
# Clear variable
unset db_password
安全检查清单(2025)
每个脚本 MUST 通过以下检查:
输入验证
- 使用正则表达式模式验证所有用户输入
- 限制所有输入的最大长度
- 拒绝空或 null 输入
- 转义或拒绝特殊字符
命令安全
- 不对用户输入使用 eval
- 不根据用户输入动态生成变量名
- 所有命令参数都使用 -- 分隔符
- 使用数组,而不是字符串拼接
文件操作
- 验证所有路径,防止目录遍历
- 使用 mktemp 创建临时文件
- 严格设置文件权限(600/700)
- 注册清理处理程序(trap EXIT)
密钥
- 不硬编码密码/密钥/令牌
- 从安全存储中读取密钥
- 绝不记录或打印密钥
- 使用完毕后从内存中清除密钥
权限
- 以所需的最低权限运行
- 除非确有必要,否则拒绝以 root 身份执行
- 在需要时降低权限
- 尽量缩小 sudo 的使用范围
错误处理
- 启用 set -euo pipefail
- 将所有错误记录到 stderr
- 错误信息中不暴露敏感数据
- 退出代码具有明确含义
自动化安全扫描
ShellCheck 集成
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: ShellCheck
run: |
# Fail on security issues
find . -name "*.sh" -exec shellcheck \
--severity=error \
--enable=all \
{} +
自定义安全检查
#!/usr/bin/env bash
# security-lint.sh - Check scripts for security issues
set -euo pipefail
lint_script() {
local script="$1"
local issues=0
echo "Checking: $script"
# Check for eval
if grep -n "eval" "$script"; then
echo " ❌ Found eval (command injection risk)"
((issues++))
fi
# Check for hardcoded secrets
if grep -nE "(password|secret|token|key)\s*=\s*['\"][^'\"]+['\"]" "$script"; then
echo " ❌ Found hardcoded secrets"
((issues++))
fi
# Check for predictable temp files
if grep -n "/tmp/[a-zA-Z0-9_-]*\\.tmp" "$script"; then
echo " ❌ Found predictable temp file"
((issues++))
fi
# Check for unquoted variables
if grep -nE '\$[A-Z_]+[^"]' "$script"; then
echo " ⚠️ Found unquoted variables"
((issues++))
fi
if ((issues == 0)); then
echo " ✓ No security issues found"
fi
return "$issues"
}
# Scan all scripts
total_issues=0
while IFS= read -r -d '' script; do
lint_script "$script" || ((total_issues++))
done < <(find . -name "*.sh" -type f -print0)
if ((total_issues > 0)); then
echo "❌ Found security issues in $total_issues scripts"
exit 1
else
echo "✓ All scripts passed security checks"
fi
实用的安全脚本模板
#!/usr/bin/env bash
#
# Secure Script Template (2025)
#
set -euo pipefail
IFS=$'\n\t'
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
# Security: Reject root execution
if [[ $EUID -eq 0 ]]; then
echo "Error: Do not run as root" >&2
exit 1
fi
# Security: Clean environment
export PATH="/usr/local/bin:/usr/bin:/bin"
unset CDPATH GLOBIGNORE
# Security: Secure temp file
readonly TEMP_FILE=$(mktemp)
trap 'rm -f "$TEMP_FILE"; exit' EXIT INT TERM
chmod 600 "$TEMP_FILE"
# Validate input
validate_input() {
local input="$1"
if [[ -z "$input" ]]; then
echo "Error: Input required" >&2
return 1
fi
if [[ ! "$input" =~ ^[a-zA-Z0-9_/-]+$ ]]; then
echo "Error: Invalid characters in input" >&2
return 1
fi
if [[ ${#input} -gt 255 ]]; then
echo "Error: Input too long" >&2
return 1
fi
return 0
}
# Sanitize file path
sanitize_path() {
local path="$1"
path="${path//..\/}"
path="${path#/}"
echo "$path"
}
# Main function
main() {
local user_input="${1:-}"
# Validate
if ! validate_input "$user_input"; then
exit 1
fi
# Sanitize
local safe_path
safe_path=$(sanitize_path "$user_input")
# Process safely
echo "Processing: $safe_path"
# ... your logic here ...
}
main "$@"
合规标准(2025)
CIS 基准
- 使用 ShellCheck 进行自动化合规检查
- 对所有用户数据实施输入验证
- 安全处理临时文件
- 以最小权限执行
NIST 指南
- 强大的输入验证(NIST SP 800-53)
- 安全编码实践
- 日志记录与监控
- 强制执行访问控制
OWASP Top 10
- A03:注入 - 防止命令注入
- A01:访问控制失效 - 路径验证
- A02:加密失效 - 安全管理机密
资源
在 2025 年,安全优先开发是不可妥协的原则。每个脚本都必须通过所有安全检查后才能部署。
评论
支持 Markdown