暗号スイート(Cipher Suite)
概要
暗号スイート(Cipher Suite) は、TLS 通信で使用する暗号アルゴリズムの組み合わせです。鍵交換、認証、暗号化、ハッシュの 4 つの要素で構成され、クライアントとサーバーが TLS ハンドシェイクの最初の段階で合意します。RFC 5246(TLS 1.2)と RFC 8446(TLS 1.3)で定義されています。
TLS 1.2 では鍵交換・認証・暗号化・ハッシュの 4 要素をすべて 1 つの暗号スイート名に含めていました。TLS 1.3 ではこの設計を見直し、鍵交換と認証をハンドシェイクパラメーターに分離しました。暗号スイートには AEAD 暗号とハッシュ関数だけを含めるように簡素化されています。使用できるスイートも 5 つに限定され、安全性が低いアルゴリズムは仕様レベルで排除されています。
サーバー管理者にとって暗号スイートの選択はセキュリティと互換性のバランスに直結します。弱い暗号スイートを有効にしたままにすると、ダウングレード攻撃によって通信の安全性が損なわれます。逆に強い暗号スイートだけに絞りすぎると、古いクライアントが接続できなくなります。
仕組み
暗号スイートの構成要素は TLS 1.2 と TLS 1.3 で大きく異なり、TLS 1.3 では安全性の低いアルゴリズムが仕様レベルで排除されています。
TLS 1.2 の暗号スイート構造
TLS 1.2 の暗号スイート名は 4 つの要素をアンダースコアで連結した形式です。TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 を例に分解します。
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
│ │ │ │
│ │ │ └── ハッシュ: SHA-256(PRF / HMAC)
│ │ └── 暗号化: AES 128-bit、GCM モード
│ └── 認証: RSA(サーバー証明書の署名アルゴリズム)
└── 鍵交換: ECDHE(楕円曲線 Diffie-Hellman Ephemeral)
各要素の役割は次のとおりです。
- 鍵交換は、クライアントとサーバーが共有秘密鍵を安全に導出する方法を決めます。ECDHE や DHE はエフェメラル鍵を使うため Forward Secrecy(前方秘匿性)を提供します。RSA 鍵交換は静的な鍵を使うため、サーバーの秘密鍵が漏洩すると過去の通信もすべて復号されます
- 認証は、サーバー証明書の署名検証に使うアルゴリズムを指定します。RSA または ECDSA が使われます
- 暗号化は、ハンドシェイク完了後のデータを保護する対称暗号アルゴリズムです。AES-GCM や ChaCha20-Poly1305 は AEAD(Authenticated Encryption with Associated Data)であり、暗号化と改ざん検知を同時に行います
- ハッシュは、TLS 1.2 では PRF(擬似乱数関数)と HMAC に使用します
IANA が管理する TLS Cipher Suites レジストリに、各スイートの 2 バイトのコードポイントが割り当てられています。TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 のコードポイントは 0xC0, 0x2F です。
TLS 1.3 の暗号スイート構造
TLS 1.3 では暗号スイートの設計が根本から変わりました。鍵交換と認証はスイート名から分離され、ハンドシェイクの別のパラメーター(supported_groups 拡張と signature_algorithms 拡張)で交渉します。暗号スイート名には AEAD アルゴリズムとハッシュ関数だけが含まれます。
TLS_AES_128_GCM_SHA256
│ │
│ └── ハッシュ: SHA-256(HKDF / ハンドシェイク MAC)
└── AEAD 暗号: AES 128-bit、GCM モード
RFC 8446 で定義された TLS 1.3 の暗号スイートは以下の 5 つです。
TLS_AES_128_GCM_SHA256 (必須実装)
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_CCM_SHA256
TLS_AES_128_CCM_8_SHA256
TLS_AES_128_GCM_SHA256 は RFC 8446 で必須実装(MUST implement)と規定されています。すべての TLS 1.3 対応実装がこのスイートをサポートします。
TLS 1.3 では RC4、3DES、CBC モードの暗号、RSA 鍵交換、静的 Diffie-Hellman など安全性に問題のあるアルゴリズムが仕様レベルで削除されました。すべてのスイートが AEAD であり、Forward Secrecy が必須です。
ハンドシェイクでのネゴシエーション
暗号スイートの選択は TLS ハンドシェイクの最初のステップで行われます。
- クライアントが ClientHello を送る。クライアントが対応する暗号スイートの一覧を優先順に含める
- サーバーが一覧を受け取り、自身の設定と照合して 1 つのスイートを選択する
- サーバーが ServerHello で選択したスイートをクライアントに通知する
- 以降の通信は選択されたスイートの暗号アルゴリズムで保護される
サーバー側で ssl_prefer_server_ciphers on を設定すると、サーバーの優先順位が適用されます。TLS 1.2 では弱い暗号スイートを下位に回すためにこの設定が推奨されていました。TLS 1.3 ではすべてのスイートが安全なため、Mozilla はこのオプションを off にすることを推奨しています。クライアントが自身のハードウェアに最適なスイートを選択できるようにするためです。AES-NI を持たないモバイル端末は ChaCha20-Poly1305 のほうが高速に動作します。
設定例
Nginx で TLS 1.2 と TLS 1.3 の暗号スイートを設定する例です。Mozilla の Intermediate プロファイルに準拠しています。
server {
listen 443 ssl;
server_name example.com;
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.2 の暗号スイート
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;
# TLS 1.3 の暗号スイート(nginx 1.19.4 以降)
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
# TLS 1.3 ではクライアント優先を推奨
ssl_prefer_server_ciphers off;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
ssl_ciphers は TLS 1.2 以前に適用され、ssl_conf_command Ciphersuites は TLS 1.3 専用です。nginx は 1.19.4 以降でこの分離をサポートしています。それ以前のバージョンでは TLS 1.3 のスイートを個別に制御できません。
暗号スイート名は IANA 形式と OpenSSL 形式で異なります。nginx は OpenSSL 形式を使用します。
IANA 形式: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
OpenSSL 形式: ECDHE-RSA-AES128-GCM-SHA256
構成に迷った場合は Mozilla SSL Configuration Generator(ssl-config.mozilla.org)で対象のサーバーソフトウェアとバージョンを選択すると、推奨設定を自動生成できます。
確認方法
openssl でサーバーがネゴシエートした暗号スイートを確認するには次のコマンドを使います。
# 接続で使用された暗号スイートを表示
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep "Cipher is"
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
出力の Cipher is 行にネゴシエートされたスイート名が表示されます。
TLS 1.3 と TLS 1.2 のどちらで接続されたかは Protocol 行で確認できます。
# プロトコルと暗号スイートを簡潔に表示
echo | openssl s_client -connect example.com:443 -servername example.com -brief 2>/dev/null
特定の暗号スイートに限定して接続テストを行うには、TLS 1.2 では -cipher、TLS 1.3 では -ciphersuites オプションを使います。
# TLS 1.2 の特定スイートでテスト
echo | openssl s_client -connect example.com:443 -servername example.com -no_tls1_3 -cipher ECDHE-RSA-AES128-GCM-SHA256 2>/dev/null | grep "Cipher is"
# TLS 1.3 の特定スイートでテスト
echo | openssl s_client -connect example.com:443 -servername example.com -tls1_3 -ciphersuites TLS_AES_128_GCM_SHA256 2>/dev/null | grep "Cipher is"
接続に失敗した場合、サーバーがそのスイートを有効にしていないことを示します。
外部の視点からも確認したい場合は、Labee Dev Toolbox の SSL Cert API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/ssl-cert?hostname=example.com"
{
"success": true,
"data": {
"hostname": "example.com",
"port": 443,
"reachable": true,
"status": 200
},
"error": null,
"meta": { "responseTime": 123 }
}
data.reachable が true であれば TLS ハンドシェイクが成立しています。false の場合、暗号スイートの不一致や TLS バージョンの非互換が原因である可能性があります。
よくある問題
暗号スイートの設定ミスは、脆弱な暗号の残存やクライアントとの互換性問題として現れます。
弱い暗号スイートが有効なまま
RC4 は 2015 年に RFC 7465 で TLS での使用が禁止されました。3DES は 2016 年の Sweet32 攻撃で実用的な脆弱性が示され、現在のブラウザーはすべて無効化しています。CBC モードの暗号は BEAST 攻撃(2011 年)や Lucky Thirteen 攻撃(2013 年)の対象であり、TLS 1.3 では仕様から削除されています。
SSL Labs(ssllabs.com)でスキャンすると、これらの弱いスイートが有効な場合に警告が表示されます。nginx の ssl_ciphers で ECDHE + AEAD のスイートのみを列挙し、弱いスイートを明示的に排除します。
Forward Secrecy のない暗号スイート
RSA 鍵交換を使うスイート(例: TLS_RSA_WITH_AES_128_GCM_SHA256)は Forward Secrecy を提供しません。サーバーの秘密鍵が将来漏洩した場合、過去に記録された通信をすべて復号できてしまいます。ECDHE または DHE を鍵交換に使うスイートを選択すると、各セッションの共有鍵が独立するため、この問題を回避できます。TLS 1.3 では RSA 鍵交換が廃止されており、すべてのスイートが Forward Secrecy を持ちます。
クライアントとの暗号スイート不一致
サーバーが TLS 1.3 のスイートのみを有効にしている場合、TLS 1.2 までしか対応していない古いクライアントは接続できません。逆に、TLS 1.2 を無効にしていなくても ssl_ciphers にクライアントが対応するスイートが含まれていなければ handshake failure が発生します。
openssl の -cipher オプションで対象クライアントが使うスイートを指定して接続テストを行うと、不一致を事前に検出できます。
ssl_prefer_server_ciphers の誤用
TLS 1.2 で ssl_prefer_server_ciphers off を設定すると、クライアントの優先順位が適用されます。クライアントが弱いスイートを最優先に設定している場合、サーバーがそのスイートを有効にしていれば弱いスイートが選択されてしまいます。TLS 1.2 ではサーバー側で優先順位を制御する on が安全です。TLS 1.3 ではすべてのスイートが安全なため off で問題ありません。
OpenSSL 形式と IANA 形式の混同
nginx の ssl_ciphers は OpenSSL 形式(例: ECDHE-RSA-AES128-GCM-SHA256)を使います。IANA 形式(例: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)をそのまま設定しても認識されず、意図しないスイートが有効になるか、すべてのスイートが無効になって接続不能になります。openssl ciphers -v コマンドで設定文字列を検証できます。
# 設定文字列が展開される暗号スイートの一覧を確認
openssl ciphers -v 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
廃止されたアルゴリズムの時系列
暗号スイートに関連するアルゴリズムの廃止は段階的に進みました。
- 2011 年: BEAST 攻撃により CBC モード暗号の脆弱性が公表される
- 2013 年: Lucky Thirteen 攻撃が CBC モードの HMAC タイミングを突く
- 2015 年: RFC 7465 で RC4 が TLS での使用を禁止される
- 2016 年: Sweet32 攻撃により 64-bit ブロック暗号(3DES)の実用的な脆弱性が示される
- 2018 年: RFC 8446(TLS 1.3)が標準化され、RC4、3DES、CBC モード、RSA 鍵交換、静的 DH が仕様から削除される
- 2020 年: 主要ブラウザー(Chrome、Firefox、Safari、Edge)が TLS 1.0 / 1.1 を無効化する
- 2021 年: RFC 8996 で TLS 1.0 / 1.1 が正式に非推奨化される
TLS 1.3 への移行により、暗号スイートの選択肢は大幅に狭まりましたが、安全性の担保が仕様レベルで組み込まれた結果、設定ミスによる脆弱性のリスクも低減しています。