mcpField
S
security-first-2025

security-first-2025

github.com/josiahsiegel安全59163 次安装19 周前更新
代码仓库

关于 security-first-2025

security-first-2025 是面向 Bash 脚本开发者的安全技能,涵盖输入校验、命令安全、文件操作、机密信息与权限管理,并提供检查清单和 ShellCheck 扫描建议。

主要功能

  1. 01输入校验模式
  2. 02命令注入防护
  3. 03路径与临时文件安全
  4. 04机密信息与权限指南
  5. 05ShellCheck 与安全静态检查

适用场景

  1. 01部署前审查 Bash 脚本
  2. 02处理不可信路径和输入
  3. 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