- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. PEM、Key、CRTファイル形式の理解
- 2. Nginx基本TLS設定
- 3. TLSバージョン設定
- 4. 暗号スイート設定
- 5. DHパラメータ
- 6. HSTS(HTTP Strict Transport Security)
- 7. OCSP Stapling
- 8. SSLセッションキャッシング
- 9. HTTP/2設定
- 10. mTLS(Mutual TLS)設定
- 11. 自己署名証明書の生成(開発用)
- 12. 証明書変換
- 13. 証明書検証コマンド
- 14. セキュリティ点検ツール
- 15. Let's Encrypt自動更新連携
- 16. 設定チェックリスト
- 17. まとめ
1. PEM、Key、CRTファイル形式の理解
1.1 PEM(Privacy Enhanced Mail)
PEMはBase64でエンコードされた証明書/鍵の形式です。テキストエディタで開くことができます。
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
... (Base64エンコードデータ)
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7o4qne60TB3pO
... (Base64エンコードデータ)
-----END PRIVATE KEY-----
1.2 DER(Distinguished Encoding Rules)
バイナリ形式です。PEMからBase64エンコードを除去したものと同じです。
1.3 鍵タイプ
| アルゴリズム | 鍵サイズ | 性能 | セキュリティ | 推奨 |
|---|---|---|---|---|
| RSA 2048 | 2048 bit | 遅い | 十分 | 互換性重視 |
| RSA 4096 | 4096 bit | 非常に遅い | 高い | 高セキュリティ必要時 |
| ECDSA P-256 | 256 bit | 速い | 十分 | 推奨(Modern) |
| ECDSA P-384 | 384 bit | 速い | 高い | 高セキュリティ + 性能 |
ECDSAはRSA比で同等のセキュリティレベルにおいて鍵サイズが遥かに小さく、TLSハンドシェイクの性能に優れています。
1.4 Let's Encrypt発行ファイルのマッピング
/etc/letsencrypt/live/example.com/
fullchain.pem = サーバー証明書 + 中間CA証明書
privkey.pem = 秘密鍵
chain.pem = 中間CA証明書のみ
cert.pem = サーバー証明書のみ
Nginxで使用するファイル:
ssl_certificate -> fullchain.pem
ssl_certificate_key -> privkey.pem
2. Nginx基本TLS設定
2.1 最小設定
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
2.2 HTTPからHTTPSへのリダイレクト
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
2.3 完全なプロダクション設定例
# /etc/nginx/conf.d/ssl-params.conf
# 共通SSLパラメータ
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/nginx/conf.d/ssl-params.conf;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
3. TLSバージョン設定
3.1 TLSバージョン別セキュリティ状態
| バージョン | 状態 | 推奨 |
|---|---|---|
| SSL 2.0 | 廃止(1996年) | 絶対使用禁止 |
| SSL 3.0 | 廃止(POODLE脆弱性) | 絶対使用禁止 |
| TLS 1.0 | 廃止(2021年) | 無効化 |
| TLS 1.1 | 廃止(2021年) | 無効化 |
| TLS 1.2 | 現行標準 | 使用 |
| TLS 1.3 | 最新標準 | 使用(推奨) |
3.2 TLS 1.2/1.3のみ許可
ssl_protocols TLSv1.2 TLSv1.3;
3.3 TLS 1.3専用(Modern)
ssl_protocols TLSv1.3;
TLS 1.3の利点:
- ハンドシェイクラウンドトリップ削減(1-RTT、0-RTT)
- 脆弱なレガシー暗号スイートの除去
- Forward Secrecyがデフォルトで適用
- ハンドシェイクの暗号化(サーバー証明書を含む)
4. 暗号スイート設定
4.1 Mozilla SSL Configuration Generatorプロファイル
Mozillaは3つの構成プロファイルを提供しています。
Modern(TLS 1.3専用):
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
Intermediate(TLS 1.2 + 1.3、推奨):
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
4.2 暗号スイート名の解読
ECDHE-RSA-AES256-GCM-SHA384の意味:
ECDHE = 鍵交換アルゴリズム(Elliptic Curve Diffie-Hellman Ephemeral)
→ Forward Secrecyを提供
RSA = 認証アルゴリズム(サーバー証明書署名)
AES256 = 対称暗号化(256ビットAES)
GCM = 暗号化モード(Galois/Counter Mode、AEAD)
SHA384 = ハッシュ関数(完全性検証)
5. DHパラメータ
Diffie-Hellman鍵交換に使用されるパラメータです。DHE暗号スイートを使用する際に必要です。
# DHパラメータ生成(2048ビット以上推奨)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
# より強力な4096ビット(生成に時間がかかる)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
6. HSTS(HTTP Strict Transport Security)
6.1 HSTSとは
HSTSはブラウザに対して該当ドメインに常にHTTPSで接続するよう指示するHTTPヘッダーです。
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
6.2 HSTS Preload List
HSTS Preload Listに登録すると、ブラウザが初回アクセスからHTTPSを強制します。
登録条件:
- 有効なTLS証明書
- ポート80から443へのリダイレクト
- すべてのサブドメインにHTTPSを適用
- HSTSヘッダーにmax-age最低31536000(1年)、includeSubDomains、preloadを含む
6.3 注意事項
- HSTSを設定すると元に戻すのが困難(max-age期間中ブラウザがHTTP接続をブロック)
- まず小さなmax-age(例:300秒)から始めて徐々に増やすことを推奨
7. OCSP Stapling
7.1 OCSP Staplingとは
OCSP(Online Certificate Status Protocol)は証明書が失効(revoke)されたかを確認するプロトコルです。OCSP Staplingはサーバーが事前にCAからOCSP応答を取得し、TLSハンドシェイク時にクライアントに渡す技術です。
OCSP Staplingなし:
Client -> Server(TLSハンドシェイク)
Client -> CA OCSPサーバー(証明書有効性確認)<- 追加遅延
OCSP Staplingあり:
Server -> CA OCSPサーバー(定期的にOCSP応答を更新)
Client -> Server(TLSハンドシェイク + OCSP応答を含む)<- 高速
7.2 設定
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
7.3 OCSP Staplingの確認
openssl s_client -connect example.com:443 -servername example.com -status < /dev/null 2>/dev/null | grep -A 20 "OCSP Response"
8. SSLセッションキャッシング
# 共有メモリキャッシュ(複数workerプロセス間で共有)
# 10MBキャッシュ = 約40,000セッション保存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# セッションチケット無効化(Forward Secrecy保証のため)
ssl_session_tickets off;
9. HTTP/2設定
9.1 HTTP/2の有効化
server {
listen 443 ssl http2;
server_name example.com;
# ... SSL設定 ...
}
HTTP/2の利点:
- マルチプレキシング:1つのTCP接続で複数リクエストを同時処理
- ヘッダー圧縮:HPACKでHTTPヘッダーを圧縮
- サーバープッシュ:クライアントがリクエストする前にリソースを送信
- 優先度:リソース別の送信優先度設定
9.2 HTTP/3(QUIC)準備
# Nginx 1.25.0+(またはnginx-quic)
server {
listen 443 ssl;
listen 443 quic reuseport;
http2 on;
http3 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
}
10. mTLS(Mutual TLS)設定
10.1 mTLSとは
通常のTLSはサーバーのみ証明書を提示しますが、mTLSはクライアントも証明書を提示して双方向認証を行います。
通常のTLS:
Client ── サーバー証明書確認 ──> Server
Mutual TLS:
Client <── サーバー証明書確認 ──> Server
Client ── クライアント証明書 ──> Server(サーバーがクライアント証明書を確認)
使用例:
- マイクロサービス間通信(サービスメッシュ)
- API認証(API Gateway)
- IoTデバイス認証
- 内部管理ツールのアクセス制御
10.2 CA証明書でクライアント証明書を発行
# 1. CA秘密鍵生成
openssl genrsa -out ca.key 4096
# 2. CA証明書生成(自己署名)
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/C=JP/ST=Tokyo/O=MyOrg/CN=Internal CA"
# 3. クライアント秘密鍵生成
openssl genrsa -out client.key 2048
# 4. クライアントCSR生成
openssl req -new -key client.key -out client.csr \
-subj "/C=JP/ST=Tokyo/O=MyOrg/CN=service-a"
# 5. CAでクライアント証明書に署名
openssl x509 -req -days 365 -in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt
10.3 Nginx mTLS設定
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/server.fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/server.privkey.pem;
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
location / {
proxy_pass http://backend;
proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
}
}
10.4 ssl_verify_clientオプション
# on:クライアント証明書必須(ない場合400エラー)
ssl_verify_client on;
# optional:証明書任意(なくてもアクセス可能、あれば検証)
ssl_verify_client optional;
# optional_no_ca:検証なしで転送のみ(バックエンドで検証)
ssl_verify_client optional_no_ca;
10.5 mTLSテスト
# クライアント証明書でリクエスト
curl --cert client.crt --key client.key \
--cacert ca.crt \
https://api.example.com/data
# 証明書なしでリクエスト(ssl_verify_client onの場合失敗)
curl --cacert ca.crt https://api.example.com/data
# 400 Bad Request - No required SSL certificate was sent
11. 自己署名証明書の生成(開発用)
11.1 単一ドメイン
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout selfsigned.key \
-out selfsigned.crt \
-days 365 \
-subj "/C=JP/ST=Tokyo/O=Dev/CN=localhost"
11.2 SAN(Subject Alternative Name)付き
cat > san.cnf << 'SANEOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C = JP
ST = Tokyo
O = Dev
CN = localhost
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = myapp.local
DNS.3 = *.myapp.local
IP.1 = 127.0.0.1
IP.2 = 192.168.1.100
SANEOF
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout selfsigned.key \
-out selfsigned.crt \
-days 365 \
-config san.cnf \
-extensions v3_req
12. 証明書変換
12.1 PEMからDERへ
openssl x509 -in cert.pem -outform DER -out cert.der
openssl rsa -in key.pem -outform DER -out key.der
12.2 DERからPEMへ
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
12.3 PFX/PKCS12からPEMへ
openssl pkcs12 -in certificate.pfx -clcerts -nokeys -out cert.pem
openssl pkcs12 -in certificate.pfx -nocerts -nodes -out key.pem
openssl pkcs12 -in certificate.pfx -cacerts -nokeys -out chain.pem
12.4 PEMからPFX/PKCS12へ
openssl pkcs12 -export \
-out certificate.pfx \
-inkey key.pem \
-in cert.pem \
-certfile chain.pem
13. 証明書検証コマンド
13.1 証明書内容確認
openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.pem -issuer -noout
openssl x509 -in cert.pem -subject -noout
openssl x509 -in cert.pem -enddate -noout
openssl x509 -in cert.pem -noout -ext subjectAltName
13.2 リモートサーバー証明書確認
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | \
openssl x509 -text -noout
# 証明書チェーン確認
openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null 2>/dev/null
# TLSバージョンとcipher確認
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | \
grep -E "Protocol|Cipher"
13.3 証明書と鍵のマッチング確認
# 証明書と鍵のmodulusを比較(同じであればマッチ)
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
14. セキュリティ点検ツール
14.1 SSL Labs
オンラインでTLS設定を点検するサービスです。
https://www.ssllabs.com/ssltest/analyze.html?d=example.com
等級基準:
A+ = 最優秀(HSTS + 強力な設定)
A = 優秀
B = 良好(改善推奨)
C = 不十分(セキュリティリスク)
F = 危険(即時対応必要)
14.2 testssl.sh
コマンドラインでTLS設定を点検するオープンソースツールです。
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
./testssl.sh/testssl.sh example.com
./testssl.sh/testssl.sh --protocols example.com
./testssl.sh/testssl.sh --ciphers example.com
./testssl.sh/testssl.sh --vulnerabilities example.com
14.3 Nginx設定検証
sudo nginx -t
sudo nginx -T
# Gixy(Nginxセキュリティ分析ツール)
pip install gixy
gixy /etc/nginx/nginx.conf
15. Let's Encrypt自動更新連携
15.1 Certbot + Nginx Reloadフック
sudo certbot renew --deploy-hook "systemctl reload nginx"
15.2 Docker Composeでの自動更新
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
restart: unless-stopped
command: '/bin/sh -c ''while :; do sleep 6h & wait; nginx -s reload; done & nginx -g "daemon off;"'''
certbot:
image: certbot/certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait; done'"
16. 設定チェックリスト
プロダクションデプロイ前に確認すべき項目:
| 項目 | コマンド/確認方法 | 基準 |
|---|---|---|
| TLSバージョン | ssl_protocols | TLSv1.2 TLSv1.3のみ |
| 証明書チェーン | fullchain.pem使用 | 中間CA含む |
| HSTS | ヘッダー確認 | max-age 1年以上 |
| OCSP Stapling | openssl s_client -status | Cert Status: good |
| HTTPリダイレクト | curl -I http://domain | 301 to HTTPS |
| SSL Labs等級 | ssllabs.com | A+目標 |
| 秘密鍵権限 | ls -la privkey.pem | 600(rootのみ) |
| 自動更新 | certbot renew --dry-run | 成功 |
| DHパラメータ | 2048ビット以上 | 生成完了 |
| Session Tickets | off | Forward Secrecy |
17. まとめ
Nginx TLS設定は単に証明書を適用するだけでなく、総合的なセキュリティ最適化が必要です。要点整理:
- fullchain.pem + privkey.pemを使用して証明書チェーンを完成
- TLS 1.2/1.3のみ許可し、レガシープロトコルを無効化
- Mozilla Intermediateプロファイルの暗号スイートを推奨
- HSTSでHTTPS接続を強制、OCSP Staplingでパフォーマンス向上
- SSLセッションキャッシングでハンドシェイクオーバーヘッドを削減
- HTTP/2でウェブパフォーマンスを最適化
- mTLSでマイクロサービス間の双方向認証を実現
- SSL LabsでA+等級を目標に設定を最適化
- Let's Encrypt証明書の自動更新を必ず設定