0Pricing
DevOps Bootcamp · レッスン

シークレットの安全な取り扱いと環境の衛生管理

標準入力、ファイル、不要な情報を除去した環境を使い、認証情報がプロセス一覧やログに残らないようにします。

「シークレットの安全な取り扱いと環境の衛生管理」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。

シークレットの衛生管理が重要な理由

シークレット(API キー、パスワード、トークン)は、あらゆるシステムで最も機密性の高いデータです。Bash スクリプトでこれらを誤って扱うことは、最も一般的で被害の大きいセキュリティ上のミスの 1 つです。

  • プロセス一覧: コマンドに渡した引数は ps aux、/proc/<pid>/cmdline、システム監査ログに表示され、ホスト上のすべてのユーザーから見える状態になります。
  • シェル履歴: 対話的に入力したコマンド(場合によってはスクリプトも)は ~/.bash_history に記録されます。
  • ログファイル: set -x のトレース、アプリケーションログ、CI/CD の出力に変数の値が記録されることがあります。
  • 環境変数の漏えい: 子プロセスは親プロセスの環境全体を継承するため、エクスポートされたシークレットも引き継がれます。

堅牢化されたスクリプトでは、シークレットを放射性物質のように扱います。露出時間を最小限にし、露出範囲を制限し、外部へ出す際にはすべてサニタイズします。

プロセス一覧における攻撃対象領域

シークレットをコマンドライン引数として渡すと、システム上のすべてのユーザーが ps 経由ですぐに読み取れます。これは理論上の問題ではなく、共有ホスティングやコンテナ環境で日常的に悪用されています。

以下のスニペットでは、問題と対策を並べて示します。

#!/usr/bin/env bash
# DANGEROUS: password visible in 'ps aux' output
# curl -u "admin:SuperSecret123" https://api.example.com/data

# SAFE: pass credentials via stdin or a flag that reads from a file
# Many tools support reading secrets from stdin with '-' or dedicated flags:

# Option 1 — pipe the secret so it never appears in argv
echo 'SuperSecret123' | curl -u 'admin' --password-stdin \
  https://api.example.com/data 2>/dev/null || true

# Option 2 — write a temporary netrc and point curl at it
# (covered in a later scene)
echo 'Secret never touches the command line this way'

標準入力からシークレットを読み取る

対話的にシークレットを扱う最も安全な方法は、read -rs を使って実行時に読み取ることです。-s フラグはエコーを抑制するため文字が表示されず、-r はバックスラッシュの解釈を防ぎます。

重要なポイント:

  • 変数はエクスポートされないため、子プロセスが /proc/<pid>/environ 経由で参照することはできません。
  • 使用後は、露出する時間を短くするために変数をすぐにunsetしてください。
  • echo "$SECRET" は避け、printf '%s' を使用してください。末尾の改行による値の破損を防ぎ、トレースにも表示されないようにできます。
#!/usr/bin/env bash
set -euo pipefail

# Prompt on stderr so stdout stays clean for piping
read -rsp 'Enter API token: ' API_TOKEN <&2
printf '\n' >&2

