Skip to content
Published on

[DevOps] Nginx TLS設定完全ガイド:PEM/Key適用とセキュリティ最適化

Authors

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 20482048 bit遅い十分互換性重視
RSA 40964096 bit非常に遅い高い高セキュリティ必要時
ECDSA P-256256 bit速い十分推奨(Modern)
ECDSA P-384384 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_protocolsTLSv1.2 TLSv1.3のみ
証明書チェーンfullchain.pem使用中間CA含む
HSTSヘッダー確認max-age 1年以上
OCSP Staplingopenssl s_client -statusCert Status: good
HTTPリダイレクトcurl -I http://domain301 to HTTPS
SSL Labs等級ssllabs.comA+目標
秘密鍵権限ls -la privkey.pem600(rootのみ)
自動更新certbot renew --dry-run成功
DHパラメータ2048ビット以上生成完了
Session TicketsoffForward 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証明書の自動更新を必ず設定