環境変数・シークレット管理 - 設定方法からシークレット管理ツールまで
環境変数の基本から、シークレット管理ツールの比較、CI/CDでの扱い、セキュリティのベストプラクティスまでをまとめたリファレンス。
環境変数の基本
環境変数は、OS上のプロセスに設定情報を渡す仕組み。アプリケーションの設定(DB接続先、APIキー、動作モードなど)をコードから分離するために使用する。
OS別の設定方法
Linux / macOS
# 一時的に設定(現在のシェルセッションのみ)
export DATABASE_URL="postgres://localhost:5432/mydb"
export API_KEY="sk-xxxxxxxxxxxx"
# 確認
echo $DATABASE_URL
printenv DATABASE_URL
# 一覧表示
env
printenv
# コマンド実行時のみ有効にする
DATABASE_URL="postgres://localhost/test" node app.js
# 削除
unset DATABASE_URL
永続化する場合はシェルの設定ファイルに記述する。
| シェル | 設定ファイル |
|---|---|
| bash | ~/.bashrc または ~/.bash_profile |
| zsh | ~/.zshrc |
| fish | ~/.config/fish/config.fish |
# ~/.bashrc に追加
export NODE_ENV="production"
export PATH="$HOME/.local/bin:$PATH"
Windows
# PowerShell - 一時的(現在のセッションのみ)
$env:DATABASE_URL = "postgres://localhost:5432/mydb"
# PowerShell - 永続化(ユーザー環境変数)
[Environment]::SetEnvironmentVariable("DATABASE_URL", "postgres://localhost:5432/mydb", "User")
# PowerShell - 永続化(システム環境変数、管理者権限が必要)
[Environment]::SetEnvironmentVariable("DATABASE_URL", "postgres://localhost:5432/mydb", "Machine")
# 確認
$env:DATABASE_URL
Get-ChildItem Env:
rem コマンドプロンプト
set DATABASE_URL=postgres://localhost:5432/mydb
setx DATABASE_URL "postgres://localhost:5432/mydb"
プログラム言語からのアクセス
// Node.js
const dbUrl = process.env.DATABASE_URL
const port = parseInt(process.env.PORT || '3000', 10)
# Python
import os
db_url = os.environ.get("DATABASE_URL")
port = int(os.environ.get("PORT", "3000"))
// Go
dbURL := os.Getenv("DATABASE_URL")
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
// Java
String dbUrl = System.getenv("DATABASE_URL");
.envファイルとdotenvパターン
.envファイルの形式
.env ファイルにキーバリュー形式で環境変数を記述し、アプリケーション起動時に読み込むパターン。
# .env
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=sk-xxxxxxxxxxxx
# コメント行
# 値にスペースを含む場合はクォートで囲む
APP_NAME="My Application"
# 変数の参照(dotenvライブラリによってはサポート)
BASE_URL=http://localhost:${PORT}
dotenvライブラリ
| 言語 | ライブラリ | インストール |
|---|---|---|
| Node.js | dotenv | npm install dotenv |
| Python | python-dotenv | pip install python-dotenv |
| Ruby | dotenv | gem install dotenv |
| Go | godotenv | go get github.com/joho/godotenv |
| PHP | vlucas/phpdotenv | composer require vlucas/phpdotenv |
// Node.js - アプリケーションのエントリポイントで読み込む
import 'dotenv/config'
// または
import dotenv from 'dotenv'
dotenv.config()
console.log(process.env.DATABASE_URL)
# Python
from dotenv import load_dotenv
import os
load_dotenv() # .envファイルを読み込む
db_url = os.environ.get("DATABASE_URL")
環境ごとの.envファイル
.env # デフォルト値(全環境共通)
.env.local # ローカル環境用(.gitignoreに含める)
.env.development # 開発環境用
.env.staging # ステージング環境用
.env.production # 本番環境用
.env.test # テスト環境用
.gitignoreでの除外(必須)
シークレットを含む.envファイルは必ずGitの管理対象から除外する。 一度コミットしてしまうとGit履歴に残り、git rmだけでは完全に削除できない。
# .gitignore
# 環境変数ファイル
.env
.env.local
.env.*.local
# テンプレートはコミットしてよい
!.env.example
!.env.template
.env.example として、実際の値を含まないテンプレートをリポジトリに含めるのが一般的な慣習。
# .env.example(チームメンバーへのガイド用)
NODE_ENV=development
PORT=3000
DATABASE_URL=postgres://user:password@localhost:5432/dbname
API_KEY=your-api-key-here
フロントエンドフレームワークでの環境変数
フロントエンドではビルド時に環境変数が静的にバンドルされる。公開されるJavaScriptに含まれるため、シークレットは絶対に含めてはいけない。
Vite
# .env
VITE_API_URL=https://api.example.com # ← クライアントに公開される
VITE_APP_TITLE=My App # ← クライアントに公開される
SECRET_KEY=sk-xxxxxxxxxxxx # ← クライアントには公開されない
// アクセス方法
const apiUrl = import.meta.env.VITE_API_URL
const mode = import.meta.env.MODE // "development" | "production"
const isDev = import.meta.env.DEV // boolean
const isProd = import.meta.env.PROD // boolean
VITE_プレフィックスが付いた変数のみクライアントコードに公開される- プレフィックスなしの変数はサーバーサイド(
vite.config.ts等)でのみ利用可能
Next.js
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com # ← クライアントに公開
DATABASE_URL=postgres://localhost/mydb # ← サーバーサイドのみ
// クライアントコンポーネントで利用可能
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// サーバーコンポーネント・API Routeでのみ利用可能
const dbUrl = process.env.DATABASE_URL
NEXT_PUBLIC_プレフィックスでクライアント公開を制御
各フレームワークのプレフィックス比較
| フレームワーク | クライアント公開プレフィックス | アクセス方法 |
|---|---|---|
| Vite | VITE_ | import.meta.env.VITE_XXX |
| Next.js | NEXT_PUBLIC_ | process.env.NEXT_PUBLIC_XXX |
| Create React App | REACT_APP_ | process.env.REACT_APP_XXX |
| Nuxt 3 | NUXT_PUBLIC_ | useRuntimeConfig().public.xxx |
| Angular | なし(environment.tsで管理) | environment.xxx |
Dockerでの環境変数
docker runでの指定
# 個別に指定
docker run -e DATABASE_URL="postgres://db:5432/mydb" \
-e API_KEY="sk-xxxx" \
myapp
# ホスト環境変数を引き継ぐ(値を省略)
export DATABASE_URL="postgres://db:5432/mydb"
docker run -e DATABASE_URL myapp
# ファイルから読み込む
docker run --env-file .env myapp
# 複数のファイルを指定
docker run --env-file .env --env-file .env.local myapp
Dockerfileでの設定
# ビルド時の変数(イメージに残る場合がある)
ARG NODE_VERSION=20
# 実行時の環境変数(デフォルト値の設定)
ENV NODE_ENV=production
ENV PORT=3000
# ARGからENVへの受け渡し
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
注意:
ARGやENVにシークレットを設定すると、イメージのレイヤーに残る。ビルド時のシークレットには--secretフラグを使用する。
# BuildKitのsecretマウント(シークレットがイメージに残らない)
docker build --secret id=mysecret,src=./secret.txt .
# Dockerfile内でのsecret参照
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Docker Compose
# docker-compose.yml
services:
app:
image: myapp
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://db:5432/mydb
env_file:
- .env
- .env.local
db:
image: postgres:16
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword # 本番では外部シークレット管理を推奨
POSTGRES_DB: mydb
シークレット管理ツール
本番環境では、.envファイルや環境変数の直接設定ではなく、専用のシークレット管理ツールを使用することが推奨される。
ツール比較
| ツール | 提供元 | 特徴 | 料金目安 |
|---|---|---|---|
| AWS Secrets Manager | AWS | AWSサービスとの統合が容易、自動ローテーション | $0.40/シークレット/月 + API呼び出し |
| AWS SSM Parameter Store | AWS | 無料枠あり、シンプルな設定値管理 | Standard: 無料、Advanced: $0.05/パラメータ/月 |
| Azure Key Vault | Microsoft | Azure統合、HSMバックエンド | トランザクションベース課金 |
| Google Secret Manager | GCP統合、シンプルなAPI | $0.06/10,000アクセス | |
| HashiCorp Vault | HashiCorp | マルチクラウド、動的シークレット生成 | OSS版は無料、Enterprise版は有償 |
| 1Password / Doppler | 各社 | 開発者フレンドリー、CLI統合 | チーム規模に応じた月額課金 |
AWS Secrets Manager
# シークレットの作成
aws secretsmanager create-secret \
--name myapp/production/db \
--secret-string '{"username":"admin","password":"s3cret","host":"db.example.com"}'
# シークレットの取得
aws secretsmanager get-secret-value \
--secret-id myapp/production/db \
--query SecretString --output text
// Node.js (AWS SDK v3)
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager'
const client = new SecretsManagerClient({ region: 'ap-northeast-1' })
async function getSecret(secretId) {
const command = new GetSecretValueCommand({ SecretId: secretId })
const response = await client.send(command)
return JSON.parse(response.SecretString)
}
const dbConfig = await getSecret('myapp/production/db')
AWS SSM Parameter Store
# パラメータの登録(SecureString = 暗号化)
aws ssm put-parameter \
--name "/myapp/production/DATABASE_URL" \
--value "postgres://admin:s3cret@db.example.com:5432/mydb" \
--type SecureString
# パラメータの取得
aws ssm get-parameter \
--name "/myapp/production/DATABASE_URL" \
--with-decryption \
--query Parameter.Value --output text
# パスベースで一括取得
aws ssm get-parameters-by-path \
--path "/myapp/production/" \
--with-decryption
HashiCorp Vault
# シークレットの書き込み
vault kv put secret/myapp/db \
username=admin \
password=s3cret
# シークレットの読み取り
vault kv get secret/myapp/db
vault kv get -field=password secret/myapp/db
# 動的シークレット(DB認証情報を一時的に生成)
vault read database/creds/myapp-role
Vaultの動的シークレット生成機能は、必要な時にだけ一時的な認証情報を作成し、TTL経過後に自動で無効化する。長期間固定のシークレットを持たないため、漏洩リスクが大幅に低減される。
CI/CDでのシークレット管理
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
echo "Deploying..."
# シークレットはログに自動でマスクされる
- Repository secrets: リポジトリ単位で設定
- Environment secrets:
production/stagingなど環境別に設定 - Organization secrets: 組織内の複数リポジトリで共有
- ワークフローログではシークレットの値が
***に自動マスクされる
GitLab CI/CD
# .gitlab-ci.yml
deploy:
stage: deploy
variables:
DATABASE_URL: $DATABASE_URL # CI/CD変数から注入
script:
- echo "Deploying..."
environment:
name: production
- Settings > CI/CD > Variables から設定
Protectedフラグで保護ブランチのみに制限可能Maskedフラグでログ出力時にマスク
AWS CodeBuild / CodePipeline
# buildspec.yml
version: 0.2
env:
secrets-manager:
DB_PASSWORD: "myapp/production/db:password"
API_KEY: "myapp/production/api:key"
parameter-store:
DB_HOST: "/myapp/production/DB_HOST"
phases:
build:
commands:
- echo "DB_HOST=$DB_HOST"
セキュリティのベストプラクティス
シークレットのローテーション
定期的にシークレットを更新し、漏洩時の被害を限定する。
# AWS Secrets Manager - 自動ローテーション設定
aws secretsmanager rotate-secret \
--secret-id myapp/production/db \
--rotation-lambda-arn arn:aws:lambda:ap-northeast-1:123456789:function:SecretsRotation \
--rotation-rules '{"AutomaticallyAfterDays": 30}'
最小権限の原則
- 各サービス・コンポーネントには必要最小限のシークレットのみアクセス許可を付与する
- IAMポリシーでシークレットの読み取り範囲を限定する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789:secret:myapp/production/*"
}
]
}
監査
- シークレットへのアクセスログを記録する(AWS CloudTrail、Vault Audit Log等)
- 異常なアクセスパターンを検知するアラートを設定する
- 誰が、いつ、どのシークレットにアクセスしたかを追跡可能にする
その他の推奨事項
| 項目 | 推奨 |
|---|---|
| 暗号化 | 保存時・転送時ともに暗号化(AES-256、TLS) |
| アクセス制御 | RBAC(ロールベースアクセス制御)を適用 |
| バックアップ | シークレット管理ツール自体のバックアップと復旧手順を整備 |
| 有効期限 | APIキーやトークンには有効期限を設定 |
| 二重管理の回避 | シークレットの正規の保管場所を一か所に定める |
アンチパターン
ソースコードへのハードコード
// ❌ NG: シークレットをコードに直接記述
const client = new S3Client({
credentials: {
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
},
})
// ✅ OK: 環境変数またはシークレット管理ツールから取得
const client = new S3Client({}) // IAMロールまたは環境変数から自動解決
Gitリポジトリへのコミット
コミット履歴に一度入ったシークレットは、git rm しても git log やリフログから復元可能。万が一コミットした場合は以下を実行する。
- 即座にシークレットを無効化・ローテーションする(最優先)
git filter-repoや BFG Repo-Cleaner で履歴から削除する- GitHubの場合、サポートに連絡してキャッシュの削除を依頼する
# git filter-repo による履歴からの削除(要事前バックアップ)
git filter-repo --path .env --invert-paths
ログへの出力
// ❌ NG: デバッグ時にシークレットをログ出力
console.log('DB connection:', process.env.DATABASE_URL)
console.log('Config:', JSON.stringify(config)) // configにシークレットが含まれる可能性
// ✅ OK: シークレットをマスクする
console.log('DB connection: [REDACTED]')
console.log('DB host:', new URL(process.env.DATABASE_URL).hostname)
その他のアンチパターン
| アンチパターン | リスク | 対策 |
|---|---|---|
| 全環境で同じシークレットを使い回す | 1つ漏洩で全環境に影響 | 環境ごとに異なるシークレットを使用 |
| シークレットをURLパラメータに含める | アクセスログに記録される | ヘッダーやリクエストボディで送信 |
| チャットやメールでシークレットを共有 | 平文で残り続ける | シークレット管理ツール経由で共有 |
| シークレットを変更せず長期間使い続ける | 漏洩に気付かないリスク | 定期的なローテーションを実施 |
.env ファイルを本番サーバーに直接配置 | 管理が属人化、追跡不可 | シークレット管理ツールを使用 |
参考リンク
- The Twelve-Factor App - Config - 環境変数による設定管理の原則
- dotenv - npm - Node.js用dotenvライブラリ
- Vite - Env Variables and Modes - Viteの環境変数ドキュメント
- Next.js - Environment Variables - Next.jsの環境変数ドキュメント
- AWS Secrets Manager User Guide - AWS Secrets Manager公式ガイド
- AWS Systems Manager Parameter Store - SSM Parameter Store公式ガイド
- HashiCorp Vault Documentation - Vault公式ドキュメント
- GitHub Actions - Using secrets - GitHub Actionsのシークレット管理
- OWASP Secrets Management Cheat Sheet - OWASPシークレット管理ガイド