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とセキュリティLint

活用シーン

  1. 01デプロイ前のBashスクリプトレビュー
  2. 02信頼できないパスや入力の処理
  3. 03シェルスクリプトの安全性チェック構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill security-first-2025

インストール後、Claude Code で/security-first-2025を入力して読み込まれたか確認します。

詳細

🚨 重要なガイドライン

Windows のファイルパス要件

必須:Windows ではファイルパスに必ずバックスラッシュを使用してください

Windows で Edit または Write ツールを使用する場合、ファイルパスにはスラッシュ(/)ではなく、必ずバックスラッシュ(\)を使用してください。

例:

  • ❌ 誤り: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. 入力検証(必須)

すべての入力は使用前に必ず検証してください。

#!/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年)

すべてのスクリプトは、次のチェックに合格する必要があります。

入力検証
  • すべてのユーザー入力が正規表現パターンで検証されている
  • すべての入力に最大長が設定されている
  • 空または 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年において、セキュリティを最優先する開発は譲れません。すべてのスクリプトは、デプロイ前にすべてのセキュリティチェックに合格する必要があります。

コメント