# Use the secret — printf keeps it out of argv
response=$(printf '%s' "$API_TOKEN" | curl -sS -X POST \
  -H 'Content-Type: application/json' \
  --data-binary @- \
  https://httpbin.org/post 2>/dev/null) || true

echo "Request sent."

# Scrub immediately — unset removes it from shell memory
unset API_TOKEN

ファイル内の秘密情報:権限と所有者

秘密情報をディスク上に保存しておく必要がある場合(サービスアカウントのキーなど)、ファイルの権限が第一の防御手段になります。

  • モード 0600 — 所有者だけが読み書きできます。グループやその他のユーザーはアクセスできません。
  • モード 0400 — 所有者だけが読み取りできます。誤って上書きすることが決してないキーには、こちらを推奨します。
  • ~/.secrets/ や /run/secrets/ など、専用ディレクトリの下に秘密情報ファイルを保存します(後者は多くの Linux システムで RAM ベースの tmpfs であり、再起動するまでしか存続しません)。
  • 強固な .gitignore がない限り、git で追跡されるディレクトリ内に秘密情報ファイルを置かないでください。
#!/usr/bin/env bash
set -euo pipefail

SECRETS_DIR="${HOME}/.secrets"
mkdir -p "$SECRETS_DIR"
chmod 700 "$SECRETS_DIR"   # directory: only owner can list contents

KEY_FILE="${SECRETS_DIR}/api_token"

# Write secret — atomically restrict permissions before writing content
install -m 0600 /dev/null "$KEY_FILE"
printf '%s' 'my-super-secret-token' > "$KEY_FILE"

echo "Permissions:"
ls -la "$KEY_FILE"

# Read back safely — no subshell, no echo
API_TOKEN=$(< "$KEY_FILE")
echo "Token length: ${#API_TOKEN} chars (value not printed)"
unset API_TOKEN

curl で .netrc ファイルを使用する

curl は、ホスト名と認証情報を対応付ける ~/.netrc ファイル(または --netrc-file で指定する任意のパス)をサポートしています。これにより、認証データをコマンドラインやスクリプト本文から完全に排除できます。

ファイル形式は単純です。

machine api.example.com
  login admin
  password s3cr3t

ベストプラクティス:

  • 必ず chmod 0600 ~/.netrc を設定してください。一部のシステムでは、curl は他のユーザーから読み取り可能なファイルを拒否します。
  • --netrc-file /run/secrets/netrc を使用して、tmpfs ベースまたはコンテナから注入された秘密情報を指定します。
  • 一時的な netrc ファイルは、EXIT 時の trap で削除してください。
#!/usr/bin/env bash
set -euo pipefail

TMP_NETRC=$(mktemp)
chmod 0600 "$TMP_NETRC"

# Trap ensures cleanup even on error or signal
trap 'rm -f "$TMP_NETRC"' EXIT

# Write credentials to the temp netrc
cat > "$TMP_NETRC" <<'EOF'
machine httpbin.org
  login myuser
  password mypassword
EOF

curl -fsS --netrc-file "$TMP_NETRC" \
  https://httpbin.org/basic-auth/myuser/mypassword \
  -o /dev/null -w 'HTTP %{http_code}\n' || true

# trap fires here: $TMP_NETRC is deleted
echo 'Temp netrc cleaned up by trap.'

環境変数の適切な管理

環境変数は、スクリプトに秘密情報を注入する一般的な方法です(12-factor アプリや CI/CD パイプラインなど)。ただし、環境変数はすべての子プロセスに漏洩し、プロセスが存続する間は /proc/<pid>/environ にも現れます。

防御パターン:

  • 秘密情報を直ちにローカル変数へ取り込み、環境変数の設定を解除して、子プロセスが継承できないようにします。
  • 完全な継承環境を渡すのではなく、env -i またはインライン代入を使って、特定のコマンドに必要な秘密情報だけを渡します。
  • 秘密情報の変数を export しないでください。可能な場合は、export なしの代入だけを使用します。
#!/usr/bin/env bash
set -euo pipefail

# Simulate a secret arriving via environment (e.g., from CI system)
export DB_PASSWORD='hunter2'   # set by CI — we did not choose this

# Capture locally, then strip from environment immediately
db_password="$DB_PASSWORD"
unset DB_PASSWORD

# Verify the env var is gone before spawning any child process
if printenv DB_PASSWORD 2>/dev/null; then
  echo 'ERROR: DB_PASSWORD still in environment!' >&2
  exit 1
fi

echo 'Secret captured and env var scrubbed.'
echo "Password length: ${#db_password}"
unset db_password

set -x のトレースに秘密情報を表示させない

set -x(xtrace)はデバッグに非常に役立ちますが、展開するすべての変数の値を、秘密情報も含めて標準エラー出力に表示します。こうしたトレースは、CI ログや syslog に記録されることがよくあります。

トレースの有用性を保ちながら秘密情報を守る方法:

  • { set +x; } 2>/dev/null を使い、機密性の高い処理の周囲で一時的にトレースを無効にします。
  • 処理後に set -x で再度有効にします。
  • xtrace の出力を、公開ログストリームではなく保護されたログファイルへ送る別のファイルディスクリプターにリダイレクトします。
#!/usr/bin/env bash
set -euo pipefail
set -x   # tracing ON — safe for non-sensitive sections

echo 'Building application...'
SRC_DIR='/tmp/build'
mkdir -p "$SRC_DIR"

# Disable xtrace around secret handling (suppress the set +x line itself)
{ set +x; } 2>/dev/null

read -rsp 'Token (hidden from trace): ' SECRET_TOKEN <&2
printf '\n' >&2
token_len=${#SECRET_TOKEN}
unset SECRET_TOKEN

set -x  # tracing back ON

echo "Token captured (length=$token_len). Continuing build..."
ls "$SRC_DIR"

ログファイルから秘密情報を除去する

注意深く扱っていても、特に詳細ログを出力するスクリプトや古いスクリプトでは、秘密情報がログ出力に入り込むことがあります。既知のパターンをマスキングするロギングラッパー関数を用意すると、安全策を追加できます。

このパターンでは、すべてのログ出力に対して正規表現による置換を行います。これは、すでに説明した他の適切な管理方法の代わりではなく、最後の防御層です。

#!/usr/bin/env bash
set -euo pipefail

# A logging function that scrubs common secret patterns before writing
log() {
  local line
  # Replace anything that looks like key=VALUE or password=VALUE
  line=$(printf '%s\n' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***REDACTED***/gi')
  printf '[%s] %s\n' "$(date -u '+%T')" "$line"
}

# Usage:
log 'Connecting to database with password=hunter2'
log 'Loaded API token=sk-abc123xyz secret'
log 'Build step completed successfully'   # unchanged

env -i による隔離環境

env -i は空の環境でコマンドを開始します。これにより、誤って設定された秘密情報を含め、継承された変数が子プロセスに渡るのを防げます。そのうえで、必要なものだけを明示的に渡します。

信頼できないスクリプト、ビルドツール、または環境データを外部へ持ち出す可能性のあるサードパーティ製ユーティリティを実行する場合に、特に有効です。

#!/usr/bin/env bash
set -euo pipefail

# Polluted parent environment (simulating a CI runner)
export AWS_SECRET_ACCESS_KEY='AKIAIOSFODNN7EXAMPLE'
export GITHUB_TOKEN='ghp_faketoken123'
export HOME="$HOME"
export PATH="$PATH"

echo '--- Child sees full environment:'
env | grep -E 'AWS|GITHUB' | head -5

echo '--- Sanitised child (env -i) sees nothing secret:'
env -i HOME="$HOME" PATH="$PATH" TERM="${TERM:-dumb}" \
  bash -c 'env | grep -E "AWS|GITHUB" || echo "No secrets visible"'

unset AWS_SECRET_ACCESS_KEY GITHUB_TOKEN

tmpfs 上の一時的な秘密情報ファイル

tmpfs は RAM ベースのファイルシステムです。そこに書き込まれたファイルがディスクへフラッシュされることはないため、秘密情報がスワップ、ディスクキャッシュ、スナップショットに残るリスクをなくせます。

  • Linux では、/dev/shm と /run/user/<uid> が通常 tmpfs としてマウントされています。
  • tmpfs を使用する場合は、スクリプト終了時にファイルを削除する trap EXIT を必ず併用してください。
  • コンテナ(Docker、Kubernetes)では、秘密情報を tmpfs ボリュームとして /run/secrets に直接マウントできます。
#!/usr/bin/env bash
set -euo pipefail

# Prefer /run/user/$UID (user-owned tmpfs) or /dev/shm (world-readable dir!)
if [[ -d "/run/user/$UID" ]]; then
  TMPFS_DIR="/run/user/$UID"
elif [[ -d '/dev/shm' ]]; then
  TMPFS_DIR='/dev/shm'
else
  # Fallback: warn that disk will be used
  echo 'WARNING: No tmpfs available; using /tmp (disk-backed)' >&2
  TMPFS_DIR='/tmp'
fi

SECRET_FILE=$(mktemp "${TMPFS_DIR}/secret.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'shred -u "$SECRET_FILE" 2>/dev/null || rm -f "$SECRET_FILE"' EXIT

printf '%s' 'my-runtime-token' > "$SECRET_FILE"
echo "Secret stored in: $SECRET_FILE"
df -T "$SECRET_FILE" | awk 'NR==2 {print "Filesystem type:", $2}'

# Use the secret...
token=$(< "$SECRET_FILE")
echo "Token length: ${#token}"
unset token
# trap fires on exit: file shredded

総仕上げ:強化されたデプロイスクリプト

次のスクリプトは、このレッスンで扱ったすべての手法を、実際的なデプロイ用ヘルパーに組み合わせたものです。それぞれの防御層がどのように相互に補強し合うかを確認してください。

  • -s を使った標準入力からの読み取り — 端末にエコーされません
  • trap で後片付けを行うtmpfs 上の秘密情報ファイル
  • 環境変数の除去 — サブプロセスを起動する前に秘密情報の設定を解除します
  • xtrace ガード — 機密性の高いコードの周囲でトレースを一時停止します
  • ログのマスキング — ログへ書き込む前に安全策として正規表現を適用します
#!/usr/bin/env bash
set -euo pipefail

### 1. Redacting logger
log() {
  local msg
  msg=$(printf '%s' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***/gi')
  printf '[%s] %s\n' "$(date -u +%T)" "$msg"
}

### 2. tmpfs secret store
TMPFS_DIR="${XDG_RUNTIME_DIR:-/tmp}"
SECRET_FILE=$(mktemp "${TMPFS_DIR}/deploy_token.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'rm -f "$SECRET_FILE"; log "Secret file cleaned up."' EXIT

### 3. Read secret without trace
{ set +x; } 2>/dev/null
read -rsp 'Deploy token: ' _tok <&2; printf '\n' >&2
printf '%s' "$_tok" > "$SECRET_FILE"
unset _tok
set -x

### 4. Scrub inherited env vars before subprocess
unset DEPLOY_TOKEN 2>/dev/null || true

log 'Starting deployment...'
# Simulate deploy using secret from file (token never in argv)
# curl -H "Authorization: Bearer $(< $SECRET_FILE)" https://api.example.com/deploy
log 'Deployment complete. token=hidden_by_redactor'

echo 'Done.'

理解度チェック:プロセス一覧による秘密情報の漏洩

プロセス一覧を通じて秘密情報が漏洩する仕組みと、それを防ぐ方法について理解度を確認しましょう。

レッスンのまとめ:秘密情報を安全に扱う

Secure Secret Handling and Environment Hygieneを完了しました。ここまでに学んだ内容を簡潔にまとめます。

  • プロセス一覧:秘密情報をコマンドライン引数として渡さないでください。ps aux や /proc/<pid>/cmdline に表示されます。代わりに標準入力経由のパイプや --netrc-file を使用します。
  • 標準入力からの読み取り:read -rs を使うと、端末へのエコーやシェル履歴への露出なしに、対話的に秘密情報を取得できます。
  • ファイル権限:秘密情報ファイルには chmod 0600(または 0400)を設定する必要があります。アトミックに作成するには install -m 0600 を使用します。
  • netrc ファイル:認証情報を一時ファイルに委ね、--netrc-file でそのファイルを指定します。trap EXIT で後片付けを行います。
  • 環境変数の適切な管理:秘密情報の環境変数をローカルに取得したら、直ちに unset します。不必要に export せず、子プロセスを隔離するには env -i を使用します。
  • xtrace ガード:機密性の高いコードを { set +x; } 2>/dev/null ... set -x で囲み、デバッグトレースによる値の漏洩を防ぎます。
  • ログのマスキング:最後の安全策として、sed ベースのロガーを使用します。
  • tmpfs:実行時の秘密情報を /run/user/$UID または /dev/shm に保存し、ディスクに触れないようにします。終了時には完全に消去します。

重要なのは多層防御の考え方です。単一の対策では十分ではありませんが、対策を組み合わせることで秘密情報の漏洩を非常に困難にできます。

よくある質問

「シークレットの安全な取り扱いと環境の衛生管理」レッスンは無料ですか?

はい。「シークレットの安全な取り扱いと環境の衛生管理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。

「シークレットの安全な取り扱いと環境の衛生管理」で何を学びますか?

標準入力、ファイル、不要な情報を除去した環境を使い、認証情報がプロセス一覧やログに残らないようにします。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

DevOps Bootcampを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「シークレットの安全な取り扱いと環境の衛生管理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このDevOps Bootcampレッスンでコードを書いて実行できますか?

はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. コマンドインジェクションと引数インジェクションの防止
  2. シークレットの安全な取り扱いと環境の衛生管理
  3. 最小権限実行とsudoの適切な運用
  4. ShellCheckによる静的解析と監査
← DevOps Bootcampに戻る