Closed207

『プロフェッショナルSSL/TLS』読書メモ

KIDANI AkitoKIDANI Akito

第1章 SSL/TLSと暗号技術

1.1 Transport Layer Security

p.1 セキュリティの効能

  • 正しいサーバとやりとりしている確認が持てる
  • 完全な状態で相手が情報を受け取れる

p.2 TLSの目標

  • 暗号学的なセキュリティ:二者間で安全な通信ができる(最優先)
  • 相互運用性:別のプログラム間で同じパラメータを使って暗号通信できる
  • 拡張性:新しい暗号アルゴリズムやハッシュ関数に対応しやすくする
  • 効率性:キャッシュにより他の三目標を妥当なパフォーマンスで達成する
KIDANI AkitoKIDANI Akito

第1章 SSL/TLSと暗号技術

1.2 ネットワークの階層

p.2 インターネットの根幹(IP+TCP)はセキュリティが考慮されていない、データを覗ける
p.2 DNSやBGPなど他のプロトコルも脆弱:意図しないコンピュータから返信を受け取りうる

※BGP
https://www.nic.ad.jp/ja/newsletter/No35/0800.html

  • パケットの経路制御のためのプロトコル
  • 経路制御プロトコル=EGP(Exterior Gateway Protocol)またはIGP(Interior Gateway Protocol)
  • EGP=インターネット上でやりとり、IGP=組織内でやりとり
  • BGPはEGPに分類される
KIDANI AkitoKIDANI Akito

p.2 トラフィックが正しい宛先に送られているか?=PKIで確認
p.3 OSIモデル
アプリケーション層:アプリデータ。 例)HTTP、SMTP、IMAP
プレゼンテーション層:データ表現、変換、暗号化。例)SSL/TLS
セッション層:複数のコネクションを管理
トランスポート層:パケット、ストリームの配送。例)TCP、UDP
ネットワーク層:経路制御、ノード間データグラム配送。例)IP、IPsec
データリンク層:LAN。例)Ethernet
物理層:ケーブル。例)CATS

※セッションより上は分類難。HTTP/2はセッション層にも踏み込んでいる。

p.3 HTTPはSSL/TLSなしでもTCPの上で動く。SSL/TLSはアプリケーション層の様々なプロトコルの暗号化に利用可能

KIDANI AkitoKIDANI Akito

1.3 プロトコルの歴史

p.3 SSLはNetscapeが開発、日の目を見ず
p.3 SSL2は1994リリース
p.3 SSL3.0は1995リリース
p.4 Microsoftが加わりTLS1.0として1999リリース
p.4 TLS1.1は2006リリース
p.4 TLS1.2は2008リリース

参考)TLS1.3は2018リリース

KIDANI AkitoKIDANI Akito

RFC 8996でTLS1.0とTLS1.1が廃止に

IETFで、TLS1.0とTLS1.1を正式に非推奨にする「RFC 8996 Deprecating TLS 1.0 and TLS 1.1」が公開されました。

新しいプロトコルへの以降期間は十分であるとし、TLS1.0, TLS1.1, DTLS1.0は廃止となり、TLS 1.2, TLS1.3, DTLS 1.2のみが使用できます。表現としても、MUST NOTで利用を禁止しています。

TLS 1.0 MUST NOT be used
TLS 1.1 MUST NOT be used
2015年に公開された、TLS利用時の推奨事項を定めたRFC7525がありますが、今回の禁止内容も含めて改定作業が開始されています。

KIDANI AkitoKIDANI Akito

1.4 暗号技術

p.4 数学を基盤とする暗号技術の解決する問題

  • 機密性(秘密が守られるか)
  • 真正性(本人である検証ができるか)
  • 完全性(データが改竄されないか)
KIDANI AkitoKIDANI Akito

1.4.1 要素技術

p.5 要素技術単体ではそれほど便利ではないが組み合わせることで強固なセキュリティを実現

■共通鍵暗号方式(対称暗号方式)
p.6 平文(plaintext)を暗号(cipher、アルゴリズムのこと)を利用して暗号文(ciphertext)にする
p.6 鍵のない置換暗号が数千年前から利用されてきた:安全ではない
p.6 Kerckhoffsの原則(ケルクホフスの原則):鍵以外の全てが攻撃者に掌握されても安全でなくてはならない

  • 暗号アルゴリズムは複数の相手と共有しなければ使えない
  • 他方で、たくさんの人の目にふれて精査されないと安全とは言えない

p.6 今日の暗号技術は計算量的安全性を根拠としている(↔︎情報理論的安全性もある)

KIDANI AkitoKIDANI Akito

p.7 暗号は大きく2種類、ストリーム暗号化とブロック暗号化

●ストリーム暗号化方式 例)RC4
p.7 1バイトずつ逐次暗号化/復号するような方式。排他的論理和を利用する。
p.7 ストリーム暗号は平文と暗号文が同じサイズになるため、HTTPリクエストなどリクエストメソッドやプロトコルバージョン、ヘッダー名などは位置が分かりやすいため、同じ鍵を使い回すと危険。通信のたびに一回限りの鍵を生成して利用する。

※RC4は鍵テーブルに統計的な偏りがあり平文の一部が回復可能なため脆弱であり、2015年にはRFC 7645でSSL/TLSでの利用禁止が提案された

※7.5節でRC4の弱点についてまとめられている(p.222-p.228)

●ブロック暗号化方式 例)AES
p.8 データをブロックごとにまとめて暗号化する。現在多くのブロックは128ビット(16バイト)。AESでは192ビット、256ビットも扱える。
p.8 入力が1ビット変わると出力が大きく変化する性質
p.8 欠点1:そのままでは任意の長さのデータを扱えない(ので後述のパディングを利用する)
p.8 欠点2:同じ入力・鍵については同じ結果となるので、様々な攻撃がありうる
p.8 上記の欠点については暗号化利用モードを利用し緩和

●パディング
p.8 ブロック長に満たないデータに余分なデータを追加する
p.8 TLSの場合、末尾にパディング長を示すバイトがあり、その直前に同じ個数だけ同じバイトを付加する

KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■ハッシュ関数
p.9 任意の長さの入力を固定長の出力(ハッシュ値=フィンガープリント=(メッセージ)ダイジェスト)へ変換するアルゴリズム
p.9 メリット:大きなデータを手軽に比較できる
p.9 暗号学的ハッシュ関数に求められる3つの性質

  • 原像計算困難性 Preimage resistance
  • 第2原像計算困難性 Second preimage resistance
  • 衝突耐性 Collision resistance
    p.9 例)SHA2, SHA3 ※SHA1、MD5は弱いので利用すべきでない
KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■MAC(メッセージ認証コード Message Authentication Code)
p.10 ハッシュ関数によるメッセージの完全性の検証にはハッシュ値の転送が必要だが、攻撃者により捏造されうる
p.10 MAC=鍵付きハッシュ(keyed-hash, keying-hash):ハッシュ関数を拡張して認証可能に。
p.10 暗号処理は機密性を、ハッシュ関数は完全性を与える
p.10 HMAC(Hash-based MAC)を使うとどんなハッシュ関数でもMACに使える

KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■MAC(メッセージ認証コード Message Authentication Code)
p.10 ハッシュ関数によるメッセージの完全性の検証にはハッシュ値の転送が必要だが、攻撃者により捏造されうる
p.10 MAC=鍵付きハッシュ(keyed-hash, keying-hash):ハッシュ関数を拡張して認証可能に。
p.10 暗号処理は機密性を、ハッシュ関数は完全性を与える
p.10 HMAC(Hash-based MAC)を使うとどんなハッシュ関数でもMACに使える

KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■暗号化利用モード
p.10 ブロック暗号化方式を拡張し、任意の長さのデータを暗号化できる。
p.10 ストリーム暗号化方式として使えるものもある。
p.10 ECB, CBC, CFB, OFB, CTR, GCMなどがある

GCM:認証つき暗号スイート。TLS1.2から利用可能
●ECBモード(Electronic Codebook最も単純な暗号化利用モード)
p.10 ブロック長の整数倍のデータにのみ対応=要パディング
p.10 ブロック暗号=決定論的(入力が同じなら結果も同じ)
p.11 さまざまな平文の暗号化を試すと暗号文から平文を推測できてしまう
p.11 TLSへのBEAST攻撃の原因

●CBCモード(Cipher Block Chaining)
p.11 ECBの決定論的性質への対処として、ランダムな初期化ベクター(IV, Initialization Vector)を導入:入力が同じでも出力が異なる
p.11 初期化ベクターと1つ目のブロックとの排他的論理和をとって暗号文ブロックとする
p.11 1つ目のブロックの暗号化の結果を次のブロックの初期化ベクターとして利用していく
p.11 初期化ベクターを受信側に送付することがポイント

KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■公開鍵暗号化方式
p.11 共通鍵のメリット:大きなデータを高速に扱える、デメリット:個別に鍵を用意すると危殆化しやすい、多数必要、同じ鍵の他の暗号文も危険
p.12 公開鍵暗号(非対称暗号化方式)は鍵を2種類使う:公開鍵と秘密鍵
p.12 公開鍵で暗号化したら秘密鍵で復号
p.12 秘密鍵で暗号化したら公開鍵で復号(例:ディジタル署名)
p.12 公開鍵のメリット:公開鍵をPKIの仕組みで広く安全に共有し、大きな集団での安全な通信を実現できる
p.12 公開鍵のデメリット:計算に時間がかかり容量が大きなデータには不向き→共有鍵のネゴシエーションや、認証に利用
p.12 例:RSA(2048ビットの鍵が推奨、2016年時点)

KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■ディジタル署名
p.12 電子的なメッセージの真正性(authenticity)を検証可能にするもの、例)MAC
p.13 MACの制約:事前にMAC鍵の共有が必要
p.13 公開鍵暗号方式を利用したディジタル署名:秘密鍵で署名して公開鍵で検証
p.13 RSA+ハッシュ関数のディジタル署名例

  1. 署名対象のハッシュ値を計算
  2. ハッシュ関数などのメタ情報を付加し符号化(エンコード)
  3. エンコードされたハッシュ値を秘密鍵で暗号化したものがディジタル署名。

p.13 署名検証手順は以下

  1. 同じハッシュ関数でハッシュ値を計算
  2. 公開鍵で署名から送り手の計算したハッシュ値を取得
  3. 1と2を比較
KIDANI AkitoKIDANI Akito

(1.4.1 要素技術)
■乱数生成器
p.13 秘密鍵=とても長い乱数;乱数生成器の品質が重要
p.13 予測可能な動作をするというコンピュータの特性が邪魔をする
p.14 無作為性(エントロピー)を集める:キー入力、マウスの動き、HDDの割り込み
p.14 上記をシード(種)として擬似乱数生成器(pseudo random number generator)を利用する
p.14 暗号学的PRNGには予測不可能性が必要:リバースエンジニアリングされないように

KIDANI AkitoKIDANI Akito

1.4.2 プロトコル
p.14 要素技術は機密性、完全性、真正性のいずれかを満たしてくれる:組み合わせてプロトコルを構成する
p.14 セキュリティプロトコルの最小構成の例

  • 機密性:AESを使ってメッセージを暗号化する
  • 完全性:当事者だけが知るハッシュ鍵でMACを計算し、メッセージと一緒に送る、連番つき、終了メッセージあり
  • 真正性:事前に公開鍵暗号化方式でお互いを認証(+データ交換のために必要な鍵を交換)
    p.15 ハンドシェイク:認証と鍵公開の一連のやりとり
KIDANI AkitoKIDANI Akito

1.4.3 暗号技術に対する攻撃

p.15 暗号技術は単純で用途特化:通常十分に問題点が解明されている <-> より複雑なスキーム、プロトコルの方が攻撃の余地が大きい
p.15 スキーム:要素技術の組み合わせ
p.15 プロトコルの実装への攻撃(ソフトウェアのバグ、脆弱性を悪用)もある
p.15 例)タイミング攻撃:処理時間を観察することで暗号を破られる
(参考:有名な例としてCPUの投機的実行に由来する2018年のMeltdown/Spectreがある)
(参考:Rebuild 200 34.37〜
p.15 暗号をバイパスする:サーバーに侵入して暗号鍵を入手すれば時間のかかる総当たり攻撃は不要
p.16 ポイント:自分でプロトコルやスキームを設計しない、暗号処理を実装しない、確立された暗号アルゴリズムを十分な鍵長で使う

KIDANI AkitoKIDANI Akito

1.4.4 暗号強度

p.16 暗号の強度=暗号アルゴリズムを破るのに必要な操作数で測定(ビット安全性)
p.16 128ビットの鍵=2^128の操作が必要:1ビット増やすと2倍強力になる
p.16 ENCRYPT2(2012)によれば、共通鍵暗号、楕円曲線暗号、ハッシュ値は256ビットでも長期間(30年)の攻撃に耐えうる:公開鍵暗号、DHは1776ビットでようやく短期間(10年)の攻撃に耐えられる

ディフィー・ヘルマン鍵共有(Wikipedia)
公開鍵暗号の一種。RSAより処理負荷高い。認証がなく中間者攻撃に脆弱、2015年には事前計算を利用したLogjam攻撃が明らかに。詳細は本書p.167(6.5. Logjam)。

p.16 現在のセキュリティだけでなく、長期間のセキュリティも考慮すべし:コンピュータが高速になり廉価になれば暗号の強度は変化する

※等価安全性について SSL/TLS暗号設定ガイドライン by IPA p.17

KIDANI AkitoKIDANI Akito

1.4.5 MITM攻撃(中間者攻撃、man-in-the-middle)

p.17 受動的ネットワーク攻撃:第三者が会話を聞いているだけ
p.18 能動的ネットワーク攻撃:通信内容を操作したり当事者間の会話に影響を与える

■アクセスの奪取
p.18 攻撃の前提条件:犠牲者、サーバ、通信インフラへの接近・接続
p.18 通信会社と結託した事例:おそらくアメリカ国家安全保障局(National Security Agency)とAT&T。脚注31のサイトはリンク切れだったのでNY Timesなどが参考になる)
p.18 通信機器を乗っ取った事例:光ファイバーケーブルを盗聴するイギリスの政府通信本部(GCHQ、Government Communications Headquarters)によるTEMPORAプログラム。

※NSAおよびCIAの元局員エドワード・スノーデンによるこれらの告発は2013年6月に行われた

  • ARPスプーフィング:MACアドレスとIPアドレスを関連づけるアドレス解決プロトコル(Address Resolution Protocol)を利用してLAN上で通信機器のなりすましを行う
  • WPADハイジャック:ブラウザのWebプロキシ自動発見(WPAD)を利用して、攻撃者がローカルでプロキシを起動してMITM攻撃を行う。2007年にIEの脆弱性があった
  • DNSハイジャック:ドメインの管理権限のない第三者が不正にドメインをのっとる
  • DNSキャッシュポイズニング:権威サーバーへのDNSメッセージの32ビットのIDベースでのリクエストに対する不正なレスポンスを送り込むことで、ドメイン名に対応する偽のIPをキャッシュさせる。DNSサーバーの脆弱性を悪用。カミンスキーアタックなどが知られる。対応策としてソースポートのランダム化がある。
  • BGP経路ハイジャック:ISPの相互接続などで利用するルーティングプロトコルであるBGPで、不正な経路を広告することで中間者攻撃を実現する。2018年に中国政府がアメリカの通信を傍受していた事例

■受動的攻撃
p.19 トラフィックが暗号化されていない場合に最も有効
p.19 RFC7258:「蔓延する監視は攻撃だ」(2014年5月)
p.19 暗号化されていても、暗号を破れるようになるまで保存することで受動的攻撃が成立しうる
p.20 前方秘匿性(perfect forward secrecy):長期的な鍵(long-term secrets、TLSでいうとサーバーの秘密鍵)からセッションキーを生成して通信を行うが、長期的な鍵が流出した場合でもセッションキーの安全性が保たれるという性質のこと。RSAをベースにしたTLSの鍵交換アルゴリズムではPFSを欠いており、RSA鍵を手に入れれば以前の通信が復号可能。2014年にはHeartbleed脆弱性により秘密鍵流出が問題に。(詳細は6.3, p.180)

■能動的な攻撃
p.20 有効なものとして受け取れる証明書を攻撃者が提示できる(詳細は第4章、Microsoft社を騙り証明書入手した事例など)
p.20 ブラウザの検証のバグをついて、有効に見える証明書を構成する(詳細は第6章、OSのAPIに問題がある場合も)
p.20 不正な証明書でも、エンドユーザーがブラウザの警告をうっかり無視することを期待して攻撃に利用するケースも。2011年シリアで電気通信省が行った?FacebookへのMITM攻撃の事例
p.20 ブラウザが能動的攻撃のターゲットとなることが多い
p.21 能動的攻撃は大規模には仕掛けにくい:主に重要人物に対して仕掛けられる
p.21 QuantumInsertプログラム:NSAとGCHQによる中間者攻撃の手法。本来のページより高速なレスポンスを返すサーバーを使い、不正なページにアクセスさせマルウェアをダウンロードさせる。LinkedInなどが狙われた。

KIDANI AkitoKIDANI Akito

第2章 プロトコル

p.23 TLSは接続指向のネットワーク通信を安全にするための暗号化プロトコル:上位層のプロトコルはなんでもよい

※接続指向:データを確実に相手に送信できる。TCP、SCTPなど。UDPは接続指向ではない。
SCTP:Stream Control Transmission Protocol(ストリーム制御伝送プロトコル)。電話網のプロトコルをIP上で転送するために作られた。LTEで利用。

NTTドコモ テクニカルレポート

KIDANI AkitoKIDANI Akito

2.1 Record プロトコル

p.24 TLSレコードがコネクション上でやりとりされる低レベルのメッセージ転送すべてを担う
p.24 TLSレコード=ヘッダ+メッセージデータ
ヘッダは以下の3つから成る。

  • コンテントタイプ:サブプロトコルのいずれかを示す以下の値。 20(Change Cipher Spec)、21(Alert)、22(Handshake)、23(Application Data)。Java実装
  • バージョン:majorバージョンとminorバージョン。それぞれuint8型(8 ビットの符号なし整数、0-255)。 TLS1.2の場合 { 3, 3 }(TLS1.0が{ 3, 1 }、TLS1.3は{ 3, 4 })。Java実装
  • レコード長:uint16型。最長で2^14バイト(16,384バイト)。
    p.24 上記のほか、64ビットのシーケンス番号が割り当てられ、通信両側がそれぞれ記録する。送受信されるデータには含まれない。リプレイ攻撃への防御として利用。Java実装

●メッセージの転送
p.25 書式が定まっていない(opaque)データのバッファを転送する
p.25 レコードは最長16,384バイト:それより長い場合、分割される。逆に単一のレコードにまとめられることもある。

●暗号化および完全性の検証
p.25 最初の接続におけるメッセージ群は保護されない(厳密にはTLS_NULL_WITH_NULL_NULL暗号スイートが利用される)
p.25 暗号スイートによっては暗号化を利用せず完全性の検証のみを行うものもある

●圧縮
p.25 TLSの圧縮機能は現在では利用されていない(2012年にCRIME攻撃に利用されたため(詳細は7.3))
p.25 HTTPレベルですでに圧縮されている

●拡張性
p.25 Recordプロトコルは暗号処理とデータ転送:他の機能はサブプロトコルで行う
p.25 サブプロトコルはネゴシエーションされたパラメータで自動的に暗号化され保護される
p.25 4つのサブプロトコル:Handshake, Change Cipher Spec, Application Data, Alert

KIDANI AkitoKIDANI Akito

2.2 Handshakeプロトコル

p.25 TLS接続で使うパラメータのネゴシエーションと認証を行うプロトコル
p.25 通信確立=ハンドシェイクには6〜10のメッセージが必要(利用機能に応じて異なる)
p.25 実用的には3つ

  • サーバ認証を伴うフルハンドシェイク
  • 前回のセッションを再開する場合の一部メッセージを省略したハンドシェイク
  • クライアントとサーバの認証を伴うハンドシェイク

p.26 Handshakeプロトコルのメッセージの先頭のヘッダ

  • メッセージの種類(HandshakeType):1バイト。Java実装
  • メッセージの長さ:3バイト(uint24)
enum {
  hello_request(0),
  client_hello(1),
  server_hello(2), 
  certificate(11),
  server_key_exchange (12), 
  certificate_request(13),
  server_hello_done(14), 
  certificate_verify(15),
  client_key_exchange(16), 
  finished(20),
  (255)
} HandshakeType;
KIDANI AkitoKIDANI Akito

2.2.1 フルハンドシェイク

p.26 TLS Sessionを確立するためにフルハンドシェイクを実行

  1. 接続で利用したいパラメータを双方が提示・合意
  2. 認証:証明書or他の方法
  3. セッション保護に利用するマスターシクレットを共有
  4. ハンドシェイクメッセージ群が第三者に書き換えられていないことの検証

p.26 上記の2と3は実際には1ステップで行う=鍵交換(key exchange)

p.27 フルハンドシェイクの10ステップ

  1. ClientHello (C->S):新規ハンドシェイクをクライアントが開始。暗号スイートや鍵交換方法のパラメータ群を送信。
  2. ServerHello (C<-S):サーバがパラメータを決定
  3. Certificate* (C<-S):サーバの証明書チェーンを送信(サーバ認証が必要な場合のみ)
  4. ServerKeyExchange* (C<-S):マスターシークレット生成に必要な情報がある場合これを送信
  5. ServerHelloDone (C<-S):サーバの番が終わったことを通知
  6. ClientKeyExchange (C->S):マスターシークレット生成に必要な情報を送信
  7. ChangeCipherSpec (C->S):クライアントが暗号通信に切り替え、そのことをサーバに通知
  8. Finished (C->S):ハンドシェイクメッセージのMACを送る(改竄検知のため)
  9. ChangeCipherSpec (C<-S):サーバが暗号通信に切り替え、そのことをクライアントに通知
  10. Finished (C<-S):ハンドシェイクメッセージのMACを送る(改竄検知のため)

p.27 何もエラーがなければこの後アプリケーションデータを送信する

KIDANI AkitoKIDANI Akito

2.2.1 フルハンドシェイク

■ClientHello
p.27 クライアントが希望するパラメータ群とその優先度を伝えるメッセージ
p.27 以下の3つの場合に送信

  • 新規にコネクションを開始するとき
  • 再ネゴシエーションをしたいとき
  • サーバからの再ネゴシエーション要求(HelloRequest)に応えるとき
    p.28 Versionフィールド:クライアントがサポートする最良のバージョンを表す(RFCの記載ではclient_versionフィールド)
    p.28 Randomフィールド
  • 32バイト=4バイトのクライアント時刻情報+28バイトのランダムな値。16進数表記になっている(4ビットで1文字)ので56文字。
  • クライアント時刻情報は1994年のNetscape Navigatorの脆弱性以降非推奨となり、ランダム値が送信されることもある。
  • この乱数が完全性検証における重要な役割をもつ(リプレイ攻撃も防止)
    p.29 Session IDフィールド:ランダムな32バイトが含まれる(意味はない)。初回接続時は空。再ネゴシエーション時は一意の識別子が含まれサーバがセッションをキャッシュするのに利用。
    p.29 Cipher Suitesフィールド:クライアントの対応可能な暗号スイートが適切な順番で格納される
    p.29 Compression methodsフィールド:クライアントの対応している圧縮方法。デフォルトはnullで圧縮しない(前述のCRIME攻撃の脆弱性のため(p.25))
    p.29 Extensionsフィールド:拡張情報が任意の数含まれる。server_nameやelliptic_curvesなど。詳細は後述。
struct {
     ProtocolVersion client_version;
     Random random;
     SessionID session_id;
     CipherSuite cipher_suites<2..2^16-2>;
     CompressionMethod compression_methods<1..2^8-1>;
     select (extensions_present) {
         case false:
             struct {};
         case true:
             Extension extensions<0..2^16-1>;
     };
} ClientHello;
KIDANI AkitoKIDANI Akito

■ServerHello

p.29 サーバが接続で使うパラメータを選択して返答する:構造はClientHelloと同じ
p.29 クライアントと同じ最新バージョンに対応している必要はなく、別の下位バージョンを提案することもある

※クライアントがサポートしていない場合、JavaだとSSLHandshakeExceptionが発生する

KIDANI AkitoKIDANI Akito

■Certificate

p.29 主に、サーバからクライアントにX.509証明書チェーンを運ぶ
p.29 証明書チェーン:ASN.1のDER(Distinguished Encoding Rules)を使用してエンコードされた証明書を順番に並べたもの(詳細は3.4を参照)

X.509:ITU-Tの公開鍵基盤 (PKI)の規格。公開鍵証明書の標準形式や証明書パス検証アルゴリズムなどを定めている。

https://www.ipa.go.jp/security/pki/033.html

ITU-T(International Telecommunication Union Telecommunication Standardization Sector):世界規模で電気通信を標準化することを目的として勧告Recommendationsを作成する国連機関。アルファベット1文字で○シリーズ勧告がある(Aシリーズ、Bシリーズなど)。Xシリーズ勧告は「データ網及びオープン・システム・コミュニケーション」に関する規定。ITU-Tで公開されているX.509標準の目次

DER:ITU-TのX.690標準で定められているASN.1符号化規則の一つ。X.690 は、基本符号化規則(BER、Basic Encoding Rules)と 2 つのサブセット、標準符号化規則(CER、Canonical Encoding Rules)と識別符号化規則(DER)を定義する。
CISCOの解説

例: DER では、20 ビット値 1010 1011 1100 1101 1110 が、次のように符号化されます。
タグ: 0x03(ビット列)
長さ: 0x04(バイト)
値: 0x04ABCDE0
DER 符号化全体: 0x030404ABCDE0

ASN.1:Abstract Syntax Notation One(抽象構文記法1)。ASN.1記法とASN.1符号化(エンコード)規則を組み合わせて使う。

p.30 サーバ証明書が先頭、中間証明書が続く。ルート証明書は省くべきとされている。
p.30 サーバには複数の証明書を設定できる:選択された暗号スイートによって、利用できる証明書の公開鍵アルゴリズムが変わるため
p.30 Certificateメッセージは必須ではない:証明書を必要としない認証方式、認証を利用しない暗号スイートもあるため(DH_anonなど)。
p.30 X.509でなくPGP鍵を用いる暗号スイートもある

RFCで定義されている構造体

      opaque ASN.1Cert<1..2^24-1>;
      struct {
          ASN.1Cert certificate_list<0..2^24-1>;
      } Certificate;
KIDANI AkitoKIDANI Akito

■ServerKeyExchange

p.30 目的:鍵交換に必要な付加的データを運ぶ
p.30 中身は暗号スイートによって異なる、送られない場合もある。

鍵交換手法のうち、以下の場合にServerKeyExchangeが送られる

  • DHE_DSS
  • DHE_RSA
  • DH_anon

下記の鍵交換手法の場合にはServerKeyExchangeが送られない

  • RSA
  • DH_DSS
  • DH_RSA

※鍵交換手法についてはRFC2246を参照

      enum { dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa
            /* 拡張される可能性がある。(例:ECDH について [TLSECC] 参照)*/
           } KeyExchangeAlgorithm;
      struct {
          opaque dh_p<1..2^16-1>;
          opaque dh_g<1..2^16-1>;
          opaque dh_Ys<1..2^16-1>;
      } ServerDHParams;     /* 超短期(Ephemeral)DH パラメータ */
      dh_p
         The prime modulus used for the Diffie-Hellman 処理。
      dh_g
         The generator used for the Diffie-Hellman 処理。
      dh_Ys
         サーバの Diffie-Hellman 公開値 (g^X mod p)。
      struct {
          select (KeyExchangeAlgorithm) {
              case dh_anon:
                  ServerDHParams params;
              case dhe_dss:
              case dhe_rsa:
                  ServerDHParams params;
                  digitally-signed struct {
                      opaque client_random[32];
                      opaque server_random[32];
                      ServerDHParams params;
                  } signed_params;
              case rsa:
              case dh_dss:
              case dh_rsa:
                  struct {} ;
                 /* メッセージは、rsa、dh_dss について省略される。*/
              /* そして、dh_rsa は、拡張される可能性がある。(例:ECDH について [TLSECC] 参照) */
          };
      } ServerKeyExchange;
      params
         そのサーバの鍵交換パラメータ。
      signed_params
         非匿名鍵交換用に、サーバの鍵交換パラメータ上の署名。
KIDANI AkitoKIDANI Akito

■ServerHelloDone

p.30 予定していたハンドシェイクメッセージをすべて送信したときにサーバから送る合図
p.30 サーバが待機状態に入る

※ServerHello および関連するメッセージの終りを示す。
※クライアントは、「そのサーバは、(要求される場合)有効な証明書を提供したか?」を検証する。

構造体

struct { } ServerHelloDone;
KIDANI AkitoKIDANI Akito

■ClientKeyExchange

p.30 鍵交換に必要な情報をクライアントから送信するための必須のメッセージ
p.30 中身は暗号スイートによって異なる。

構造体

struct {
    select (KeyExchangeAlgorithm) {
        case rsa:
            EncryptedPreMasterSecret;
        case dhe_dss:
        case dhe_rsa:
        case dh_dss:
        case dh_rsa:
        case dh_anon:
            ClientDiffieHellmanPublic;
        case ecdhe:
            ClientECDiffieHellmanPublic;
    } exchange_keys;
} ClientKeyExchange;
KIDANI AkitoKIDANI Akito

■ChangeCipherSpec

p.30 クライアント、サーバ両方が送信する
p.30 このメッセージの意味は以下の通り

  • 接続で使うパラメータを組み立てるのに十分な情報を手に入れたこと
  • 暗号鍵を生成したこと
  • これから暗号処理へ移行すること

p.30 サブプロトコルChangeCipherSpecのメッセージ。
p.30 これはハンドシェイクメッセージではない:完全性の検証対象外。ゆえにOpenSSLでは攻撃を受けやすい状態になっていた(2014年)。

※このメッセージは、値が 1 の 1byte から成る。

構造体

   struct {
       enum { change_cipher_spec(1), (255) } type;
   } ChangeCipherSpec;
KIDANI AkitoKIDANI Akito

■Finished

p.31 ハンドシェイクが完了したという合図:このメッセージ以前にApplication Dataプロトコルのメッセージを送信することは認められていない(攻撃を避けるため)

構造体

struct {
    opaque verify_data[verify_data_length];
} Finished;

verify_data
   PRF(master_secret, finished_label, Hash(handshake_messages))
      [0..verify_data_length-1];

p.31 verify_dataフィールド:受信した全ハンドシェイクメッセージをハッシュかしてマスターシークレットと合わせて計算した値
p.31 finished_label:クライアント側は「client finished」、サーバ側は「server finished」
p.31 このメッセージは暗号化されている:ネゴシエーション済みのMACで完全性保証[1]。=マスターシークレットとハンドシェイクメッセージのハッシュ値を知るのは非常に困難。
p.31 TLS1.2のFinishedメッセージ、つまりverify_dataのデフォルトの長さは12バイト(96ビット)。暗号スイートによって変わる[2][3]

RFC5246
ハンドシェイクメッセージとは?

  • HelloRequest以外のメッセージ、このFinishedメッセージまで(このメッセージ自身は含まない)のすべてのデータ
  • レコード層のヘッダは含まない
  • ※ChangeCipherSpecの節で見た通り、ChangeCipherSpecはハンドシェイクメッセージに含まれない。
    https://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-5c5c5f435668d1
  • しかし、Finishedメッセージはハンドシェイクメッセージに含まれるので、後から送られるFinishedメッセージのハッシュには先に送られたFinishedメッセージが含まれる
脚注
  1. これってどういうこと? ↩︎

  2. SSL3.0では合計36バイト, TLS1.0TLS1.1では12バイトだった ↩︎

  3. 変わることは変わるらしいが、具体的にどの暗号スイートがいくつなのか不明。stackoverflowJava8の初期実装では12バイトとして扱われている。Java16ではTLS1.3のときに変更になりverifyDataLen = context.negotiatedCipherSuite.hashAlg.hashLength;とある。CipherSuiteのHashAlg定義によれば、SHA-256のとき32、SHA-384のとき48。 ↩︎

KIDANI AkitoKIDANI Akito

2.2.2 クライアント認証

p.31 クライアントとサーバの相互認証は必須ではない
p.32 サーバがクライアント認証を求めるときCertificateRequestメッセージを送る
p.32 クライアントはCertificateメッセージとCertificateVerifyメッセージを送る

p.32 相互認証フルハンドシェイクの13ステップ

  1. ClientHello (C->S):新規ハンドシェイクをクライアントが開始。暗号スイートや鍵交換方法のパラメータ群を送信。
  2. ServerHello (C<-S):サーバがパラメータを決定
  3. Certificate* (C<-S):サーバの証明書チェーンを送信(サーバ認証が必要な場合のみ)
  4. ServerKeyExchange* (C<-S):マスターシークレット生成に必要な情報がある場合これを送信
  5. CertificateRequest (C<-S):クライアントに認証を要求
  6. ServerHelloDone (C<-S):サーバの番が終わったことを通知
  7. Certificate (C->S):クライアントの証明書を送信
  8. ClientKeyExchange (C->S):マスターシークレット生成に必要な情報を送信
  9. CertificateVerify (C->S):秘密鍵でハンドシェイクメッセージに署名して送信
  10. ChangeCipherSpec (C->S):クライアントが暗号通信に切り替え、そのことをサーバに通知
  11. Finished (C->S):ハンドシェイクメッセージのMACを送る(改竄検知のため)
  12. ChangeCipherSpec (C<-S):サーバが暗号通信に切り替え、そのことをクライアントに通知
  13. Finished (C<-S):ハンドシェイクメッセージのMACを送る(改竄検知のため)
KIDANI AkitoKIDANI Akito

■CertificateRequest

構造体

      enum {
          rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
          rsa_ephemeral_dh_RESERVED(5), dss_ephemeral_dh_RESERVED(6),
          fortezza_dms_RESERVED(20), (255)
      } ClientCertificateType;
      opaque DistinguishedName<1..2^16-1>;

      struct {
          ClientCertificateType certificate_types<1..2^8-1>;
          SignatureAndHashAlgorithm
            supported_signature_algorithms<2^16-1>;
          DistinguishedName certificate_authorities<0..2^16-1>;
      } CertificateRequest;

p.32 certificate_types:許容できる証明書(クライアントが提供してよい)の公開鍵を示す。

  • rsa_sign:RSA 鍵を含む証明書
  • dss_sign:DSA 鍵を含む証明書
  • rsa_fixed_dh:無期の(static)DH 鍵を含む証明書
  • dss_fixed_dh:無期の(static)DH 鍵を含む証明書

p.32 supported_signature_algorithms:サーバが検証できるハッシュ/署名アルゴリズムペアのリスト
p.32 certificate_authorities:許容できるCA(認証局)の識別名DistinguishedNameのリスト

KIDANI AkitoKIDANI Akito

■CertificateVerify

構造体

struct {
     digitally-signed struct {
         opaque handshake_messages[handshake_messages_length];
     }
} CertificateVerify;

p.33 これまでにやりとりした全ハンドシェイクメッセージの署名Signature

RFC5246より

  • 署名において使われるハッシュアルゴリズムおよび署名アルゴリズムは、CertificateRequest メッセージの supported_signature_algorithms フィールド中にあるもののひとつでなければならない
  • そのハッシュアルゴリズムおよび署名アルゴリズムは、クライアントのエンド主体証明書中の鍵と互換性がなければならない[1]
脚注
  1. RSAが署名に使われるなら、証明書の公開鍵もRSA、ECDSAで署名するなら証明書の鍵もECDSAということか?[1] ↩︎

KIDANI AkitoKIDANI Akito

2.2.3 セッションリザンプション

p.33 フルハンドシェイクはコストがかかる

  • アプリケーションデータ送信までにクライアント・サーバ間のやりとりが2往復必要
  • CPU負荷の高い暗号処理が何度も必要
  • 認証時の証明書検証はさらに負荷が高い

p.33 Session IDを利用してセッションを再開可能にする
p.33 以前共有したマスターシークレット(ClientKeyExchangeで共有したプリマスターシークレットから生死したもの)から、新たな暗号鍵を生成して通信に利用する

  1. ClientHello (C->S):以前のセッション再開リクエストをクライアントが送信
  2. ServerHello (C<-S):サーバが同一のSession IDを送信
  3. ChangeCipherSpec (C<-S):サーバが暗号通信に切り替え、そのことをクライアントに通知
  4. Finished (C<-S):ハンドシェイクメッセージのMACを送る(改竄検知のため)
  5. ChangeCipherSpec (C->S):クライアントが暗号通信に切り替え、そのことをサーバに通知
  6. Finished (C->S):ハンドシェイクメッセージのMACを送る(改竄検知のため)

p.34 HTTPクッキーに似たセッションチケットの仕組みもある:こちらはクライアントがすべての情報を保持する。サーバがセッション情報を管理しなくてもよい。チケットはサーバの公開鍵で暗号化されている。
TLS Session resumptionに関する英語のブログより

※TLS1.3ではPSK(Pre-Shared Keys、事前共有鍵)を用いてセッションリザンプションを実現する
TLS Session resumptionに関する英語のブログより

KIDANI AkitoKIDANI Akito

2.3 鍵交換

p.34 鍵交換の目的:プリマスターシークレットを生成し共有する
p.34 プリマスターシークレットからマスターシークレット(48バイト)を構成する
p.34 マスターシークレットがTLSセッションのセキュリティを支えている
p.34 よく利用される鍵交換アルゴリズム

鍵交換 説明
dh_anon 認証なしDHカギコウカン
dhe_rsa RSA認証を伴う一時的DH鍵交換
ecdh_anon 認証なしECDH鍵交換(RFC4492)[1]
ecdhe_rsa RSA認証を伴う一時的ECDH鍵交換(RFC4492)
ecdhe_ecdsa ECDSA認証を伴う一時的ECDH鍵交換(RFC4492)
krb5 Kerberos鍵交換(RFC2712)
rsa RSA鍵交換および認証
psk PSK(Pre-Shared Key)鍵交換および認証(RFC4279)
dhe_psk PSK認証を伴う一時的DH鍵交換(RFC4279)
rsa_psk PSK鍵交換およびRSA認証(RFC4279)
srp SRP鍵交換および認証(RFC5054)(Secure Remote Password)[2]

p.34 よく利用される4つ:RSA, DHE_RSA, ECDHE_RSA, ECDHE_ECDSA

脚注
  1. テキスト本文ではECDHE鍵交換となっているが誤記と思われる ↩︎

  2. SRP自体はRFC2945で定められている。Wikipediaによると、辞書攻撃に強く、信頼された第三者機関が必要ない。2021年現在のバージョンは6a。 ↩︎

KIDANI AkitoKIDANI Akito

●RSA鍵交換アルゴリズム

p.34 事実上の標準ともいえる
p.34 深刻な設計上の問題:サーバの秘密鍵が漏洩すると過去のやりとりもすべて復号可能(cf. Heartbleed)
p.35 従って、前方秘匿性のある他のアルゴリズムに置き換えが進んでいる
p.35 クライアントがプリマスターシークレットを生成、サーバの公開鍵で暗号化してClientKeyExchangeメッセージで送信
p.35 鍵転送のアルゴリズム

KIDANI AkitoKIDANI Akito

●DHE_RSA

p.35 一時的DH(Ephemeral Diffie-Hellman)の長所は前方秘匿性があること、短所は遅いこと
p.35 サーバとクライアント双方が処理に参加して共通の鍵を決定する鍵合意のアルゴリズム

KIDANI AkitoKIDANI Akito

●ECDHE_RSAおよびECDHE_ECDSA

p.35 一時的楕円曲線Diffie-Hellman(Ephemeral elliptic curve-):鍵合意アルゴリズム
p.35 処理が高速かつ前方秘匿性がある
p.35 現代的なクライアントでは広くサポートされている[1]

脚注
  1. ブラウザが対応するSSL/TLS CipherSuite一覧によると2010年時点でもそこそこサポートされている ↩︎

KIDANI AkitoKIDANI Akito

p.35 鍵交換はServerKeyExchangeメッセージから開始
p.35 rsa、dh_rsaの場合にはServerKeyExchangeメッセージは送られない=必要な情報は他で手に入る
p.35 鍵交換パラメータとともに署名も送られる(認証方法がanonでない場合のみ、サーバの証明書の秘密鍵で署名):クライアントはサーバの真正性を検証できる

p.35 ClientKeyExchangeメッセージは常に必須

KIDANI AkitoKIDANI Akito

2.3.1 RSA鍵交換

p.36 クライアントが48バイトの乱数=プリマスターシークレットを生成、サーバの公開鍵で暗号化、ClientKeyExchangeメッセージで送信
p.36 TLSで利用されるRSA暗号化スキームはRSAES-PKCS1-v1_5(RFC3447 Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1[1]
p.36 前方秘匿性がない:簡単さゆえに、サーバの秘密鍵が漏洩するとプリマスターシークレットも漏洩し、セッションが乗っ取られる(リアルタイムでなくても通信を記録しておけばよい)

脚注
  1. 署名のためのスキームはRSASSA-PKCS1-v1_5など。RFC 8017 PKCS #1: RSA Cryptography Specifications Version 2.2で改訂されているが暗号スキームの方は変わってなさそう ↩︎

KIDANI AkitoKIDANI Akito

2.3.2 Diffie-Hellman鍵交換

p.37 安全でない通信路で共有の秘密鍵を確立するためのアルゴリズム:能動的攻撃(通信経路ののっとり)には脆弱なので認証と合わせて利用するのが一般的
p.37 逆方向の計算が非常に困難な数学の関数を利用:以下は簡易的な計算例[1]

dh_g 99
dh_p(素数) 13
dh_Ys 5
dh_Yc 6

S=dh_g^dh_Ys mod dh_p= 9509900499 mod 13=8
C=dh_g^dh_Yc mod dh_p=941480149401 mod 13 =12

C^dh_Ys mod dh_p = 248832 mod 13 =12
S^dh_Yc mod dh_p = 262144 mod 13 = 12

p.37 dh_g とdh_pをドメインパラメータ(またはDHパラメータ、グループパラメータ)と呼ぶ:サーバが選択し、ServerKeyExchangeメッセージで送信される
p.37 DHE(一時的DH、Ephemeral DH):前回のDH鍵交換で利用したパラメータを再利用しない
p.37 サーバおよびクライアント証明書に静的に埋め込まれたパラメータでDH鍵交換を行うこともできるが、同一鍵が使いまわされるため前方秘匿性がないため、TLSではまず使われない

DH鍵交換の問題点
p.38 DHパラメータのセキュリティ:サーバが選ぶパラメータの品質に依存する。弱いDHパラメータの場合、再ネゴシエーション時の脆弱性をつくトリプルハンドシェイク攻撃が可能(詳細は7.6節で後述)
p.38 DHパラメータ強度のネゴシエーションができない:DHパラメータがクライアントに受け入れられることを期待するしかない
p.38 不十分なDHパラメータ強度:2048ビット以上が強いが、かつては512ビットを利用しているサーバも珍しくなかった。2015年のLogjam攻撃は512ビットのDHパラメータをリアルタイムで破れることが判明。詳細は6.5節。

p.38 ネゴシエーションが可能になれば解消されるはず:テキスト脚注22のドラフトは2016年8月にRFC7919としてまとめられた[2]

脚注
  1. 実際にはもっと大きな値が利用される。参考:RFC3562 ↩︎

  2. Qiita TLSとDiffie-Hellmanグループパラメータ ↩︎

KIDANI AkitoKIDANI Akito

2.3.3 楕円曲線Diffie-Hellman鍵交換

p.39 ECDHE(一時的楕円曲線DH)の数学的基盤は楕円曲線[1]暗号[2]

p.39 理論上、一時的でない楕円曲線DHもあるが実際には利用されない
p.39 利用する楕円曲線を定義するのはサーバの役目(DHにおけるドメインパラメータと同じ役割):ServerKeyExchangeメッセージで送信(RFC8422で以下のように拡張されている)。

struct


      select (KeyExchangeAlgorithm) {
          ..(省略)..
          case ec_diffie_hellman:
              ServerECDHParams    params;
              Signature           signed_params;
      } ServerKeyExchange;


      enum { ecdsa } SignatureAlgorithm;  
      select (SignatureAlgorithm) {
          case ecdsa:
              digitally-signed struct {
                  opaque sha_hash[sha_size];
              };
      } Signature;
      ServerKeyExchange.signed_params.sha_hash
           SHA(ClientHello.random + ServerHello.random + ServerKeyExchange.params);

      struct {
           ECParameters    curve_params;
           ECPoint         public;
      } ServerECDHParams;

       struct {
           opaque point <1..2^8-1>;
       } ECPoint;

       struct {
           ECCurveType    curve_type;
           select (curve_type) {
               case named_curve:
                   NamedCurve namedcurve;
           };
       } ECParameters;

       enum {
           deprecated (1..2),
           named_curve (3),
           reserved(248..255)
       } ECCurveType;


       enum {
           deprecated(1..22),
           secp256r1 (23), secp384r1 (24), secp521r1 (25),
           x25519(29), x448(30),
           reserved (0xFE00..0xFEFF),
           deprecated(0xFF01..0xFF02),
           (0xFFFF)
       } NamedCurve;

楕円曲線

p.39 上記のようにサーバは名前つき曲線を指定する:これを受けてクライアントは以下のように公開パラメータを送信、その後それぞれがプリマスターシークレットを計算

       struct {
           select (KeyExchangeAlgorithm) {
               case ec_diffie_hellman: ClientECDiffieHellmanPublic;
           } exchange_keys;
       } ClientKeyExchange;

       struct {
           ECPoint ecdh_Yc;
       } ClientECDiffieHellmanPublic;

p.39 クライアントが対応する曲線をTLS拡張 elliptic_curvesで伝達する(ClientHelloメッセージ。RFC8422)詳細は2.12.3節。

脚注
  1. y^2=x^3+ax+bという方程式で定義される平面曲線としてあらわされる。Wikipedia ↩︎

  2. 楕円曲線上の離散対数問題 (EC-DLP) の困難性を安全性の根拠とする暗号。離散対数を計算する問題は整数の因数分解と以下の点が共通している:
    1.両方とも難しい(量子コンピュータ以外では効率的に解くアルゴリズムが得られていない)
    2.片方に対するアルゴリズムはしばしばもう片方にも利用できる
    3.問題の困難性が暗号系の構築に利用されている ↩︎

  3. https://asecuritysite.com/encryption/go_x448 ↩︎

KIDANI AkitoKIDANI Akito

2.4 認証

p.39 TLSでは認証と鍵交換が一体化:コストがかかる暗号処理の回数を減らすため
p.39 基本は証明書を利用した公開鍵暗号方式

RSAの場合
p.40 クライアントがランダム値を生成(=プリマスターシークレット):サーバの公開鍵で暗号化して送信(ClientKeyExchangeメッセージ)
p.40 秘密鍵をもつサーバだけがClientKeyExchangeメッセージを復号でき、マスターシークレットを生成して正しいFinishedメッセージを生成できる:暗黙的に認証完了

DHEおよびECDHEの場合
p.40 サーバの秘密鍵でパラメータに署名:クライアントは証明書から公開鍵を取得して、パラメータの出自を検証する
p.40 2.3.3 ECDHでみたように、パラメータ署名はClientHelloのRandomとServerHelloのRandomを利用する=ハンドシェイクごとに異なる。暗号化されずに署名が送られるが、再利用されることはない。ただし、弱い鍵の場合は事前計算によりLogjam攻撃が可能。

KIDANI AkitoKIDANI Akito

2.5 暗号化

p.40 多様な暗号化アルゴリズムでアプリケーションデータを暗号化:AES, 3DES, ARIA, CAMELLIA, RC4, SEEDなど

2.5.1 ストリーム暗号化方式

p.40 暗号化は2段階

  1. シーケンス番号+Recordヘッダ+平文を結合しMAC計算
  2. MACと平文を暗号化

p.40 RecordヘッダがMACに含まれる=暗号化されていないRecordヘッダが改竄されていないことを保障
p.40 シーケンス番号がMACに含まれる=リプレイ攻撃がないことを保障

2.5.2 ブロック暗号化方式

p.41 暗号化は5段階

  1. シーケンス番号、Recordヘッダ、平文データのMAC計算
  2. 暗号化前のデータに対しパディングを作る(通常16バイトの整数倍)
  3. 暗号化ブロックと同じ長さで初期化ベクター(IV)を生成(同じ平文でも違う暗号文を得るため)
  4. 平文データ、MAC、パディングをCBCモードで暗号化(Cipher Block Chaining、p.11
  5. IVと暗号文を一緒に送る

p.41 上記手順をMAC-then-encryptと呼ぶ:数多くの問題の元凶
p.41 TLS1.1以降ではレコードごとに明示的なIVが含まれる:TLS1.0以前は暗黙のIVを利用(前のTLSレコードを暗号化したブロックを次のIVとする)していたが、2011年に安全でないと判明(BEAST攻撃、7.2節参照)
p.41 パディングがMAC計算に含まれないため、パディングオラクル攻撃が可能(7.4節参照)
p.41 2014年9月、Encrypt-then-MACとよばれるTLS拡張が公開:平文データとパディングを暗号化し結果をMACに渡す

2.5.3 AEAD(認証付き暗号、Authenticated Encryption with Associated Data)

p.41 暗号化および完全性検証を1つのアルゴリズムにまとめた
p.41 ストリーム暗号化方式とブロック暗号化方式の折衷案

  1. 64ビットのナンス(nonce, 特別な乱数)を生成
  2. 平文を暗号化し、同時に完全性検証のためシーケンス番号とRecordヘッダも利用
  3. ナンスと暗号文を一緒に送る[1]

p.42 TLSで利用できる現在最良の暗号化利用モード:MAC-then-encryptの問題を回避できる
p.42 認証方式の選択肢としてGCMモード[2]およびCCMモード[3]が規程されているが、実際に利用可能なのはGCMのみ
p.42 AEADの新しい暗号スイートとしてChaCha20ストリーム暗号化方式に基づくものが標準化(RFC7905[4]

脚注
  1. ナンスの役割が分からん...... ↩︎

  2. Galois/Counter_Modeの略。暗号化利用モードのCTR(Counter)モードに認証としてGaloisモードを組み合わせたもの。並列計算が可能なため連鎖の必要なCBCモードより高速。2007年NISTが標準化。 ↩︎

  3. Counter with CBC-MACの略。CCMモードは128ビットブロック暗号に対してのみ定義されている。暗号のCTRモードと認証のCBC-MACモード。 ↩︎

  4. 2016年標準化。たとえばTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256など。Poly1305は認証モード。 ↩︎

KIDANI AkitoKIDANI Akito

2.6 再ネゴシエーションrenegotiation

p.43 新しい接続のセキュリティパラメータの合意のため、改めてハンドシェイクを実行する。以下の場合に便利:

クライアント証明書

p.43 二要素認証の1つ。
p.43 全接続にクライアント証明書を要求すると不親切(クライアントがエラーを受け取れない)
p.43 トップページはクライアント証明書なしで許可する運用方法が好まれる
p.44 クライアント証明書が必要なページへのアクセスがあった場合に、サーバが再ネゴシエーションでクライアント証明書を要求する

情報の隠蔽

p.44 前述のように、二段階でクライアント証明書を有効にすると、2回目のハンドシェイクが暗号化される
p.44 つまり、クライアント証明書の盗聴、ネゴシエーションの監視といった受動的攻撃ができなくなる
p.44 Torの通信で利用されている方式[1]

暗号強度の変更

p.44 昔、Webサイトとの通信を暗号化するのが先進的だった時代は、暗号化強度を分割しているサイトがあった(前述のページによってクライアント証明書の要不要をきりかえるのと類似)

以下の2つの状況になる可能性は低い。

SGC: Server-Gated Crypto

p.44 1990年代に強い暗号技術を米国が輸出規制していた:アメリカだけが強い暗号を使えるようにする機能
p.44 デフォルトでは弱い暗号:限られたCAが発行するクライアント証明書をもつ場合のみ、再ネゴシエーションで強い暗号を利用できた
p.44 2000年の規制緩和でSGCは廃れた

TLSレコードのカウンタオーバーフロー

p.44 各レコードは64ビットのシーケンス番号が割り当てられる
p.44 オーバーフローしそうな場合は再ネゴシエーションするしかない(64ビットもあるのでほぼない)
p.45 クライアントが再ネゴシエーションしたい場合、ClientHelloを送る(Client-initiated Renegotiation)
p.45 サーバが再ネゴシエーションしたい場合、HelloRequestプロトコルメッセージを送る:クライアントはアプリケーションデータの送信を中止し、新しいハンドシェイクを開始

構造体

      struct { } HelloRequest;

p.45 もともと、再ネゴシエーションは能動的攻撃に悪用の恐れ:2009年に発見され(詳細は7.1節)[2]、renegotiation_info拡張の導入によって修正された[3]

脚注
  1. 諜報活動をオンラインで匿名で行うために米海軍が開発したネットワーク技術『The Onion Routing』をもとに、オープンソース・コミュニティーのプログラマーたちが海軍公認で開発を進めたもの。Torは主として接続経路を匿名化するものであり、通信内容の秘匿を保証するものではない。TCP通信は可能だが、UDP通信は現時点で不可能。参考:パソコン遠隔操作事件 ↩︎

  2. 中間者攻撃が可能になるらしい。 ↩︎

  3. とはいえ再ネゴシエーション周りは脆弱性が多そう。2021年でも、TLSらじお第2回でとりあげたOpenSSLの脆弱性がある。 ↩︎

KIDANI AkitoKIDANI Akito

2.7 Application Dataプロトコル

p.45 アプリケーションのデータを運ぶバッファ
p.45 セキュリティパラメータに従ってRecordプロトコルのレイヤでパッケージ化、細分化[1]、暗号化される

脚注
  1. p.25 レコードは最長16,384バイト(2^14):それより長い場合、分割される。逆に単一のレコードにまとめられることもある。https://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-369ba9c2435126 ↩︎

KIDANI AkitoKIDANI Akito

2.8 Alertプロトコル

p.45 例外的な状況を伝える通知の仕組み
p.45 ほとんどはエラー:接続シャットダウンを通知するclose_notifyもある[1]

構造体(RFC5246)

      struct {
          AlertLevel level;
          AlertDescription description;
      } Alert;

      enum { warning(1), fatal(2), (255) } AlertLevel;

      enum {
          close_notify(0),
          unexpected_message(10),
          bad_record_mac(20),
          decryption_failed_RESERVED(21),
          record_overflow(22),
          decompression_failure(30),
          handshake_failure(40),
          no_certificate_RESERVED(41),
          bad_certificate(42),
          unsupported_certificate(43),
          certificate_revoked(44),
          certificate_expired(45),
          certificate_unknown(46),
          illegal_parameter(47),
          unknown_ca(48),
          access_denied(49),
          decode_error(50),
          decrypt_error(51),
          export_restriction_RESERVED(60),
          protocol_version(70),
          insufficient_security(71),
          internal_error(80),
          user_canceled(90),
          no_renegotiation(100),
          unsupported_extension(110),
          (255)
      } AlertDescription;

p.45 AlertDescriptionに任意の情報を入れる機能はない
p.45 深刻度がfatalの場合、現在の接続を即中断してセッションを無効化(同じセッションで進行中の接続は続く場合もあるが、再開はされない)
p.45 warningの場合、アラート送信側が接続を終えることはない(受信側はfatal扱いすることも)

脚注
  1. ネットワークファイアウォールなどで通信が遮断される場合、これがfatalで送信される ↩︎

KIDANI AkitoKIDANI Akito

2.9 接続を閉じる

p.45 接続を切断する場合close_notifyアラートを送る(SSL3.0で追加された、後述の強制切断攻撃対策)
p.45 受信側は書き込み中の内容を破棄し、自身もclose_nofityを送信:その後受け取ったメッセージは無視する
p.46 シャットダウンプロトコルが必要なのは、強制切断攻撃(truncation attack)への防御
p.46 強制切断攻撃に脆弱な実装は多数ある(6.7節によれば古いIEがそう)

KIDANI AkitoKIDANI Akito

2.10 暗号処理

2.10.1 疑似乱数生成器(Pseudorandom number generator / Pseudorandom function)

p.46 引数:シークレット、シード、一意なラベル
p.46 TLS1.2以降、暗号スイートでPRFを明示することが要求されている(TLS1.2(RFC5246)ではP_SHA256がデフォルト)[1]

p.46 TLS1.2のPRF:HMACとSHA256に基づく(暗号スイートに依らない)

PRF(secret, label, seed) = P_<hash>(secret, label + seed)

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                             HMAC_hash(secret, A(2) + seed) +
                             HMAC_hash(secret, A(3) + seed) + ...

      A(0) = seed
      A(i) = HMAC_hash(secret, A(i-1))

p.46 シードとラベルが異なるので同じシークレットでもマスターシークレット生成時と鍵生成時で出力が異なる

2.10.2 マスターシークレット

p.47 鍵交換によってプリマスターシークレットを得る:さらにPRFで処理し、48バイト(384ビット)のマスターシークレットを生成する

      master_secret = PRF(pre_master_secret, "master secret",
                          ClientHello.random + ServerHello.random)

p.47 マスターシークレットは事実上ランダムで、ハンドシェイクに紐づいている
p.47 根拠となっているのが交換した乱数値のみなので、これらの値を観察して複製すると同じマスターシークレットを共有する複数のセッションを生成できる(トリプルハンドシェイク攻撃、詳細は7.6節)

2.10.3 鍵生成

p.47 アプリケーションデータの通信に必要な鍵素材は以下のように生成される

      key_block = PRF(SecurityParameters.master_secret,
                      "key expansion",
                      SecurityParameters.server_random +
                      SecurityParameters.client_random);

p.47 鍵ブロックはネゴシエーションしたパラメータによって長さが異なる
p.47 鍵ブロックは6つに分割される:MAC鍵2つ、暗号鍵2つ、IVが2つ[2][3][4]

      client_write_MAC_key[SecurityParameters.mac_key_length]
      server_write_MAC_key[SecurityParameters.mac_key_length]
      client_write_key[SecurityParameters.enc_key_length]
      server_write_key[SecurityParameters.enc_key_length]
      client_write_IV[SecurityParameters.fixed_iv_length]
      server_write_IV[SecurityParameters.fixed_iv_length]

p.47 セッション再開時に、鍵ブロック生成の際に利用するマスターシークレットは以前のセッションと同じものを利用する。しかし、新しいハンドシェイクから乱数を取得するので、結果として鍵は異なるものになる。

脚注
  1. TLS1.3ではPRFがHKDFに置き換えられている模様。https://datatracker.ietf.org/doc/html/rfc8447#page-8HMAC-based Key Derivation Function↩︎

  2. ストリーム暗号化方式(ChaCha20など)ではIVは利用されない。AEAD(GCM、ChaCha20)ではMAC鍵を使わない。 ↩︎

  3. RFC5246)ではIVが生成されるのはAEADの場合のみで、nonce値として利用される。 ↩︎

  4. AES_256_CBC_SHA256スイートの場合、32バイトの暗号キー2つと32バイトのMACキー2つで合計128バイトとなる(RFC5246↩︎

KIDANI AkitoKIDANI Akito

2.11 暗号スイート

p.48 TLSはセキュリティプロトコルを作り出すためのフレームワーク:柔軟な実装ができる
p.48 以前のバージョンではプロトコルにハードコードされている要素技術もあった[1]
p.48 暗号スイートを規定する属性の例

  • 認証の種類:RSA、ECDSAなど
  • 鍵交換の種類:RSA、ECDHEなど
  • 暗号化アルゴリズム:AES、3DESなど
  • 暗号鍵の長さ:128、256など
  • 暗号化利用モード:CBC、GCMなど
  • MACアルゴリズム:SHA(SHA1)、MD5など
  • PRF(TLS1.2以降):SHA256、SHA384など
  • Finishedメッセージのハッシュ関数(TLS1.2以降)
  • verify_data構造体の長さ(TLS1.2以降)

RFC5246 Appendix C TLS1.2のCipherSuites一覧

TLS_DH_anon_WITH_AES_128_CBC_SHA256
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 など

※GCM利用スイートはRFC5288で別途定義されている

      CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9C}
      CipherSuite TLS_RSA_WITH_AES_256_GCM_SHA384 = {0x00,0x9D}
      CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = {0x00,0x9E}
      CipherSuite TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = {0x00,0x9F} など

p.48 名前から類推できないパラメータはRFCで定義されている
p.48 本書執筆時点(2017年?)で300のスイート:一覧はIANAのページ参照[2]

脚注
  1. TLS1.2以前は暗号化利用モードとしてCBCしか利用できなかった....はず ↩︎

  2. tls-parameters-4.csvがダウンロードできた。442行あったが、ざっと目視で40行ほどunassignedまたはreservedのものがあったので2021年5月末時点での総数は400程度と思われる。 ↩︎

KIDANI AkitoKIDANI Akito

p.49 AEAD(認証付き暗号、GCMoyobi
ChaCha20-Poly1305)の導入に伴い、MAC不要のスイートも登場:その場合、スイート末尾のSHA256などはPRFを指す
p.49 CCM(Counter with CBC-MAC)の場合PRFは省略されている
p.49 TLS1.2以前のスイートは、TLSバージョンによってPRFが異なる
p.49 例:TLS_RSA_WITH_AES_128_CBC_SHA

  • TLS1.2: HMAC-SHA256
  • TLS1.0, TLS1.1: HMAC-MD5/HMAC-SHA1の組み合わせ[1]

p.49 SHA1は選択的プレフィクス衝突攻撃に弱いことが知られているがHMAC-SHA1に対する既知の重大な攻撃はない
p.49 使えるスイートはサーバの持つ鍵の種類などにより制限される:RSA鍵しか持たない場合、ECDSAを使うスイートは対応できない
p.49 認証の強度は証明書・鍵の長さと署名アルゴリズムによって決まる:サーバ側で設定されるが、利用者に公開していないサーバもある

脚注
  1. TLS1.1のRFCによると PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) と排他的論理和を利用している ↩︎

KIDANI AkitoKIDANI Akito

2.12 TLS拡張Extensions

p.50 RFC3546(2003年)で定義[1]:TLS1.1で仕様に追加
p.50 拡張ブロックがClientHello/ServerHello Messageのあとに配置

RFC3546より

      struct {
          ProtocolVersion client_version;
          Random random;
          SessionID session_id;
          CipherSuite cipher_suites<2..2^16-1>;
          CompressionMethod compression_methods<1..2^8-1>;
          Extension client_hello_extension_list<0..2^16-1>;
      } ClientHello;

      struct {
          ProtocolVersion server_version;
          Random random;
          SessionID session_id;
          CipherSuite cipher_suite;
          CompressionMethod compression_method;
          Extension server_hello_extension_list<0..2^16-1>;
      } ServerHello;


      struct {
          ExtensionType extension_type;
          opaque extension_data<0..2^16-1>;
      } Extension;

      enum {
          server_name(0), max_fragment_length(1),
          client_certificate_url(2), trusted_ca_keys(3),
          truncated_hmac(4), status_request(5), (65535)
      } ExtensionType; // 2バイト

      enum {
          hello_request(0), client_hello(1), server_hello(2),
          certificate(11), server_key_exchange (12),
          certificate_request(13), server_hello_done(14),
          certificate_verify(15), client_key_exchange(16), finished(20),
          certificate_url(21), certificate_status(22), // 追加部分
          (255)
      } HandshakeType;

p.50 新しい機能に対応していることの表明+必要なデータを運搬する役割
p.50 プロトコルの進化を牽引
p.50 拡張の一覧はIANAのページ

脚注
  1. TLS1.0のリリースが1999年、TLS1.1が2006年、TLS1.2が2008年。 ↩︎

KIDANI AkitoKIDANI Akito

2.12.1 ALPN(Application-Layer Protocol Negotiation)

p.50 ポート443でデフォルトはHTTP1.1、ネゴシエーションしてSPDYやHTTP/2などが可能
p.50 application_layer_protocol_negotiation(0x10):クライアントが対応するプロトコル一覧を送る、レスポンスも同じ拡張
p.50 以前からあるNext Protocol Negotiation拡張と同様:NPNは暗号化されているが、ALPNは平文(ネットワーク機器が経路制御できるように)

RFC7301で定義されている。
ハンドシェイク中にネゴシエーションが完了するため、通信のラウンドトリップを削減できる。

KIDANI AkitoKIDANI Akito

2.12.2 CT(Certificate Transparency:証明書の透明性)

p.51 公開のログサーバにCA(認証局)が自身の証明書を登録する
p.51 ログサーバはSCT(Signed Certificate Timestamp)を登録証明としてCAに返送する
p.51 末端の利用者のツール上でSCTをチェック
p.51 SCTの転送方法の1つがsinged_certificate_timestamp拡張

RFC6962で定義されている

  • ExtensionTypeは18(0x12)
  • ClientHelloでextension_dataが空で送られる
  • ServerHelloで以下のデータがextension_dataにセットされる
        opaque SerializedSCT<1..2^16-1>;

        struct {
            SerializedSCT sct_list <1..2^16-1>;
        } SignedCertificateTimestampList;
  • セッションリザンプションでセッションが再開された場合、ClientHelloは通常時と同じだが、SeverHelloにはこの拡張は含まれない
KIDANI AkitoKIDANI Akito

2.12.3 楕円曲線の利用可能性

p.51 RFC4492 Elliptic Curve Cryptography (ECC) Cipher Suites for TLS
p.51 以下の2つの拡張が定義されている

     enum { elliptic_curves(10), ec_point_formats(11) } ExtensionType;

p.51 elliptic_curves:対応する名前付き曲線の名前のリストをClientHelloで送信[1][2]
p.52 ec_point_formats:楕円曲線上の点を圧縮するオプションをネゴシエーションできる(帯域の節約のため:とはいえ256ビットの曲線で64バイト程度の節約のため、圧縮されることは実際にはほとんどない)

p.52 RFC4492のNIST選定の楕円曲線はパラメータ選定基準が不明で、Dual_EC_DRBGにはバックドアがあることが判明している。
p.52 置き換えを目指してCurve25519、Curve448が標準化された(2018年、RFC8422)

脚注
  1. 楕円曲線DH鍵交換で見た通りhttps://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-46c09a05248899 ↩︎

  2. 名前付き楕円曲線は最適化されており処理が早いという利点がある。 ↩︎

KIDANI AkitoKIDANI Akito

2.12.4 Heartbeat(RFC6520

p.53 TLS/DTLS[1]向けのキープアライブ(死活確認)およびパスMTU探索[2]機能のプロトコル拡張
p.53 TCPにはキープアライブ機能があるので、これは主にUDPベースのDTLS向け
p.53 長さゼロのTLSレコードをキープアライブに使う案もある(プロトコルで許可されている、らしいがRFCのどの部分かはっきりせず...):PMTU探索ではペイロードを変化させる必要があるため両立不可

   enum {
      peer_allowed_to_send(1),
      peer_not_allowed_to_send(2),
      (255)
   } HeartbeatMode;

   struct {
      HeartbeatMode mode;
   } HeartbeatExtension;

   enum {
      heartbeat_request(1),
      heartbeat_response(2),
      (255)
   } HeartbeatMessageType;

   struct {
      HeartbeatMessageType type;
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
      opaque padding[padding_length];
   } HeartbeatMessage;

p.53 HeartbeatはTLSのサブプロトコル:他のプロトコルメッセージやアプリケーションデータと同時にやりとり可能
p.53 RFCはハンドシェイク完了時点でHeartbeatメッセージ許可:実際のところ、OpenSSLではTLS拡張やりとり直後から許可
p.53 2014年4月までは実際に使われているか不明だったが、Heartbleed攻撃が発覚(サーバのメモリ上にあるデータが抜き出される、詳細は6.3節)

脚注
  1. Datagram Transport Layer Security ↩︎

  2. Path Maximum Transmission Unit:1回の転送で送信できるデータ長の最大値。パケットがホップする場合、有効な値を探索する必要がある。イーサネットでは1500バイトだが、ユーザー認証などのためPPPoEを使うとカプセル化で8バイト使うため、1492バイトとなる。Wikipediaより ↩︎

KIDANI AkitoKIDANI Akito

2.12.5 Next Protocol Negotiation

p.54 GoogleがSPDYプロトコルを広める際に必要となり追加された拡張、ChromeだけでなくFirefox、OpenSSLが対応していた。
p.54 標準化を目指していたが、最終的には前述のALPNが採用された(2014年)

  • 新しいハンドシェイクメッセージの導入により通常のハンドシェイクフローに変更が必要となり、破壊的で混乱を招くとされたため。
  • 経路上のネットワーク機器がネゴシエーションされるプロトコルが把握できないことによる問題も懸念された(ので平文のALPNが採用された)

p.54 NPNはバージョン違いの仕様がある:2010年の012011年の022012年の032012年の04
p.54 実際に使われているのは2011年の古いバージョン

ハンドシェイク

p.54 SPDYが有効なクライアントは空のnext_protocol_negotiation拡張と接続したいホストを示すserver_name拡張をClientHelloで送る
p.54 ServerHelloには対応しているプロトコル一覧がnext_protocol_negotiation拡張で返る
p.54 NextProtocolハンドシェイクメッセージは以下の構造体で、接続に使いたいプロトコルを示す

struct {
  opaque selected_protocol<0..255>;
  opaque padding<0..255>;
} NextProtocol;

p.54 クライアントを受動的攻撃から守るためNextProtocolは暗号化して送られる:ChangeCipherSpecのあと、Finishedの前
p.54 パディングを使って選択したプロトコルを推測されないようにしている

KIDANI AkitoKIDANI Akito

2.12.6 安全な再ネゴシエーション(RFC5764、2010年

p.55 過去にハンドシェイクを確立した二者間の再ネゴシエーションを検証するための拡張

p.55 最初のハンドシェイクでデータなしでrenegotiation_info (extension type 0xff01 [65281])を送信し合う
p.55 SSL3.0では非対応: TLS_EMPTY_RENEGOTIATION_INFO_SCSV[1]という特別な暗号スイート[2]をクライアントが利用(これもRFC5764で定義。ClientHelloのcipher_suitesにTLS_EMPTY_RENEGOTIATION_INFO_SCSVを追加してメッセージを送信するだけ)

      struct {
          opaque renegotiated_connection<0..255>;
      } RenegotiationInfo;

p.55 再ネゴシエーションのハンドシェイクでは、以前のハンドシェイクのFinishhedメッセージのverify_dataを送る
p.55 クライアントは自身が送信したverify_dataを送る[3](暗号化されているため攻撃者は取得できない)
p.55 サーバはクライアントのverify_dataと自信が送信したverify_dataを送る[4]

脚注
  1. Signaling Cipher Suite Value。 ↩︎

  2. IANAのリストにも載っている。 ↩︎

  3. TLS1.2では12バイト。SSL3.0の場合は36バイトとなる。 ↩︎

  4. TLS1.2では合計24バイト。SSL3.0の場合は72バイト。この拡張の256バイト制限を考慮すると、verify_dataの長さは暗号スイートによっては128バイトまで利用可能、ということだろうか? ↩︎

KIDANI AkitoKIDANI Akito

2.12.7 SNI(Server Name Indication)

p.55 2003年のRFC3546で提案され2006年にTLS1.1からTLSに追加(初期のTLS拡張の一つ)
p.55 server_name拡張:ClientHelloに含まれる
p.55 同じネットワークアドレスで複数の仮想サーバに安全に接続できるようになる:サーバに該当の証明書を見つけさせる

extension_dataはServerNameList

      struct {
          NameType name_type;
          select (name_type) {
              case host_name: HostName;
          } name;
      } ServerName;

      enum {
          host_name(0), (255)
      } NameType;

      opaque HostName<1..2^16-1>;

      struct {
          ServerName server_name_list<1..2^16-1>
      } ServerNameList;

p.55 SNI未対応の製品:Windows XP、Androidの初期バージョンなど[1][2]

脚注
  1. 2011年2月リリースのAndroid 3.0の標準ブラウザは対応している。https://knowledge.sakura.ad.jp/1706/ ↩︎

  2. Javaの場合、2011年7月リリースのJava 7から対応している。https://docs.oracle.com/javase/7/docs/technotes/guides/security/enhancements-7.html ↩︎

KIDANI AkitoKIDANI Akito

2.12.8 セッションチケット(2008年のRFC5077

p.55 サーバ側でセッション情報を保持せずセッション再開可能にする拡張:暗号化したチケットとチケット鍵をクライアントに送る
p.55 Webサーバ間でセッションの同期が不要になるので、クラスタの大規模化が容易になるかも
p.56 チケット鍵の強度が重要:実装によっては接続のための暗号より弱い場合もある。
p.56 OpenSSLは128ビットのAES鍵を利用し、同一チケット鍵が複数セッションで再利用される:チケット鍵を周期的に交換しなければならない
p.56 session_ticket拡張:クライアントは最初、空の拡張を送信。2回目はチケットを送信。
p.56 対応しているサーバはServerHelloに空の拡張を送信。Finishedメッセージのあとに、チケットをNewSessionTicketハンドシェイクメッセージ(新規追加された)に格納して送信。

     struct {
          HandshakeType msg_type;
          uint24 length;
          select (HandshakeType) {
              case hello_request:       HelloRequest;
              case client_hello:        ClientHello;
              case server_hello:        ServerHello;
              case certificate:         Certificate;
              case server_key_exchange: ServerKeyExchange;
              case certificate_request: CertificateRequest;
              case server_hello_done:   ServerHelloDone;
              case certificate_verify:  CertificateVerify;
              case client_key_exchange: ClientKeyExchange;
              case finished:            Finished;
              case session_ticket:      NewSessionTicket; /* NEW */
          } body;
      } Handshake;

      struct {
          uint32 ticket_lifetime_hint;
          opaque ticket<0..2^16-1>;
      } NewSessionTicket;

      struct {
          opaque key_name[16];
          opaque iv[16];
          opaque encrypted_state<0..2^16-1>;
          opaque mac[32];
      } ticket;

     /* encrypted_stateの暗号化前 */
      struct {
          ProtocolVersion protocol_version;
          CipherSuite cipher_suite;
          CompressionMethod compression_method;
          opaque master_secret[48];
          ClientIdentity client_identity;
          uint32 timestamp;
      } StatePlaintext;

      enum {
         anonymous(0),
         certificate_based(1),
         psk(2)
     } ClientAuthenticationType;

      struct {
          ClientAuthenticationType client_authentication_type;
          select (ClientAuthenticationType) {
              case anonymous: struct {};
              case certificate_based:
                  ASN.1Cert certificate_list<0..2^24-1>;
              case psk:
                  opaque psk_identity<0..2^16-1>;   /* from [RFC4279] */
          };
       } ClientIdentity;

p.56 サーバがセッションチケットでセッション再開する場合、ServerHelloのSession IDフィールドを空で送る:クライアントがセッションIDを生成してもよく、その場合サーバが同じIDで応答する。

KIDANI AkitoKIDANI Akito

2.12.9 署名アルゴリズム(RFC5246=TLS1.2

p.56 signature_algorithms拡張:対応している署名アルゴリズムとハッシュ関数をクライアントが伝える。
p.56 署名アルゴリズム:RSA、DSA、ECDSA
p.56 ハッシュ関数:MD5、SHA1、SHA224、SHA256、SHHA384、SHA512
p.56 拡張なので必須ではない:サーバは暗号スイートを元に署名アルゴリズムを推察する

  • 鍵交換アルゴリズムがRSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA:SHA1またはRSA
  • 鍵交換アルゴリズムがDHE_DSS, DH_DSS:SHA1またはDSA
  • 鍵交換アルゴリズムがECDH_ECDSA, ECDHE_ECDSA:SHA1またはECDSA

RFC5246 7.4.1.4.1. Signature Algorithms

      enum {
          signature_algorithms(13), (65535)
      } ExtensionType;

      /* extension_dataはsupported_signature_algorithms */

      enum {
          none(0), // 将来のハッシュ化不要な署名アルゴリズム用
          md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
          sha512(6), (255)
      } HashAlgorithm;

      enum { anonymous(0), // この拡張で使ってはいけない(DH_anonとか用)
       rsa(1), dsa(2), ecdsa(3), (255) }
        SignatureAlgorithm;

      struct {
            HashAlgorithm hash;
            SignatureAlgorithm signature;
      } SignatureAndHashAlgorithm;

      SignatureAndHashAlgorithm
        supported_signature_algorithms<2..2^16-2>;

セッションリザンプションの際にClientHelloに含まれていた場合は無視される(すでに合意済みのため)

KIDANI AkitoKIDANI Akito

2.12.10 OCSPステープル(RFC4366、およびRFC6066

p.56 status_request拡張(0x05):クライアントがOCSPステープリングに対応していることを示す
p.56 OCSPステープル:サーバからクライアントに証明書の失効情報を送る(失効については5.11節)

ClientHello"extension_data"は"CertificateStatusRequest"

      struct {
          CertificateStatusType status_type;
          select (status_type) {
              case ocsp: OCSPStatusRequest;
          } request;
      } CertificateStatusRequest;

      enum { ocsp(1), (255) } CertificateStatusType;

      struct {
          ResponderID responder_id_list<0..2^16-1>;
          Extensions  request_extensions;
      } OCSPStatusRequest;

      opaque ResponderID<1..2^16-1>;
      opaque Extensions<0..2^16-1>;

p.56 (RFC4366/6066で追加された)CertificateStatusハンドシェイクメッセージでOCSPレスポンス(DER形式)[1]を提示(1つしか送れない)

      struct {
          CertificateStatusType status_type;
          select (status_type) {
              case ocsp: OCSPResponse;
          } response;
      } CertificateStatus;

      opaque OCSPResponse<1..2^24-1>;

p.57 status_request_v2拡張(RFC6961):複数のOCSPレスポンスに対応(2016/7年あたりではクライアント、サーバともにあまりサポートされていない)

     struct {
       CertificateStatusType status_type;
       uint16 request_length; /* Length of request field in bytes */
       select (status_type) {
         case ocsp: OCSPStatusRequest;
         case ocsp_multi: OCSPStatusRequest; // 追加された複数対応
       } request;
     } CertificateStatusRequestItemV2;

     enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;

     struct {
       ResponderID responder_id_list<0..2^16-1>;
       Extensions request_extensions;
     } OCSPStatusRequest;

     opaque ResponderID<1..2^16-1>;
     opaque Extensions<0..2^16-1>;

     struct {
       CertificateStatusRequestItemV2
                        certificate_status_req_list<1..2^16-1>;
     } CertificateStatusRequestListV2;
     struct {
       CertificateStatusType status_type;
       select (status_type) {
         case ocsp: OCSPResponse;
         case ocsp_multi: OCSPResponseList;
       } response;
     } CertificateStatus;

     opaque OCSPResponse<0..2^24-1>;

     struct {
       OCSPResponse ocsp_response_list<1..2^24-1>;
     } OCSPResponseList;
脚注
  1. RFC2560Rubyのリファレンス ↩︎

KIDANI AkitoKIDANI Akito

2.13 プロトコルの限界

p.57 TLSには意図しない弱点や、OSIレイヤや設計方針に由来する制限がある
p.57 TCPは平文なので送信元と宛先のIPアドレスは判別可能
WiresharkでIPアドレスを確認できる

p.57 TLSでも多くの情報は平文(特に最初のハンドシェイク):回避策はあるが採用されていない

  • クライアントのブラウザーフィンガープリント
  • SNI情報
  • ホスト/クライアントの証明書

p.57 暗号化が有効でも観察できる情報:サブプロトコルや各メッセージの長さ
例)リクエストとレスポンスからHTTPでどのリソースにアクセスしているか推測可能[1]

脚注
  1. 2009年くらいの研究を見つけた:George Danezis, Traffic Analysis of the HTTP Protocol over TLSによれば、暗号化されたリクエストからリソースを絞り込むことはできるし、Referrerヘッダーなども参照してユーザーの興味関心を知ることができる。サーバーからのレスポンスも、公開されているサイトならば攻撃者は事前に知ることができるので、ユーザーがどこにアクセスしているか推測可能。 ↩︎

KIDANI AkitoKIDANI Akito

2.14 プロトコルのバージョンによる相違

p.57 SSL3.0(1995年):以降プロトコルの中核はそれほど変更なし
p.57 TLS1.0(1999年):名前の修正のみ
p.57 TLS1.1(2006年):セキュリティ上の問題修正
p.57 TLS1.2(2008年):AEADの導入、ハッシュ関数の一掃、ハードコードされたプリミティブの除去

2.14.1 SSL3.0

p.58 スクラッチから開発された
p.58 詳細な変更はAnalysis of the SSL 3.0 protocol, 1997

2.14.2 TLS1.0

p.58 HMACによってPRFが規定される(HMAC-MD5とHMAC-SHA1の排他的論理和)[1]
p.58 マスターシークレットがPRFによって生成されるようになった
p.58 Finishedメッセージのverify_dataの値がPRFによって生成されるようになった
p.58 MACにおいて標準化されたHMACを利用(SSL3.0は古いバージョンを利用)
p.58 パディングのフォーマットが強固に(SSL3.0のパディングは2014年のPOODLE攻撃の対象)
p.58 暗号スイートからFORTEZZA[2]が除外

p.58 TLS1.0はFIPS認証取得:アメリカ政府機関で利用可能に
p.58 プロトコル設計方針については『マスタリングTCP/IP SSL/TLS編』が詳しい

2.14.3 TLS1.1

p.58 CBC暗号化利用モードで明示的なIVを利用(のちのBEAST攻撃で悪用される問題への対処)[3]
p.58 パティング攻撃への防御:bad_record_macアラート(20)[4]
p.58 TLS拡張の参照(RFCではClientHelloのstructに含まれず)

2.14.4 TLS1.2

p.59 AEADのサポート
p.59 HMAC-SHA256暗号スイートのサポート
p.59 IDEAおよびDES暗号スイートの除外
p.59 主要なTLS拡張が組み込まれた(RFCでもClientHelloのstructにExtensionが追加された)
p.59 signature_algorithms拡張でクライアントの希望アルゴリズムを伝達可能
p.59 PRF、ディジタル署名がMD5+SHA1からSHA256に。暗号スイートが独自のハッシュ関数を指定することも可能。
p.59 Finishedメッセージのverify_dataの長さを暗号スイートで明示的に指定可能

脚注
  1. 2.11 暗号スイート https://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-c1840870a17ff2 ↩︎

  2. アメリカ政府が利用していたPCカードを利用したハードウェアベースの暗号標準。暗号化アルゴリズムにSKIPJACKというブロック暗号を利用していたが、機密扱いで詳細が公表されなかったため、IETFが標準化を拒んだためTLSで除外された。『マスタリングTCP/IP SSL/TLS編』pp.118-119 ↩︎

  3. 2.5 暗号化 https://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-6e19c7679b29ce ↩︎

  4. 2.8 アラートプロトコル https://zenn.dev/kdnakt/scraps/1146d7c00cd3ce#comment-d84dd400d77a97 ↩︎

KIDANI AkitoKIDANI Akito

第3章 公開鍵基盤

p.61 Public Key Infrastructure(PKI)

  • 会ったことがない相手と安全に通信する
  • 公開鍵を保管・失効する
  • 世界規模で使える:数百万のサーバ、数十億のデバイス

3.1 インターネットPKI

p.61 もともとはインターネット用ではなかったPKI[1]
p.61 Web PKI:ブラウザにおける証明書利用・検証に主眼
p.61 現在のPKI:信頼のおける第三者機関であるCA(Certificate Authority、認証局)[2]が発行する証明書を無条件に信頼するモデル

p.62 証明書所有者(Subscriber):エンドエンティティ(End-Entity)ともよぶ
p.62 登録局(RA, Registration Authority):本人性の検証が仕事。実際にはCAがRAを兼ねる。
p.62 認証局(CA, Certificate Authority):証明書所有者の本人性を保証する証明書を発行する。証明書の失効情報をオンラインで提供する。
p.62 証明書利用者(Relying party):厳密には、証明書を検証するのはWebブラウザなどのプログラムやOS。プログラムやOSは、トラストアンカー(デフォルトのルート証明書)を含むルートトラストストアを利用する。

脚注
  1. Wikipediaによると1970年台前半にイギリスのGCHQで開発されたが、1990年代まで公開されなかった。 ↩︎

  2. GlobalSignDigiCertなどがある ↩︎

KIDANI AkitoKIDANI Akito

p.63 信頼(trust):証明書がトラストストアに含まれるCAのルート証明書によって検証し得る、という意味
p.63 本人性(identity):正しいサーバである、というだけ。オフラインの本人性はEV証明書でないと得られない[1]

脚注
  1. EV(Extended Validation)証明書。SSL証明書にはDV(Domain Validation)、OV(Organization Validation)、EVの3種類があり、その中で最も厳格な審査方式を採用しているのがEV。EV証明書の企業名表示は、2018年9月にリリースされたChrome 77よりアドレスバーに表示されないよう仕様変更された。 ↩︎

KIDANI AkitoKIDANI Akito

3.2 証明書の標準

p.63 X.509:公開鍵基盤の国際標準(詳細はCertificateハンドシェイクメッセージの項を参照)、元々は電子ディレクトリサービス向け。
p.63 RFC5280:X.509をベースにPKIXワーキンググループがインターネットでの利用に適した仕組みにしたもの。主に以下を規定。

  • 証明書のフォーマット
  • 信頼パス(trust path)
  • 証明書失効リスト(CRL、Certificate Revocation List)

p.64 CA/Browser Forum (CAB Forum):証明書の発行および処理の標準を確立、徹底することに関心を持つCAやブラウザベンダーを中心とした団体。2007年〜。
p.64 Baseline Requirements:正式名称はBaseline Requirements for the Issuance and Management of Publicly-Trusted Certificates。2012年発表、以後定期的にアップデートされている[1][2]
p.64 CABフォーラムには2021年6月現在で、51のCAが参加している[3]
p.64 Baseline Requirementsは事実上すべてのCAに適用される:BRがCA向け監査プログラムであるWebTrustに組み込まれており、ルートトラストストアの運営元(Mozillaなど)が明示的にBRを要求するため。

脚注
  1. 2021年6月3日にバージョン1.7.6が公開された。 ↩︎

  2. これに関連して、先日GlobalSign社からSSLサーバ証明書のドメイン認証情報の再利用期間短縮のお知らせが発表された。再利用期間を825日から397日に短縮するというもの。これはBaselime Requirements 1.7.5での変更による。おそらく、2020年9月より証明書の有効期限を398日間以上にすることを禁じたことと関連すると思われる。 ↩︎

  3. Androidには100以上のCAのルート証明書が登録されているらしい。Wikipediaより ↩︎

KIDANI AkitoKIDANI Akito

3.3 証明書

p.64 証明書には、公開鍵、公開鍵に紐づけられた主体に関する情報、証明書発行主体のディジタル署名が含まれる
p.64 証明書はPKIの基本的な構成要素

p.64 ASN.1(Abstract Syntax Notation 1、抽象構文記法1):複雑なデータ構造やオブジェクトの定義、転送、交換のためのルール集。様々なプラットフォーム間でのネットワーク通信の手段として、実装に依存しない形で設計された。1998年に規定、2008年に更新。
p.64 ASN.1のエンコード方法はBERなどの標準がある。
p.64 BER(Basic Encoding Rules)のサブセットのDER(Distinguished Encoding Rules)がX.509では利用される
p.64 DERをBase64でエンコードするとPEM(Privacy-Enhanced Mail)になる
p.65 証明書はPEM形式がほとんどだが、一部DER形式のものも。

Amazonのルート証明書(Amazon Root CA 1)をASN.1デコーダーでデコードした結果。

KIDANI AkitoKIDANI Akito

p.65 Version(バージョン):バージョン1(0)、バージョン2(1)、バージョン3(2)がある。

  • バージョン1:基本的なフィールドのみ[1]
  • バージョン2:一意の識別子が加わる[2]
  • バージョン3:拡張が利用可能となった

p.65 Serial Number(シリアル番号):あるCAの発行した証明書を一意に区別する番号。かつては連番の正の整数が利用されたが、選択プレフィクス衝突攻撃の防止のため、現在は予測不能な値を利用する。
p.65 Signature Algorithm(署名アルゴリズム):署名によって保護できるよう証明書内部に格納。
p.65 Issuer(発行者):主体の識別名(DN, Distinguished Name)。国(Country)、組織(Organization)、部門(Organizational-Unit)の3つから成る。
p.65 Validity(有効性):開始日と終了日の2つの日付で表される。
p.65 Subject(主体者):公開鍵にひもづく団体の識別名が格納される。自己署名証明書の場合、Issuerと同じ。かつてはCommonNameがサーバのホスト名として使われたが、複数ホストの場合に問題があるため現在では代わりにSAN拡張(Subject Alternative Name)が利用される
p.66 Public Key(公開鍵):Subject Public-Key Info構造体で示される(アルゴリズムの識別子+公開鍵)

※バージョン2で追加された一意の識別子は利用されず、バージョン3ではAuthority Key Identifierおよび
Subject Key Identifier拡張が利用される。

脚注
  1. RFC1422によれば、次の7つ:version, serial number, signature (algorithm ID and parameters), issuer name, validity period, subject name, subject public key (and associated algorithm ID)。 ↩︎

  2. Wikipediaによれば、issuer unique identifier / subject unique identifierが追加された。 ↩︎

KIDANI AkitoKIDANI Akito

3.3.2 証明書の拡張

p.66 バージョン3で導入:フォーマットに柔軟性を持たせるため
p.66 拡張=識別子(OID、Object Identifier)+重要度(critical)+値(ASN.1構造体)

Extension  ::=  SEQUENCE  {
        extnID      OBJECT IDENTIFIER,
        critical    BOOLEAN DEFAULT FALSE,
        extnValue   OCTET STRING
                    -- contains the DER encoding of an ASN.1 value
                    -- corresponding to the extension type identified
                    -- by extnID
        }

p.66 criticalな拡張は正しく処理できない場合、証明書ごと破棄することになっている

p.66 Subject Alternative Name(主体者の別名):単一のホスト名しか指定できない基本のSubject(主体者)フィールドにかわって、DNS名やIPアドレスやURIによって示される複数の主体を公開鍵に紐づけられる。

   id-ce-subjectAltName OBJECT IDENTIFIER ::=  { id-ce 17 }

   SubjectAltName ::= GeneralNames

   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

   GeneralName ::= CHOICE {
        otherName                       [0]     OtherName,
        rfc822Name                      [1]     IA5String,
        dNSName                         [2]     IA5String,
        x400Address                     [3]     ORAddress,
        directoryName                   [4]     Name,
        ediPartyName                    [5]     EDIPartyName,
        uniformResourceIdentifier       [6]     IA5String,
        iPAddress                       [7]     OCTET STRING,
        registeredID                    [8]     OBJECT IDENTIFIER }

   OtherName ::= SEQUENCE {
        type-id    OBJECT IDENTIFIER,
        value      [0] EXPLICIT ANY DEFINED BY type-id }

   EDIPartyName ::= SEQUENCE {
        nameAssigner            [0]     DirectoryString OPTIONAL,
        partyName               [1]     DirectoryString }

p.66 Name Constraints(名前制約):CAが証明書を発行できる主体を制限するのに利用。たとえば、下位のCAを設置して自社ドメインに対してのみ証明書を発行できるようにする、など。RFC5280ではName Constraintsをcriticalな拡張としているが、実際にはcriticalに設定せずに利用されることもある。

KIDANI AkitoKIDANI Akito

p.67 Basic Constraints(基本制約):CA証明書かどうかを示す。同時に、下位CAの証明書パスの深さを制御する。すべてのCA証明書はこれを含まなければならないが、2016/17年当時はまだバージョン1で拡張のないルート証明書もあった[1]

   id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }

   BasicConstraints ::= SEQUENCE {
        cA                      BOOLEAN DEFAULT FALSE,
        pathLenConstraint       INTEGER (0..MAX) OPTIONAL }



脚注
  1. 手元のmacOS Catalina 10.15.7のキーチェーンアクセスをざっと確認したところ、バージョン1の証明書は見つからなかった(シリアル番号が1のものはいくつか見つかった) ↩︎

KIDANI AkitoKIDANI Akito

p.67 Key Usage(鍵用途):この証明書に含まれる鍵の用途を示す。

id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }

KeyUsage ::= BIT STRING {
     digitalSignature        (0), // ディジタル署名
     nonRepudiation          (1),  // 否認防止
     keyEncipherment         (2), // 鍵暗号
     dataEncipherment        (3), // データ暗号
     keyAgreement            (4), // 鍵交換
     keyCertSign             (5), // 電子証明書の検証
     cRLSign                 (6), // CRLの署名検証
     encipherOnly            (7), // 鍵交換時のデータ暗号用
     decipherOnly            (8) } // 鍵交換時のデータ復号用

KIDANI AkitoKIDANI Akito

p.67 Extended Key Usage(鍵拡張用途):任意の用途をOIDを利用して追加で指定できる。

   id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }

   ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId

   KeyPurposeId ::= OBJECT IDENTIFIER

-- extended key purpose OIDs

id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
id-kp-clientAuth             OBJECT IDENTIFIER ::= { id-kp 2 }
id-kp-codeSigning            OBJECT IDENTIFIER ::= { id-kp 3 }
id-kp-emailProtection        OBJECT IDENTIFIER ::= { id-kp 4 }
id-kp-timeStamping           OBJECT IDENTIFIER ::= { id-kp 8 }
id-kp-OCSPSigning            OBJECT IDENTIFIER ::= { id-kp 9 }

p.67 RFC5280ではエンドエンティティ用証明書に対してのみEKU拡張を利用すべきとされているが、実際には中間CAの証明書の用途制限にも利用[1]


脚注
  1. つまりどういうこと? ↩︎

KIDANI AkitoKIDANI Akito

p.67 Certificate Policy(証明書ポリシー):1つまたは複数のポリシーを示すOIDと、オプションでポリシー全文を示すURL。

   id-ce-certificatePolicies OBJECT IDENTIFIER ::=  { id-ce 32 }

   anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }

   certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation

   PolicyInformation ::= SEQUENCE {
        policyIdentifier   CertPolicyId,
        policyQualifiers   SEQUENCE SIZE (1..MAX) OF
                                PolicyQualifierInfo OPTIONAL }

   CertPolicyId ::= OBJECT IDENTIFIER

   PolicyQualifierInfo ::= SEQUENCE {
        policyQualifierId  PolicyQualifierId,
        qualifier          ANY DEFINED BY policyQualifierId }

p.67 Baseline Requirementsではエンドエンティティ用証明書にはポリシーを最低1つ含め、BR準拠であることを示すべしとしている。
p.67 この拡張を用いて証明書の検証の種類を指定できる[1]

脚注
  1. RFCをざっくり読んだがどこの話かよくわからず... ↩︎

KIDANI AkitoKIDANI Akito

p.67 CRL Distribution Points(CRL配布点):証明書失効リスト(CRL)の場所を示す。通常、LDAPやHTTPのURIで示される。
p.67 Baseline RequirementsではCRLかOCSPいずれかで失効情報を示すこととなっている

   id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::=  { id-ce 31 }

   CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint

   DistributionPoint ::= SEQUENCE {
        distributionPoint       [0]     DistributionPointName OPTIONAL,
        reasons                 [1]     ReasonFlags OPTIONAL,
        cRLIssuer               [2]     GeneralNames OPTIONAL }

   DistributionPointName ::= CHOICE {
        fullName                [0]     GeneralNames,
        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }

このURLにアクセスするとバイナリが返ってくるためcurlで直接表示はできない。以下のようにopensslを利用する。

$ curl http://crls.pki.goog/gts1d4/E0jY99efK8g.crl | openssl crl -inform der -text
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 77323  100 77323    0     0  2288k      0 --:--:-- --:--:-- --:--:-- 2359k
Certificate Revocation List (CRL):
        Version 2 (0x1)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: /C=US/O=Google Trust Services LLC/CN=GTS CA 1D4
        Last Update: Jul  4 07:22:28 2021 GMT
        Next Update: Jul 14 06:22:27 2021 GMT
        CRL extensions:
            X509v3 Authority Key Identifier: 
                keyid:25:E2:18:0E:B2:57:91:94:2A:E5:D4:5D:86:90:83:DE:53:B3:B8:92

            X509v3 CRL Number: 
                772
            X509v3 Issuing Distribution Point: critical
                Full Name:
                  URI:http://crls.pki.goog/gts1d4/E0jY99efK8g.crl
                Only User Certificates

Revoked Certificates:
    Serial Number: 02470B5B0FC19F5A09000000005DD138
        Revocation Date: Mar 29 09:04:14 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Cessation Of Operation
    Serial Number: FB9AA96CC279D675090000000076FACD
        Revocation Date: Jun 26 11:56:32 2021 GMT
    Serial Number: 55DA994202A25FB50A00000000EA2A6D
        Revocation Date: Jun 26 11:17:45 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Cessation Of Operation
    Serial Number: 0EFD60DB5C7C8635090000000076F9BD
        Revocation Date: Jun 26 19:19:55 2021 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: 
                Cessation Of Operation
...(以下大量に表示されたので省略)
KIDANI AkitoKIDANI Akito

p.68 Authority Information Access(認証機関アクセス情報):証明書を発行したCAの付加的な情報やサービスへのアクセス情報を示す。下記のように、リアルタイムで失効情報を確認するためのOCSPレスポンダのURLなど。他に、発行者の証明書を発見できるURLも(不完全な証明書チェーンの再構築に役立つ)。

   id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }

   AuthorityInfoAccessSyntax  ::=
           SEQUENCE SIZE (1..MAX) OF AccessDescription

   AccessDescription  ::=  SEQUENCE {
           accessMethod          OBJECT IDENTIFIER,
           accessLocation        GeneralName  }

   id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }

   id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }

   id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }

KIDANI AkitoKIDANI Akito

p.68 Subject Key Identifier(主体者鍵識別子):特定の公開鍵を含む証明書を区別するための識別子。公開鍵のハッシュ値などが識別子として推奨される。CA証明書はすべてこの拡張を含まねばならないし、この値が発行済み証明書のAuthority Key Identifier(機関鍵識別子)と同じでなくてはならない。

id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 14 }

SubjectKeyIdentifier ::= KeyIdentifier

p.68 Authority Key Identifier(機関鍵識別子):証明書に対する署名鍵を一意に特定する。証明書パスを構築するために使われる。

   id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 35 }

   AuthorityKeyIdentifier ::= SEQUENCE {
      keyIdentifier             [0] KeyIdentifier           OPTIONAL,
      authorityCertIssuer       [1] GeneralNames            OPTIONAL,
      authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }

   KeyIdentifier ::= OCTET STRING



KIDANI AkitoKIDANI Akito

p.68 その他のRFC 5280拡張(あまり使われない)

  • Delta CRL Distribution Point(Freshest CRLとも)
  • Inhibit anyPolicy
  • Issuer Alternative Name
  • Policy Constraints
  • Policy Mappings
  • Subject Directory Attributes
  • Subject Information Access
KIDANI AkitoKIDANI Akito

3.4 証明書チェーン

p.68 証明書の検証にはエンドエンティティの証明書だけでは不十分:信頼の起点となるルート証明書へのチェーンが必要

【ルートを安全に保つ】
p.69 ルートCAの鍵はPKIのエコシステム全体にとってとても重要:ルートトラストストアの多くは更新されず、古くからの鍵は事実上替えが効かない
p.69 Baseline Requirementsでは、ルート鍵を使う場合手作業が要求される(自動化しない)
p.69 いくつかのCAはルート証明書を直接使ってエンドエンティティの証明書を発行しているが、そうしたやり方は認められていない

【相互認証証明書(Cross-certification)】
p.69 新しくCAの運用を開始する場合の唯一の方法:相互認証
p.69 新しいCAのルート鍵が広範囲に配布されるのには時間がかかるので、他のルート鍵で署名してもらう(古いデバイス向け)[1]

【区画化(Compartmentalization)】
p.69 下位CA(非ルートCA)は証明書発行を自動化、オンライン化
p.69 CAのリスク回避として、下位CAを複数に分割する:例)証明書のクラス、ビジネス部門などで分割[2]

【委譲(Delegation)】
p.69 CAが自分たちと関連のない別組織を下位CAにすることもある:大企業がプライベートCAを運営したくない場合など
p.69 下位CAの証明書を親CAがコントロールすることもあれば、自由に発行できる場合もある(その場合は、発行可能な名前空間に制約がつく、Name Constraints拡張を参照)

p.69 証明書チェーンは一つだが、信頼パスは多様:相互認証の場合、メインのCAだけではなく、他方のルートCAに連なるパスもある
p.70 パスの構築は実装が貧弱で、様々な問題を引き起こす:サーバー側の問題(ヒューマンエラーや証明書設定のユーザビリティ)も、クライアント側の問題(信頼パスの構築と検証にまつわるセキュリティ問題、詳細は6.1節)もある。

脚注
  1. StarfieldのリポジトリにはMicrosoftからの相互認証証明書が記載されている。 ↩︎

  2. StarfieldはEV証明書やコード署名証明書などで中間証明書を分けている。 ↩︎

KIDANI AkitoKIDANI Akito

3.5 証明書利用者

p.70 証明書検証のために信頼できるルートCA証明書の一覧が必要:OSの提供するルートトラストストア
p.70 例外:Mozillaは独自のトラストストアを用意

(画像はFirefoxの証明書ストアとMacBookのキーチェーン)

【Apple】
p.70 Apple Root Certificate Programを運用:監査を通過しないと追加してもらえない

【Chrome】
p.70 ChromeはホストOSのトラストストアを利用するが、Linux上ではMozillaのものを利用
p.70 OSの提供するものだけでは不十分として、独自の機能レイヤを持つ

  • 証明書のブラックリスト機能[1]
  • EV証明書を発行できるCAのリスト
  • 一部もしくは全部の証明書へのCTへの追加対応への要求[2]

【Microsoft】
p.70 Microsoft Root Certificate Programを運用:一年間の監査を通過しないと追加してもらえない

【Mozilla】
p.71 Mozilla CA Certificate Policy(2021年現在はMozilla Root Store Policy)にもとづき運用
p.71 様々なLinuxディストリビューションのルートトラストストアのベースとして利用される

p.71 いずれのプログラムも、CAが独立した監査を受けることを要求:DV、OVであれば以下。

  • WebTrust for CA[3][4]:EV証明書の発行についての監査のみ?(GlobalSignのページを見るとそうでもなさそう)
  • ETSI TS 101 456[5] Electronic Signatures and Infrastructures (ESI); Policy requirements for certification authorities issuing public key certificates
  • ETSI TS 102 042 Electronic Signatures and Infrastructures (ESI); Policy requirements for certification authorities issuing public key certificates
  • ISO 21188:2006 Public key infrastructure for financial services
脚注
  1. Digicertの資料によれば、2011年のComodo/Diginotar事件で登録局がハッキングされ、Gmailなどの証明書が偽造された際に、Comodoなどの証明書をブラックリストに追加してブラウザベンダが配布した、とされている。 ↩︎

  2. 2.12.2節 CTを参照。CT:Certificate Transparency(証明書の透明性)はSSL/TLSの信頼性を高めるための技術で、Google社により提唱され、2013年にRFC6962としてまとめられた。認証局が証明書を発行する都度、全ての証明書発行の証跡を、第三者の監査ログに記載し、Signed Certificate Timestamp(SCT)と呼ばれるデータが返される。Webサーバはこれを証明書とともにブラウザに提示し、ブラウザが検証する。 ↩︎

  3. https://jp.globalsign.com/webtrust/ によれば、AICPA(米国公認会計士協会)とカナダ勅許会計士協会によって共同開発された国際的な電子商取引認証局監査プログラム ↩︎

  4. 正確には、WebTrust
    Principles and Criteria for Certification Authoritiesのことか。 ↩︎

  5. European Telecommunications Standards InstituteのTechnical Specification。 ↩︎

KIDANI AkitoKIDANI Akito

3.6 CA

p.71 パブリックCAになるために必要なこと

  1. 競争力のあるCAを構築する
  2. 各国の法律を遵守する(ライセンス取得が必要な場合も)[1]
  3. ルートCA証明書プログラムで要求される監査をパスする
  4. 自分のルートCA証明書をさまざまなルートCA証明書プログラムに配置する
  5. ルート証明書の相互認証を行って運用を開始する

p.71 競争力のあるCAを構築する、とは...

  • PKIおよびCAの運用に深い知識をもつ
  • ルートCA証明書と下位CAの証明書鍵を防御しつつ、強固で安全な区画化されたネットワークを設計する
  • 証明書のライフサイクルに対応する
  • Baseline Requirementsに従う
  • EV SSL Certificate Guidlinesに従う[2]
  • グローバルなCRL、OCSP基盤を用意する

p.71 初期はDV証明書販売は割の良い仕事だった:2015年にLet's Encryptという無償CAが登場し、価格急落[3]
p.71 EV証明書関連は利益率が高くなるはず[4]

脚注
  1. 日本の場合、電子署名法にもとづきJIPDEC(一般財団法人日本情報経済社会推進協会)が認定している。参考:認証局の種別と関連制度 ↩︎

  2. こちらもCAB Forumのだしているガイドライン。 ↩︎

  3. 最近ではZeroSSLというサービスもあるらしい。参考:無料の SSL 証明書が得られる ZeroSSL を使ってみた ↩︎

  4. 参考:GlobalSignだとEV証明書は¥140,800、マルチドメインEV証明書は¥222,200。 ↩︎

KIDANI AkitoKIDANI Akito

3.7 証明書のライフサイクル

p.72 証明書所有者がCSR(Certificate Signing Request)を用意、CAに送信
p.72 CSR:公開鍵を格納すること、対応する秘密鍵で署名し秘密鍵の所有を明らかにする
p.72 CSRにはメタデータを格納できるが一部しか使われない:CAが他から手に入れた情報で上書きすることもある[1]

【DV証明書(Domain Validation)】
p.72 証明書所有者がメールアドレス宛に送られてきたメールに記載のリンクをクリックして承認すると、証明書が発行される
p.72 完全に自動化されており迅速に発行される
p.72 メール以外にも電話や郵送による確認もある

【OV証明書(Organization Validation)】
p.72 サイトを運営する組織が実在するか、本物の組織化を確認する
p.72 Baseline Requirements登場までOV証明書発行手続きは標準化されておらず、現在でも一貫していない部分がある

【EV証明書(Extended Validation)】
p.72 非常に厳格が本人性の検証が行われる。証明書の審査過程が業界統一の規格になった。[2]
p.72 証明書の入手には数日から数週間かかることもある。

p.73 上記検証が完了後、CAが証明書を発行する
p.73 証明書の再発行という言葉があるがそのような手続きはない:失効した場合は、単に新しい証明書を発行するだけ

脚注
  1. Nginx+opensslの場合のCSR作成手順。Country, Name, State or Province Name, Locality Name, Organization Name, Organization Unit Name, Common Name(ウェブサーバのFQDN。例:ssl.globalsign.com), Email Addressなどのメタデータがある。 ↩︎

  2. CA/B Forumの定めるGuidelines for the Issuance and Management of Extended Validation Certificates、通称EV SSL Certificate Guidelines。対面での本人確認には写真つきの免許証、パスポートなどを用いるべし、などと書かれている。 ↩︎

KIDANI AkitoKIDANI Akito

3.8 失効

p.73 証明書の秘密鍵が危殆化した場合、証明書が必要なくなった場合に失効される:2つの標準

【CRL(Certificate Revocation List)】[1]
p.73 期限切れになっていない、失効した証明書のシリアル番号一覧[2]
p.73 証明書のCRL Distribution Points拡張を利用してURLが格納されている
p.73 肥大化するためリアルタイム検索が遅い

【OCSP(Onlince Certificate Status Protocol)】
p.73 単一の証明書の失効状態を取得できる仕組み。OCSPのサーバをOCSPレスポンダという。
p.73 Authority Information Access拡張を利用してOSCPレスポンダの場所を示す。
p.73 リアルタイム検索ができるが、パフォーマンスとプライバシの問題がある[3]
p.73 こうした問題を解決する技術:OSCPステープリング(WebサーバがOSCPレスポンダと通信し、TLSハンドシェイクにOSCPレスポンスを埋め込む)[4]

脚注
  1. Digicertのホワイトペーパーに詳しい ↩︎

  2. 現物はhttps://zenn.dev/link/comments/ec15e852430d54 ↩︎

  3. Wikipediaの記事によれば、Google Chromeは待ち時間やプライバシーの問題を理由に、2012年に OCSP チェックをデフォルトでは無効にした。同記事の注にあるWebサイトによると、OSCPリクエストを受け取るCAがOSCPリクエスト送信元のIPと失効チェック対象の証明書から、どこからどのサイトが見られているか分かるのを問題視した模様。また、同サイトによれば、OSCPのレスポンスの中央値は300ms程度で、平均値は1秒程度とのこと。確かに遅い。 ↩︎

  4. RFC6066 Transport Layer Security (TLS) Extensions: Extension Definitionsで定義されている。本書2.12.10 OCSPステープルを参照。 ↩︎

KIDANI AkitoKIDANI Akito

3.9 弱点

p.73 1995年ごろ、インターネットはまだ重要性が低かった:Eコマースの発展とともに暗号化が必要に
p.74 Eコマースにとって十分なセキュリティが実現された:商業的セキュリティ
p.74 CAとブラウザベンダが制御
p.74 CAの立場の難しさ:何百ものCAがとても低いエラー率で証明書を発行し全体が動作しているのに、無償かつ完璧なセキュリティを求める人々がいる
p.74 インターネットPKIのさまざまな欠点

証明書の発行に際してドメイン所有者の許可が求められない

p.74 全てのCAが許可なくどんなドメイン名の証明書でも発行できる
p.74 数百あるCAの質も様々:監査を受けていても安全ではない ex.2011年のComodo/DigiNotar社の事件
p.74 CAがセキュリティより商業的利益を優先するケースも:2011年Trustwave社が証明書のでっちあげを認めた[1][2]
p.75 政府がCAのシステムを悪用している可能性も

信頼を迅速に回復できない

p.75 Trust agilityが欠如している:証明書に問題がある場合、ルートトラストストアからCAの証明書を除去すればいいが、大手は除外しづらい
p.75 ブラウザが、発行済み証明書を信頼するが新規の証明書を信頼しない、としたこともある(2017年、WoSignおよびStartCom)[3]

弱いドメインの検証

p.75 DV証明書は安全ではないWHOISプロトコルで取得できるドメイン所有者情報をもとに発行される
p.75 ドメインをのっとったり、電子メールやCAネットワークへの不正アクセスで偽のDV証明書を取得できる[4]

失効がうまくいかない

p.75 2011年にいくつかのCAで問題発生:ブラウザが自前のブラックリストなどで確実に失効させる必要があった
p.75 理由1:失効情報が各システムへ波及するまでの遅延(Baseline RequirementsではCRL/OCSPについて10 日間、中間証明書については12ヶ月までの有効にすることを認めている[5]
p.75 理由2:ブラウザのソフトフェイル・ポリシーにより、失効情報が取得できなくても接続が遮断されない
p.76 これらの理由から、ブラウザベンダーは失効確認を放棄[6]
p.76 失効については5.11節でさらに説明がある

証明書の警告がセキュリティの意図を台無しにする

p.76 証明書の検証が手ぬるい[7]:検証を完全に端折っているアプリやライブラリが多数、ブラウザの警告も迂回可能[8]
p.76 HSTS(HTTP Strict Transport Security)と呼ばれる新しい標準:ブラウザに証明書警告をエラーに置き換え、迂回をできなくさせる[9]

脚注
  1. ある記事によれば、ある会社が社内ネットワーク上でTLS接続を盗み見るために中間者攻撃を可能にする下位のルート証明書をTrustwaveが発行した。これを受けてMozillaがルート証明書プログラムからの除外を検討したとされているが、2021年7月現在のFirefoxのストアにはTrustwaveの証明書が確認できるので、結果としては除外されなかった? ↩︎

  2. 2011年のこれらの事件を受けて翌2012年にBaseline Requirementsが発表された。 ↩︎

  3. Wikipediaの記事によれば、日付を偽装して証明書を発行していたため、MozillaがWoSignをブロックした。WoSignはStartComを買収していた。別のサイトによれば、ルート証明書もその後Mozillaのストアから削除され、Googleも同様の対応をとった模様。 ↩︎

  4. CAへの不正アクセスについては、 ↩︎

  5. 最新のBaseline Requirementsを確認すると、中間証明書は失効から24時間以内にOCSPを更新すること、とされている。通常の証明書については最低8時間最大10日間OCSP情報を有効にしておくことを認めている。 ↩︎

  6. 最近のChromeはそうでもなさそう?Chromeのヘルプでは「Chrome が失効のステータス情報を取得できない場合、証明書は失効したものとして扱われます。」と書かれていた。 ↩︎

  7. このサイトによれば、iOS4.Xの証明書検証はBasicConstraints拡張を完全に無視しており、なりすましが可能であった。 ↩︎

  8. 本書1.4.5節 中間者攻撃のシリアの事例。 ↩︎

  9. RFC6797で規定。HTTPでの接続時にHTTPSを強制するのが主な機能。 ↩︎

KIDANI AkitoKIDANI Akito

3.10 ルートCA証明書の鍵の危殆化

p.76 PKIへの最も確実な攻撃=ルートCA証明書を狙う:政府機関の要求、100万ドルで新規にCAを設置
p.76 エンドエンティティ証明書を発行した形跡のないCAもいる
p.76 こうした状況への対策:公開鍵ピンニング(HPKP)[1]、HTTPS Everywhere拡張[2]
p.76 最も手間がかからない攻撃=既存のルートCA証明書、中間CA証明書を狙う:偽の証明書を発行しても発行元が同じなので見分けるのは困難
p.77 2003年のShamir/Tromerの発表:1024ビットの鍵は1000万ドルのマシンで約1年で破れる
p.77 2013年のTromerの見積もり:1024ビットの鍵は100万ドルでいける
p.77 => 政府機関にとっては容易に破りうるレベル
p.77 Googleは2013年に1024ビットの証明書から移行[3]
p.77 Mozillaは2013年末までに弱いルートCA証明書削除を予定していたが、2015年後半にようやく完了した[4]

脚注
  1. HPKP、HTTP Public Key PinningRFC7496で2015年4月に規定されたが、非推奨となり、Google Chromeは2019年のv72でサポートを廃止した。Firefoxは2015年1月リリースのv35からこれをサポートし続けている。これを置き換えられるものとして、Certificate Transparencyがある。 ↩︎

  2. Firefox版Chrome版などがある。すべてのWebサイトに接続する際にHTTPSで接続するようになる。TorプロジェクトとEFF(Electronic Frontier Foundation)が作成。 ↩︎

  3. Googleの発表によると、2013年7月に2048ビットの鍵へ移行中。2〜3ヶ月かかる見込み。 ↩︎

  4. この記事によれば、Firefox 32(2014年)でEntrustやGoDaddyなどいくつかの弱いルート証明書が削除されている。 ↩︎

KIDANI AkitoKIDANI Akito

3.11 エコシステムの観測

p.77 2010年以降PKIエコシステムの活発なスキャニング・モニタリングが実施:それ以前のことはあまりわからない
p.77 2010年7月の筆者Ivan Ristićの調査:12億ドメインの証明書、TLSセキュリティ分析
p.77 その数日後にEEFのSSL Observatory調査:全IPv4アドレス空間の同様の調査
p,77 2011年Holzらの同様の研究
p.77 2012年EEFがHTTPS Everywhereを利用したDistributed SSL Observatoryを発表:結果は未公開
p.78 2012年、筆者Ivan RistićのSSL Labsの一環としてSSL Pulseプロジェクト:毎月15万サイトをスキャン
p.78 2012年、ISC Certificate Notaryプロジェクト(2018年以降はあまり更新されていなそう)
p.78 2013年のDurumericらによる調査が最も包括的:ZMapというOSSツール、スキャン結果を公開
p.78 2014年、IE11によるテレメトリ収集対象に証明書を含める

p.78 いずれも、深刻な問題を明らかにしたわけではないが、重要な問題に光を当てた
p.78 例)CAがプライベートIPアドレス向けの証明書や完全修飾でないドメインへの証明書を定期的に発行

p.78 2014年、CA/B Forumのガイドライン遵守状況の評価結果が公開:Baseline Requirements導入後改善が見られた

p.79 CAの正確な数ははっきりしていない:通常のルートCAは100ちょっと、上位10のルートCAが市場の90%を占める
p.79 2015年Comodo社がcrt.shという証明書検索エンジンを立ち上げ:CTのログが情報源
p.79 2015年ミシガン大学がCensysというネットワークセキュリティ検索エンジンを立ち上げ:セキュリティ研究者向け

KIDANI AkitoKIDANI Akito

3.12 改善

p.79 2011年複数のCAのセキュリティが破られ、改善の動き
p.79 失敗したものも多い:残ったのはCT、公開鍵ピンニング、DANE、CAA(詳細は10章)

【Perspectives】
p.79 TLS認証を補助する公証人の概念を最初に導入:クライアントが公証人に相談して証明書を認証
p.79 2008年立ち上げ、2021年現在も活動している[1]

脚注
  1. 元はカーネギーメロン大学のプロジェクトだった。Perspectivesプロジェクトのサイトによると、2021年現在、AWS in Educationなどの助成を受けて、8つの公証人サーバを運用している。35万サイトを1日2回チェックしている。公証人にアクセスするにはFirefox拡張などを利用することができる。 ↩︎

KIDANI AkitoKIDANI Akito

【Convergence】
p.79 Perspectivesからフォークして改良した:2011年ローンチ、2013年以降活動なし、短命
p.80 プライバシー改善のため公証人へのリクエストに複数プロキシを介在
p.80 パフォーマンス改善のためサイト証明書を一定期間キャッシュ

【公開鍵ピンニング(Public key pinning)】
p.80 信頼できるCAをサイト所有者がピン留め:CAがどんなドメインの証明書も許可なく発行できる問題を解決する
p.80 2つの標準:HTTP Public Key Pinningと動的ピンニング(dynamic pinning)(詳細は10.3節)[1]
p.80 Chromeの独自の仕組み、静的ピンニング(static pinning)(詳細は10.3節)

【DANE】
p.80 DNSSECとTLSの橋渡しをする
p.80 DNSSEC(Domain Name System Security Extensions):DNSの拡張、安全性の検証を可能にする。
p.80 DANEは公開鍵ピンニングにも利用できる
p.80 DANEでパブリックCAを迂回し、DNSをTLSの認証に使えるようにもなる

【ソブリン鍵(Sovereign Keys)】
p.80 公的に検証可能なログに記録される鍵(ソブリン鍵)でドメイン名を主張する
p.80 CAやDNSSECなど既存のセキュリティ基盤を拡張する提案
p.80 2011年に公表されたが、鍵を失った場合の回復手段がなく、まだアイデアの段階(参考:EEFのプロジェクトページ

【MECAI(Mutually Endorsing CA Infrastructure)】
p.80 公証人の一種で、その基盤をCAが運用するというもの
p.80 煩雑な作業を全てサーバで行う:プライバシーとパフォーマンス改善が期待できる
p.80 CAが証明書の鮮度を保証するバウチャーを発行するVA(Voucher Authorities)を兼ねる(参考
p.80 2011年発表、まだアイデアの段階

脚注
  1. 証明書のハッシュや証明書そのものをアプリに組み込むピンニングの場合、証明書期限切れ時にアプリのアップデートができなくなる問題があった:dynamic pinningの場合ハッシュを外部からダウンロードすることでこれを解決する。参考 ↩︎

KIDANI AkitoKIDANI Akito

【CT(Certificate Transparency)】
p.81 パブリックな証明書の監査とモニタリングのためのフレームワーク
p.81 RFC6962として2013年に策定、Googleが推進[1][2]
p.81 2015年、Chromeは新規発行されるEV証明書がCT対応することを要求:最終的に2018年4月までにすべてのパブリックな証明書がCT対応することを要求[3]

【TACK(Trust Assurances for Certificate Keys)】
p.81 2012年に登場したピンニングの一種。サーバが提供した署名鍵をピン留めする。
p.81 長期的な署名鍵を利用すればCAに依存しなくて済むようになる(その分処理が大変)
p.81 他の提案と違い、HTTP以外でもTLSで保護される全てのプロトコルで動作
p.81 本書執筆時点でサポートしているクライアントはない[4]

【CAA (Certification Authority Authorization)】
p.81 ドメインに対する証明書を発行できるCAをドメイン名所有者が指定できるようにする提案
p.81 2013年RFC6844 DNS CAA Resource Recordとして提案
p.81 CA/B Forumが2017年3月にCAA利用を義務付けている

p.81 多くのアイデアは特別な状況でうまくいかず、実装されなかった
p.81 公証人をベースにした提案は注意すべき点が多すぎる:公証人の意見が一致しない場合、複数の証明書を同じ名前でデプロイしているときの誤検出、など
p.82 公開鍵ピンニングはかなり見込みがある:2011年にGoogleがデプロイしたおかげでDigiNotarによるセキュリティ侵害が発覚した(他の障害もいくつか見つかった)
p.82 DANEは優れた解決策ではあるが、DNSが中央集権的で政府の影響を避けられないことが課題:ブラウザベンダーは2021年現在も乗り気ではなさそう(参考:caniuse.com

p.82 2つの方向性:既存システムの改善路線とサイト毎にセキュリティを選べる路線
p.82 改善路線:Mozillaの働きかけによる2012年のCA/B ForumとBaseline Requirements発表や、GoogleによるCT推進、2010年以降のモニタリング活動
p.82 そこそこのセキュリティを可能にする新しい技術:公開鍵ピンニング、HSTS(HTTP Strict Transport Security)、CSP(Content Security Policy)、強制的なOCSPステープリング

脚注
  1. Googleのサイトによれば、2021年7月25日時点でGoogleのCTログに追加された証明書は 14,128,739,375 件。同サイトではCTログを閲覧することもできる。 ↩︎

  2. Let's EncryptのCTログは https://oak.ct.letsencrypt.org/2021/ct/v1/get-roots にアクセスするとみることができる。参考 ↩︎

  3. このメールによれば、Chrome 68(2018年7月リリース)以降、2018年4月30日よりあとに発行された証明書について、CT対応が必須となっている模様。 ↩︎

  4. 2021年現在でも、Googleで1年以内の記事を検索して関連する記事が出てこないため、状況は変わってなさそう。 ↩︎

KIDANI AkitoKIDANI Akito

第4章 PKIに対する攻撃

4.1 VeriSign社のMicrosoft社に対するコード署名証明書

p.83 2001年1月、身元を詐称した何者かにVeriSignがMicrosoftのコード署名証明書2通を発行
p.83 同年3月にMicrosoftが対応策を実施したあとに公になった

https://internet.watch.impress.co.jp/www/article/2001/0323/verims.htm

p.84 VeriSignは間違いに気づき証明書を失効したが、CRL配布ポイントが指定されておらず不十分な対策
p.84 Miscrosoftはこの証明書をブラックリストに入れるOSアップデートを公開

4.2 Thawte社とlogin.live.com

p.84 2008年夏、セキュリティ研究者がThawteの証明書検証処理をだまして証明書を取得
p.84 Thawteのドメイン認証に電子メールを利用している+Microsoftが@live.comのアドレスを誰でも利用できた
p.84 この問題はDEFCON17で発表された[1]
p.84 同様の問題がMicrosoftのlive.fiドメインで2015年にも発生

脚注
  1. DEFENSE CONDITION 29が2021/08/05-08開催される ↩︎

KIDANI AkitoKIDANI Akito

4.3 StartCom社のセキュリティ侵害(2008年)

p.85 セキュリティ研究者がStartComのドメイン名検証Webサイトの欠陥を悪用して迂回
p.85 StartComが目立つWebサイトドメインをブラックリストとして管理していたため発覚:paypal.comとverisign.com
p.85 数分ですべて失効された

4.4 CertStar(Comodo社)のMozilla証明書

p.85 StartComのCTOが同社への攻撃のあと他のCAでの同様の問題を発見
p.85 ComodoのパートナーのCertStarがドメイン名を全く検証せずに証明書を発行していた
p.85 mozilla.dev.tech.cryptoメーリングリストで大きな議論に
p.85 Comodoが検証し、証明書を失効させた

4.5 偽造RapidSSL

p.85 2008年SotirovとStevensを中心とする研究グループがRapidSSLを利用して偽造CA証明書を取得:任意のドメインの証明書発行可能
p.86 MD5の衝突を利用した攻撃で偽造CA証明書を取得

MD5とPKIに対する攻撃年史

  • 1991年:MD4を置き換えるものとしてMD5が設計される
  • 〜1996年:MD5が一般化、弱点を示す兆候が見られ、別のハッシュが推奨される
  • 2004年:MD5の完全な衝突の実例が示される:攻撃自体はまだ現実的ではない
  • 2005年:異なる2通の証明書が同じMD5ハッシュ値(同じ署名)を持つ現実的な衝突の実例が示される(RSA鍵空間が異なるのみで、証明書の本人性は同一であった)
  • 2006年:選択プレフィクス衝突攻撃(当初はターゲット衝突と呼ばれた)で、本人性が異なる証明書を衝突させることに成功:実質的にMD5は完全に破られた
  • 2008年:MD5による署名での証明書発行が止まらず:SotirovとStevensを中心とする研究グループによる攻撃の事例
  • 2012年:Flameとよばれるマルウェアが中東のネットワークで感染拡大:Windows Updateのコード認証の仕組みを攻撃するため、Microsoft社のCA証明書にMD5の衝突を利用していた。2-5年程度、政府の後ろ盾を受け活動していたとみられる。
KIDANI AkitoKIDANI Akito

4.5.1 選択プレフィクス衝突攻撃

p.87 ディジタル署名:データのハッシュ値に対して署名をするため、同じハッシュ値をもつ証明書が作れれば、一方に対する署名は他方についても有効となる
p.87 攻撃者にとっての制約:CAに証明書を送っても署名してもらえず、公開鍵やドメイン名などの情報を送ってCAが証明書を生成する。(乗り越えることは可能)
p.87 異なるデータのハッシュ値を一致させるために必要な条件

  • オリジナルの文書(潔白な文書)の先頭部分(プレフィクス)を事前に知っていること
  • オリジナルの文書(潔白な文書)の途中に衝突ブロックを配置できること

p.87 ファイルの末尾は同一にする必要がある

4.5.2 衝突する証明書の構成方法

p.87 ディジタル署名の作成における制約

  1. 証明書は攻撃者が作成したCSRの情報を使って、CAが作成する
  2. 証明書の構造はX509v3仕様で決まる:攻撃者は構造を予測することが容易
  3. CSRから証明書にコピーされる部分は攻撃者が制御可能:特に、公開鍵はそのままコピーされる。ランダムに見える衝突ブロックを作りやすい
  4. CAが追加する情報の一部に攻撃者が影響を及ぼすことが可能:例、証明書の失効時刻などは予測しやすい

p.88 以上より、プレフィクス=公開鍵よりも前にある全てのフィールド、衝突ブロック=公開鍵
p.88 プレフィクスの大半は、同じCAから発行された別の証明書とCSRに記載の情報から入手可能
p.88 CAが制御できるプレフィクス(シリアル番号と期限切れとなる日付):これも予測は不可能ではない(後述)
p.88 攻撃のプロセスは以下のようになる

  1. CAで生成される証明書のプレフィクスを確認し、CSRのフィールドを決める
  2. 不正な証明書に必要となるプレフィクスを構成する
  3. 証明書のサフィックスを決める
  4. 1-3のデータで衝突ブロックを構成する
  5. CSRを組みてててCAに送り本物の証明書を入手する
  6. 偽造プレフィクス、2つ目の衝突ブロック、サフィックス、本物の証明書から取り出した署名から、不正な証明書を組み立てる

p.88 RapidSSLへの攻撃では、証明書の利用に支障がないように、衝突ブロックにComment拡張(処理の際に無視される)が利用された

KIDANI AkitoKIDANI Akito

4.5.3 プレフィクスの予測方法

p.89 シリアル番号と期限切れ日時をどのように予測したか?:運とCAからの手助け

  1. RapidSSLでは完全自動化された証明書発行処理が6秒で完了[1]:期限切れ日時を秒単位で予測しやすい
  2. RapidSSLではシリアル番号に乱数を使わず単純なカウンタを利用:2つの証明書を連続して取得すると2つ目のシリアル番号が予測できる

p.89 MD5署名の証明書を発行するCAは2008年当時6つ存在[2]
p.89 研究チームは200台のPlayStation 3クラスタで1日かけて衝突を生み出した
p.89 CAの処理が最も空いている日曜夕方に攻撃を実行:4週間かけて成功

4.5.4 後日談

p.89 被害を最小限にする対策

  1. 不正な証明書は期限が過去の日時となるように生成
  2. トラストストアを管理する団体(MSやMozilla)に発表前にブラックリストへの追加を依頼
  3. RapidSSLにも事前に警告:攻撃の発表から数時間でSHA1へ移行

p.91 各種対策を経て安全であると判断してから選択プレフィクス衝突技法が完全に公開された
p.91 攻撃費用:たった657ドル(PS3クラスタを除く。RapidSSLでは1通新規69ドル、更新45ドルだが、無料で20回まで証明書を再発行できた)
p.91 Amazon EC2だと2万ドル近くかかる。改良すれば2000ドル。

p.90 真正な証明書と衝突した証明書の構成(研究報告スライドより)

研究報告サイトより

脚注
  1. 研究報告スライドによれば、Acceptボタンを押してから6秒後に完了するとのこと。 ↩︎

  2. 研究報告スライドによれば、他にFreeSSL、TrustCenter、RSA Data Security、Thawte、verisign.co.jpがMD5で証明書を発行していた。 ↩︎

KIDANI AkitoKIDANI Akito

4.6 Comodo社のリセラーのセキュリティ侵害

p.91 2011年の一連の事件のうち、最初のもの。
p.91 3/15:ComodoのRAの一つがセキュリティを侵害され7つのサイトの証明書が発行された(addons.mozilla.org、google.com、login.live.comなど)が、数時間で全て失効された
p.91 このときOCSPレスポンダへのアクセスはlogin.yahoo.comの証明書について2回だけだった(が、MITM攻撃によりOCSPへのトラフィックが抑圧されていた可能性もあるので全容は不明)
p.91 3/16:Comodoから関連団体へ通知、パッチを当てる処理が開始(例:FirefoxなどMozillaのコアモジュールで証明書を明示的にブロックChromeもFirefoxとやや異なる証明書をブラックリストに追加
p.91 3/22:3/15に攻撃があったことが、Comodo、Mozilla、Microsoftなどにより公開された
p.92 3/26:Comodoがさらに2社のリセラーでセキュリティ侵害があったことを公表、不正な証明書は発行されず
p.92 3/26:ComodoHackerを名乗る攻撃者が、数ヶ月に及ぶ大規模な攻撃であると発表(このあと他の攻撃にも関与)

p.92 2008年のCertStar事件以降、証明書発行を自由に行えるパートナーは9%となっていたが、本件以後は0%となった
p.92 本件で判明した重大な問題:Comodo社が実態に即した脅威モデル(RAそのものを攻撃対象として侵害する)を検討していなかった

KIDANI AkitoKIDANI Akito

4.7 StartCom社のセキュリティ侵害(2011年)

p.92 6/15:おそらくComodoHackerによってStartComが狙われた
p.92 これをうけて、StartComは証明書発行を1週間停止:不正な証明書の発行やルート鍵の危殆化はなし
p.93 StartComからの詳細報告はなく、2011/9/9のEddy NiggのStartComのサイト上のブログ記事が詳しい[1]

4.8 DigiNotar社

p.93 オランダのCA、DigiNotar:オランダ政府の電子政府プログラムのPKI部分を担当
p.93 7/19:ComodoHackerによるMITM攻撃を受け、531通の不正証明書を発行[2]
p.93 8/29:問題が公になる
p.93 9/5:ComodoHackerが声明を発表
p.93 9/19:DigiNotarは廃業し、自己破産

4.8.1 発見の経緯

p.93 8/27:イランのGmailユーザーが日常的なサービスダウンを報告、背景にMITM攻撃があったことが判明
p.93 Chromeの公開鍵ピンニングの仕組みがMITMを検出し防いだ
p.93 その後、約30万のIPアドレス(イランの全IPアドレスに相当)が影響を受ける大規模なものであることが判明

4.8.2 CAの信用失墜

p.93 オランダ政府は外部セキュリティコンサルタントFox-IT社を投入
p.94 1週間後の9/5にFox-ITの報告

  • CAサーバがネットワーク越しにアクセス可能
  • 全CAサーバが単一のWindowsドメインに所属、パスワードルールがあまり強くない
  • パッチが当たっていないソフトウェアがインストールされていた
  • サーバにアンチウイルス対策がない
  • IPS(侵入防止システム)は導入されていたが攻撃をブロックできず
  • ネットワークの中央ロギングシステムはなし
    p.94 1年後の2012年8月全容のレポート
  • 2011/6/17:Webサーバ上のコンテンツ管理アプリが破られる
  • 2011/7/1:ルート鍵の素材が保存される最重要ネットワークセグメントへ侵入
  • 2011/7/10:128通の不正な証明書を手に入れるプログラムを実行、最終的に53組織531通に(ログの改竄もあり、正確な数は不明)
  • 2011/7/19:DigiNotarが侵入に気づき、外部コンサルタントと協力してシステムをクリーンアップ、何通かの証明書を失効させたが、外部へは報告せず

p.94 証明書に利用された名前:有名Webサイト、CA、政府機関など

  • CA:comodo, digicert, globalsign, thawte
  • 有名サイト:google, mozilla, microsoft, skype, twitter, wordpress, facebook
  • 政府機関:cia[3], mossad[4], sis[5]
    p.94 Webサイトを騙る目的ではなく、メッセージが目的の証明書も(どんな暗号でも解ける、Torも破る、名もなき兵士、など)

4.8.3 MITM攻撃

p.95 不正な証明書にはDigiNotarのOCSP情報が埋め込まれており、OCSPレスポンダのログにより証明書の追跡が可能(MITM攻撃でOCSPサーバへのトラフィックが妨害されていない場合)
p.96 2011/8/4:大規模な運用の兆候が見られた
p.96 2011/8/29:各ブラウザでDigiNotarのルート証明書が失効され、不正な証明書が一掃された
p.96 この間、恒常的に攻撃があったわけではなく局所的だった(DNSキャッシュポイズニングが利用された?)
p.96 攻撃者の目的はGmailのパスワード収集
p.96 Google社を騙る証明書のOCSPリクエストの95%はイラン国内から、残りは世界各地のTor出口ノード、プロキシ、VPN

4.8.4 ComodoHackerの犯行声明

p.96 2011/9/5:Pastebinで声明、問題の証明書で署名したバイナリファイルや、攻撃の詳細の一部(後の公式報告と一致)が含まれた
p.97 イランにおける攻撃への関与は不明:MITMについてはあまり触れず

脚注
  1. startcomのブログは現在は消えているが、Internet Archiveで閲覧できる。このとき狙われた証明書はGoogle、Twitter、Yahooのものとのこと。 ↩︎

  2. 詳細はWikipediaに詳しい ↩︎

  3. Central Intelligence Agency、中央情報局 ↩︎

  4. イスラエル諜報特務庁 ↩︎

  5. Secret Intelligence Service(秘密情報部、通称MI6) ↩︎

KIDANI AkitoKIDANI Akito

4.9 DigiCert Sdn. Bhd.

p.98 2011年11月:Entrustと契約するマレーシアの中間CA、DigiCert Sdn. Bhd.[1]が危険な弱い証明書22通を発行していると判明[2]

  • 512ビットの鍵:総当たりで因数分解可能(一般数体ふるい法(GNSF:General Number Field Sieve Method)を使えば、総当たり不要[3]
  • EKU拡張による用途制限がない:契約上、Webサイト証明書しか発行できなかったが、制限がないためコード署名などにも利用可能
  • 失効情報がない

p.98 総当たりで破られた公開鍵がマルウェアへの署名として利用されて問題が発覚
p.98 Entrustが中間CA証明書を失効させブラウザベンダーに通達
p.98 米国DigiCert社は本件と無関係であるプレスリリースを出した

脚注
  1. Sdn. Bhd.はSendirian Berhad(スンディリアン・ブルハド)の略で、非公開株式有限責任会社の意味。参考ページ ↩︎

  2. Bugzillaのチケットによると、マレーシアの政府機関や中央銀行などもこのCAを利用していたらしい。 ↩︎

  3. GNSFをサクッと説明してくれる資料は見つからなかったorz。「近年の素因数分解について」(2008)によると、1990年に提唱された方法で、1999年時点で512ビット(155桁)、2005年時点で663ビット(200桁)、2010年のNTTの発表によると768ビット(232桁)、Qiitaの記事によると2020年に829ビットの素因数分解に成功している。 ↩︎

KIDANI AkitoKIDANI Akito

4.10 Flame

p.99 Flame:Flamer、Skywiperとも。2012年5月に研究者による分析開始(攻撃は数年前に開始したとみられる)
p.99 当時最も高度なマルウェア:Lua+SQLiteで、ネットワークアナライザ、マイク有効化、ファイル検索などの20の攻撃モジュールからなる。
p.99 イラン、イスラエル、パレスチナ、スーダン、シリア、レバノン、サウジアラビア、エジプトなど中東の1000のシステムで発見された
p.99 発見後、作成者が全インスタンスを消去する自動消滅コマンドを発行:いくつかは捕捉され分析された

4.10.1 Windows Updateに対するFlameの攻撃

p.100 Windows Updateを攻撃し、ローカルネットワーク上のWindowsに拡大:過去に知られていない暗号技術を利用した攻撃
p.100 IEがWPAD(Web Proxy Auto-Discovery)を利用しており、FlameはプロキシとしてHTTPトラフィックを乗っ取り、Windows Updateサーバとして悪意あるコードをインストールさせた
p.100 Windows Updateは平文を使っていたが、コード署名で防御していた:FlameはMD5衝突を利用して、この署名を偽造

4.10.2 Windowsターミナルサービスに対するFlameの攻撃

p.100 Windowsターミナルサービス、現在の呼び名はリモートデスクトップサービス。
p.100 問題の1つは、証明書の入手が用意であったこと

  • サービスのCA証明書がWindows UpdateのCAと同じルート証明書を利用:サービスのCA証明書から、顧客ごとの下位CA証明書を発行
  • サービスのCAを、ライセンス処理以外に、なぜかコード署名に利用できた
  • 下位CA証明書には使用方法の制限がない=親の制限が継承される(コード署名に利用できる)

p.100 上記をまとめると、リモートデスクトップサーバの顧客ごとに無制限の下位CA証明書が与えられ、これを利用してコード署名が可能だった
p.100 ただし、下位CA証明書にはHydraというcriticalな独自拡張があり、Windows Vista(2007年リリース)以降では動作せず:Windows XPでのみ動作した(XPではcriticalな拡張を無視するため)

4.10.3 MD5を悪用するFlame

p.101 もう1つの問題:証明書の署名がMD5
p.101 ターミナルサービスの設計時点でMD5は安全でないことが知られていたにもかかわらず、ルート証明書の署名に利用された
p.101 RapidSSLへの選択プレフィクス衝突攻撃が利用された
p.101 RapidSSLのときとの違いは、シリアルナンバーが連番ではないものの、起動時からの経過時間(ミリ秒
)に固定の2バイトと連番が続く形式だった。
p.101 ミリ秒単位の正確さが要求されるので、強力なクラスタが必要だったはず
p.102 研究者Marc Stevensによれば、新しい差分パス構築アルゴリズムが使われたとされる

暗号解析への対策

p.102 MD5は言うまでもなく、SHA1も弱いことがわかっている(まだしばらくは使われそう)[1]
p.102 Marc Stevensが発明した対策:耐暗号分析(counter-cryptanalysis)[2][3]

  • 暗号分析による攻撃(例:選択プレフィクス攻撃)により不可避的に生じる異常を検出できる
  • (なので)MD5/SHA1によるハッシュ値が偽造でないことを検証できる
脚注
  1. 2017年のGoogleの記事によれば、SHA-1が初めて衝突した。この記事の著者はMarc Stevensら。Stevens自身はGoogleではなくアムステルダムにあるオランダ国立数学情報科学研究所に所属。Googleと同研究所が2年間かけて共同研究した。この記事によると、MD5はスマホで30秒で破れるらしい。SHA1は110台のGPUで1年かかるとのこと。なお、この発表の翌日、FirefoxはSHA1による証明書を無効化すると発表した(参考)。 ↩︎

  2. 論文の要約によれば、Stevensはこの技術を利用して、Flameが選択プレフィクス衝突攻撃を利用していることをつきとめたらしい。 ↩︎

  3. こちらのブログで日本語で解説されていたがよくわからなかったorz。このブログで、Marc StevensによるC++製OSS暗号分析ツールHashclashが紹介されている。 ↩︎

KIDANI AkitoKIDANI Akito

4.11 TURKTRUST社

p.102 2012年10月:Google Chromeの公開鍵ピンニングが問題を発見、ユーザーが問題の証明書チェーンをGoogleに送付、Googleが調査
p.102 証明書の出所はトルコのTURKTRUST社[1]:数日で証明書は失効
p.103 2011年8月のシステム移行時にSSL証明書ではなく、誤って中間CA証明書を発行していた:そのうちの1つEGO社で MITM機能を持つファイアウォールを導入し誤ってGoogle社の証明書のクローンが作成された
p.103 GoogleとOperaではTURKTRUST社のEV証明書を無効化[2]

脚注
  1. Googleのブログによると、*.google.comドメインの証明書が、中間CA証明書によって発行された。この中間証明書のルート証明書がTURKTRUSTのもの。 ↩︎

  2. Googleのブログによると、HTTPS接続はできるがEV証明書としての表示を無効化したということのよう。 ↩︎

KIDANI AkitoKIDANI Akito

4.12 ANSSI

p.103 ANSSI:Agence nationale de la sécurité des systèmes d’information(フランスの国家機関)
p.103 2013年12月同機関から発行された下位CA証明書をChromeのルートストアから削除、親CAについてはフランス国内のドメインの証明書のみとする制限を課した
p.103 同機関のネットワーク上でMITM機能が動作し、Googleに属する証明書が利用されていた:これもやはりChromeの公開鍵ピンニングで検出
p.103 ANSSIは人為的ミスであったと説明
p.103 MozillaとMicrosoftも問題があるCA証明書を無効化

p.104 ANSSIの問題点が明らかに

  • 多くの証明書に失効情報がない
  • 空だったCRLに数千の証明書が一気に登場
  • Baseline Requirements非対応:早くて2015年12月[1]
脚注
  1. その後の対応状況を調べてみたがよくわからず。FirefoxのトラストストアやmacOSのキーチェーンには含まれていない様子。2021年8月22日現在のANSSIのサイトはCertignaというフランスのCAの発行した証明書を利用していた。2021年8月9日時点でオランダのPKIoverheidが「EU最後の政府運用CA」と言われているので、フランスも2013年から2021年の間にCAをやめたのだろうか。 ↩︎

KIDANI AkitoKIDANI Akito

4.13 インド情報工学センター

p.104 NIC:National Informatics Centre of India
p.104 2014年7月、Google所有のドメインへの証明書が同センターの中間CAから発行された
p.104 発行元はインド政府の認証管理局(CCA:Controller of Certifying Authorities):のちに、NICのCAがセキュリティ侵害を受けたことを暴露
p.104 証明書は失効され、NICのCAは証明書発行を停止
p.104 ChromeはCCA発行の証明書についてインドのドメイン(.in)の7つのサブドメインに限定[1]

脚注
  1. Googleのブログによれば、もともとWindowsのルートトラストストアにCCAのルート証明書が含まれており、Windows上のChrome(とInternet Explorer)が影響を受けた。Firefoxは独自のトラストストアを利用するため影響をうけず、また、Windows以外のOS上で動作するChromeも影響をうけなかった。 ↩︎

KIDANI AkitoKIDANI Akito

4.14 広範囲に及ぶTLSの傍受

p.104 ローカルにインストールされたソフトや、ネットワークプロバイダによるTLSの傍受は日常的(2017年のDurumericらの研究)

4.14.1 Gogo社

p.104 Gogo社:航空機内インターネット接続サービスを手がける
p.104 2015年1月、ChromeのセキュリティチームのAdrienne Porter Feltにより、同社が暗号化されたトラフィックを傍受し不正な証明書を利用していることを指摘
p.105 不正な証明書に関する警告が出る状態
p.105 同社は機内の帯域制御に必要と釈明するも、後日傍受を完全停止

4.14.2 Superfishとその仲間

p.105 2015年2月、AdrienneがLenovo社のSuperfishプリインストール問題を指摘
p.105 Superfish:利用者の全トラフィックを傍受
p.105 OSのルート証明書ストアに不正なルート証明書を追加し、プロキシ経由でこれを利用しMITM
p.105 全てのユーザーで同じルート証明書を利用していたことが問題(ユーザーごとに異なる証明書なら良い):秘密鍵を抜き出したユーザーは他のユーザーを攻撃できてしまうため
p.105 SuperfishのプロキシはTLS1.1しかサポートしていなかった(当時の最新はすでにTLS1.2)
p.106 MITMにより、どんなサイトに接続しても証明書の警告がでない状態だった
p.106 Facebookの調査では、カザフスタンからのFacebookへの接続の4.5%がSuperfish経由
p.106 MicrosoftとLenovoが協力して、Superfishを25万台から数日で削除
p.106 その後、SuperfishでのKomodiaが開発したTLS傍受用SDK利用が判明:類似の製品としてComodo社のPrivDogなど、同様のものはたくさんある

KIDANI AkitoKIDANI Akito

4.15 CNNIC

p.107 CNNIC:China Internet Network Information Center(中国ネットワークインフォーメーションセンター)
p.107 2015年3月、MCS(Mideast Communication Systems)社がCNNICから試験的中間CA証明書を受領
p.107 誤ってGoogle社を騙る証明書が発行される
p.107 Google・MozillaはCNNICのルート証明書を失効:中間CA証明書を、杜撰な組織に発行した責任を問う形
p.107 失効以前の有効な証明書についてはホワイトリストを利用して許可し続けた

KIDANI AkitoKIDANI Akito

4.16 Symantec社によるテスト用証明書

p.107 2015年9月、Symantecがgoogle.comに対するEV証明書を権限なしに発行したとするGoogleのブログが公開
p.107 Symantecは2人の社員のミスを主張し彼らを解雇
p.107 EV証明書に必須であったCTのログを精査したところ、総数2645の証明書が誤発行されていたことが判明
p.108 GoogleはSymantecのインシデント報告の遅れを理由に、2016年6月以降同社の証明書全てをCTに登録することを要求

KIDANI AkitoKIDANI Akito

4.17 WoSign社とStartCom社の問題

p.108 2011年のDigiNotarが最初の信用失墜したCA:2016年のWoSignとStartComがそれに続く
p.108 WoSign:中国の大手CA、無料証明書発行でシェア拡大。イスラエルのStartComを買収
p.108 WoSignの運用について問題があるとしてMozillaが調査:2015年1月からルート証明書の不適切利用が明らかに[1]
p.108 決定的であったのはStartCom買収に伴う所有権移転を公表しなかったこと:Mozilla Root Store Policy
の8. CA Operational Changes
に違反
p.108 信頼に値しないとしてMozilla、Apple、Googleがルートストアから排除
p.108 StartComは2008年2011年にも問題をおこしていたが、WoSignとインフラの大部分を共有し、組織的上下関係があったことで同じく排除された
p.109 排除の手法:ある日以降に発行される証明書を拒否(Google、Mozillaは2016年10月21日に設定)
p.109 WoSign/StartComのその後:他のCAの再販業者として操業継続

脚注
  1. MozillaのWikiによると、シリアルナンバーの重複や、多数のBaseline Requirements違反があった。その中には、Baseline Requirementsで認められていない暗号アルゴリズムである中国のSM2を利用した証明書発行もあった。 ↩︎

KIDANI AkitoKIDANI Akito

4.18 Various Validation Incidents

StartEncryptでの検証失敗

p.109 2016年6月、StartCom社が新しいプロトコルStartEncryptを発表:Let's Encryptと同社のACMEプロトコルへの対抗策
p.109 Let's Encryptが非対応だったOV証明書EV証明書に対応
p.109 1ヶ月で致命的な欠陥が見つかる

  • 詐称した証明書が取得可能
  • リダイレクトが許可されたWebサイトの証明書を取得可能だった

p.109 StartComはStartEncryptを破棄してACMEの利用を発表

Comodo社のトップレベルドメイン証明書の誤発行

p.109 2016年9月、sbとwww.sbの両方に有効な証明書を発行:ホストの所有者が異なる
p.110 この前にもtbドメインで同じ問題があったが、こちらは所有者が同じだった

KIDANI AkitoKIDANI Akito

Comodo社が検証の過程でOCR利用

p.110 2016年10月、研究者がComodoの検証プロセスの問題を発見:OCRに脆弱性(1とl)
p.110 WHOISがスパム対策として画像でメールアドレスを提示するためOCRを利用
p.110 のちにComodoはOCR利用を中止

GoDaddy社が404レスポンスで所有権を認めた問題

p.110 2017年1月、GoDaddyが問題を公表:権限なしに8850の証明書が発行された
p.110 ドメイン検証の際に、GoDaddyが乱数を送付、ドメイン所有者がWebサイトに同じ乱数を出力していた
p.110 もともとHTTP(S)でリクエストを送ってサイトをチェックしていたが、検証のバグにより、404ステータスの際にも(URLに乱数が含まれたため)検証が通過するようになってしまった[1]
p.110 この検証方法はCAB Forumで問題視され、Ballot 169[2]で投票の結果、Baseline Requirementsが改善された。

脚注
  1. 詳細はmozillaのメーリングリストに。 ↩︎

  2. Ballotは投票の意味。 ↩︎

KIDANI AkitoKIDANI Akito

4.19 SHA1の終焉

p.111 2017年2月SHA1が破られた:以前見た通り]
p.111 SHA1の署名が同一となる2つのPDFが公開された:このPDFをSubversion/Gitに投入しようとするとVCS自体が壊れてしまった
p.111 発表者の1人、Marc Stevensは選択的プレフィクス攻撃の検出機能のあるSHA1ライブラリを公開

4.19.1 同一プレフィクス攻撃(identical-prefix attack)

p.112 研究者チームが使用した手法:Marc Stevensがこの数年前に理論上の手法として提示していた
p.112 SHA1:任意の長さの入力から、160ビットの出力を生成するハッシュ関数。内部的に512ビットのブロックに分割される
p.112 関数の内部状態を操作するために、近衝突ブロック(near-collision block)2つを生成する:2つ目で完全に衝突させる

KIDANI AkitoKIDANI Akito

第5章 HTTPおよびブラウザの問題

p.113 ブラウザベンダーが旧来のWebサイトの扱いに苦慮した結果多くの問題が発生

5.1 サイドジャッキング

p.113 セッションハイジャックの一種:暗号化されてないトラフィックからセッションIDを抜き出す攻撃
p.113 無線LANやローカルネットワーク上で簡単に実行できる
p.113 暗号化をしていてもつけ込まれる可能性
p.113 1. 設計上のセッション漏れ:パスワード認証時のみ暗号化しているケース
p.113 2. ミスによるセッション漏れ:HTTPのコンテンツが混在するケース(クッキーから漏洩:混在コンテンツについては本章後半で)
p.114 クッキー以外にも、リクエストパスやパラメータとしてセッションIDが埋め込まれる
p.114 2007年8月Robert GrahamとDavid MaynorがFerretとGamsterという攻撃を自動化するツールをリリースし、本攻撃が知られるようになった
p.114 2010年Eric ButlerがFirefoxアドオンFiresheepを公開:簡単にサイドジャッキングを実行可能[1]、有名なサイトの完全な暗号化を促進
p.114 対策・警告ツールとしてBlackSheep[2]、FireShepherd[3]、Idiocy[4]、CookieCadgerなどが登場

脚注
  1. この記事によると、「ローカルWiFiネットワークをスキャンして、Facebook、Twitter、Google、Amazon、(中略)といった、数多あるWebサービスにログインしているユーザーを洗い出し、そのユーザーに成りすますことをいとも容易く可能に」するとのこと。 ↩︎

  2. この記事によると「Blacksheepはこのクッキーを横取りし、Firesheepには偽のログイン・クッキーを渡すと同時に、ユーザーにFiresheepの存在を警告し、攻撃者が使用しているデバイスのIPアドレスを表示する」。 ↩︎

  3. 配布サイトの説明によると、これは小さなコンソールプログラムで、近くのワイヤレスネットワークにFireSheep停止のためのパケットを大量に送り込み、FireSheepを0.5秒ごとに停止させるらしい。一歩間違えると大惨事なような...と思ったらこちらの記事では使わない方がいいと書かれている。やはり。 ↩︎

  4. こちらの記事によると、「HTTPセッションハイジャックを実施してTwitterアカウントを取得し、そのユーザになりすまして自動的に警告文章をつぶやくツール」らしい。実際のツイートはたぶんこれ。IdiocyはPythonで実装されている。 ↩︎

KIDANI AkitoKIDANI Akito

5.2 クッキー窃取

p.115 Webサイトの完全TLS化だけではサイドジャッキングは防げない:クッキーの暗号化も必要
p.115 WebサイトのTLS化時:クッキーにSecure属性を設定すべき[1]
p.115 Secure属性がない場合のMITM攻撃

  • 犠牲者が平文のHTTPリクエストを送るのを待つ
  • リクエストに、標的のWebサイトのポート80へリダイレクトさせる
  • Webサイトからクッキーが発行される
  • クッキーからセッションIDを取得

p.115 ポート80を開いていなくても、「http://www.example.com:443」にリダイレクトさせ、そのリクエストで暗号化されずに送信されるクッキーを取得できる
p.116 この問題はMike Perryが提唱:DEFCONで発表しCookieMonsterという概念実証ツールを発表

脚注
  1. MDNの説明を引用:HTTPS プロトコル上の暗号化されたリクエストでのみサーバーに送信され、安全でない HTTP では決して送信されないため、中間者攻撃者が簡単にアクセスすることはできません。(中略)ただし、Secure によってクッキー内の機密情報へのアクセスをすべて防げると思ってはいけません。例えば、クライアントのハードディスクへアクセスすることで読み取られる可能性があります。 ↩︎

KIDANI AkitoKIDANI Akito

5.3 クッキーを書き換える攻撃

p.116 Secure属性がついて読み取れないクッキーは書き換えれば攻撃できる

5.3.1 HTTPクッキーの基礎

p.116 HTTPの拡張:小さなデータをブラウザで保持する
p.116 Set-Cookieレスポンスヘッダで指定:名前と値、存続期間などのメタデータ

Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=None

// 以下の例のように、複数のディレクティブも利用することができます。
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

参考:MDN

p.116 ブラウザはクッキージャーにクッキーを格納:HTTPリクエストのたびに適用するクッキーを全てCookieリクエストヘッダで送信

Cookie: name=value
Cookie: name=value; name2=value2; name3=value3

p.117 クッキーの仕様はとても貧弱で悪用可能:2011年にようやくRFC6265
p.117 セキュリティの観点から大きく2つの問題:セキュリティを弱める振る舞いを許す、Same-Origin Policyと矛盾する

●ホスト名のスコープがゆるい

p.117 クッキーはプロトコル、ポートをまたいで共有され、サブドメインでもすべて有効になる
p.117 Domain属性を指定していない場合、親ドメインへも拡大可能[1]
p.117 つまり、悪質なサーバが同じドメイン名で別ホスト名を利用しているサイトにクッキーを注入可能
p.117 Same-Origin Policy(同一生成元ポリシー)はプロトコルとホスト名とポートが完全一致している文脈でセキュリティを規定するため、相容れない

●サーバがメタデータを見られない

p.117 クッキーの出所がわからない:自分が発行していないクッキーも拒否できない

●安全なクッキーにおける同一性の欠如

p.117 Secure属性があってもなくても同じドメイン名で格納され、同一視される
p.117 クッキーの名前、ドメイン、パスがマッチすると、Secure属性がないクッキーでSecure属性ありクッキーを上書きできる

p.117 以上のように、HTTPクッキーは完全性が保証されていないという問題がある

5.3.2 クッキー書き換え攻撃

p.118 クッキー書き換え=削除orクッキー注入(インジェクション)
p.118 インジェクション以外にも、クロスサイトクッキー、クッキー固定化Fixation、クッキー強制、クッキートスtossingなどと呼ばれる

■クッキー排除攻撃Cookie Eviction

p.118 ブラウザのクッキージャーへの攻撃:クッキージャーは個々のクッキーサイズ(通常4kb)やドメイン毎のクッキー数(通常数十個)、クッキーの総サイズが制限されている
p.118 攻撃者はダミーの巨大なクッキーを送信することで本物のクッキーを消し、攻撃に必要なものを残す

■クッキーを直接注入する攻撃

p.118 暗号化されていると読めないが、Secure属性の有無によらず名前空間が同じなので上書きは可能
p.119 リダイレクトなどでHTTPリクエストを犠牲者に強制し、レスポンスでクッキーを改竄する
p.119 これにはクッキーのメタデータの情報が必要:例)TomcatならPathが常にルート(/)など

■親戚関係にあるサブドメインからのクッキー注入

p.119 blog.example.comにXSS脆弱性があるとwww.example.comのクッキーも書き換え可能

document.cookie = 'JSESSIONID=FORCE_ID; domain=example.com';

p.119 このように別部門・組織がそれぞれサイトを運用する状況は危険

●先頭のクッキーになる

p.120 本物のクッキーと偽物のクッキーがある場合、以下のように送信される

Cookie: JSESSION_ID=REAL_ID; JSESSION_ID=FORCED_ID

p.120 とりうる攻撃は2つ:クッキージャーをあふれさせるorメタデータをごまかす
p.120 ブラウザはより特定的なクッキーを先頭におくので、以下のようにPath属性を使う

document.cookie = 'JSESSIONID=SECOND_FORCE_ID; domain=example.com; path=/admin';
↓
Cookie:  JSESSION_ID=SECOND_FORCED_ID; JSESSION_ID=REAL_ID; JSESSION_ID=FORCED_ID
●親戚関係にあるサブドメインを使ってクッキーを上書きする

p.120 ドメインが明示されていないクッキーはホストオンリーとなる
p.120 ホストオンリーのクッキーとドメインが明示されたクッキーは一致しないため、攻撃できない
p.121 blog.example.comからwww.blog.example.comやexample.comへのクッキーは発行できるが、兄弟レベルのサブドメイン(例:www.example.com)にはクッキーを発行できない
p.121 つまり、クッキーのドメインをわざわざルート(example.com)にしていると上書きされてしまう
p.121 IE11では暗黙のドメイン設定と明示的なドメイン設定が区別されないので上書きされうる

●親戚関係にあるサブドメインを捏造してクッキーを上書きする

p.121 MITM攻撃により、www.blog.example.comにリクエストを強制し、blog.example.comへのクッキーを発行できる

5.3.3 影響

p.121 XSS脆弱性、CSRF対策の回避、アプリの操作、セッション固定化[2]といった影響がある

5.3.4 緩和策

p.121 攻撃者に偽装させず、受け取ったクッキーの真正性を確認するための対策をとればよ

●サブドメインを網羅したHSTSを採用する

p.122 HSTS:HTTP Strict Transport Security、ホスト名に対し暗号化を強制する[3]
p.122 MITMによるクッキーインジェクションができなくなる
p.122 問題点1:まれにブラウザがサポートしていない[4]
p.122 問題点2:親戚関係のサブドメインが侵害されたり、信用できない組織が運用している場合は対処不可

●クッキーの名前にプレフィクスをつける

p.122 クッキープレフィクス:より安全なクッキーの階層。本書執筆時点では標準化されていないがFF, Chromeで利用可能[5]
p.122 例:__Secure-でSecure属性を強制、__Host-でホストオンリーを強制

●クッキーの完全性検証

p.122 クライアントから受け取ったクッキーが自サイトで発行したものか確認することが最大の防御
p.123 HMAC[6]や暗号化(JavaScriptで使わない場合)が有効
p.123 ユーザーごとに検証できるようにしないと、攻撃者が取得したクッキーを注入できてしまう
p.123 XSSでクッキーが盗まれる場合、HMACや暗号化では防げない:トークンバインディング(同一のブラウザでしかクッキーを使えないようにする)[7]などが必要

脚注
  1. これはおそらくIEなど特定のブラウザの話かと思われる。徳丸さんのブログ参照。 ↩︎

  2. IPAのサイトの解説がわかりやすかった。 ↩︎

  3. MDNの解説:includeSubDomainsを指定すると、サブドメインにもHTTPSを強制できる ↩︎

  4. MDNの解説:Android用のOperaがサポートしていないっぽい? ↩︎

  5. 2021年現在もまだドラフトの模様。https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis ↩︎

  6. 署名つきCookieについてはGoogleのCloud CDNのサイトが詳しい。https://cloud.google.com/cdn/docs/using-signed-cookies?hl=ja ↩︎

  7. こちらのブログが詳しい。簡単に言うと、ブラウザ側で鍵ペアを接続先ごとに生成し、接続先に公開鍵を送り、クッキーの送信時に秘密鍵で署名する、というイメージ。しかし、こちらのQiita記事によると、Financial-grade API(OAuth 2.0とOpenID Connectを基盤とした、金融業界向けのより高いセキュリティーのための技術要求事項)では、このようなProof-of-possession Tokenとしてはクライアント証明書によるmTLSのみが認められ、Token Bindingは最終版にて削除されたとのこと。 ↩︎

KIDANI AkitoKIDANI Akito

5.4 HTTPSストリッピング

p.123 URLのプロトコル部分を省略して入力するとブラウザは平文でまずアクセスする:これを悪用するとHTTPSストリッピング攻撃が可能
p.123 MITMをしかけると、HTTP通信を外部から読み取って、機微情報の観測や通信の書き換えも可能
p.124 攻撃の手口:標的サイトとよく似たアドレスを使う

p.124 sslstripというツールが公開されている

5.5 MITM証明書

p.125 中間者攻撃の方法3つ

●検証フローの裏をかく

p.125 TLSの検証フローにバグがあれば裏をかける

●不正な証明書

p.125 2008年のRapidSSLのように不正な証明書を偽造して入手する
p.125 1024ビットの弱い鍵を使う証明書を総当たりで破る、など
p.125 犠牲者のネットワークが制御下にある場合、不正な証明書を見破ることは事実上不可能:OCSP+MITMという攻撃もある、多くのブラウザがOCSP失敗を無視する

●自己署名証明書

p.125 ユーザーが警告を無視するのに期待する攻撃者

p.125 sslsniff:https://github.com/moxie0/sslsniff
p.125 SSLsplit:https://github.com/droe/sslsplit[1]

脚注
  1. まだTLS1.3はサポートしていなさそう。部分的なサポートのPRが出ている程度。 ↩︎

KIDANI AkitoKIDANI Akito

5.6 証明書の警告

p.125 TLSの認証は主に証明書による:接続先のホスト名を保証する証明書が返ってくることを期待する
p.125 不正な証明書を受け取ったら、接続を中止すべき
p.126 しかし、ブラウザは接続中止してくれない:証明書の警告を出し、ユーザーに問題を押し付ける

5.6.1

p.126 不正な証明書が使われる原因はいくつかある

●仮想ホストの設定ミス

p.126 同じIPで、平文のサイトと暗号化されたサイトを運用するミス:平文のサイトにhttpsでアクセスしようとすると異なる証明書
p.126 原因の一部:サイトから暗号化に対応しているかどうか表明する仕組みがない
p.126 平文のサイトをホストするIPアドレスのポート443を閉じるのがよい
p.126 2010年の筆者の調査:1億1900万ドメイン対象、2265万のサイトがHTTPS、72万のサイトのみが正しい証明書を持っていた
p.126 名前が一致する証明書を持っているものの、他の理由で信用できないサイトが3割(20万)

●名前が全て網羅されていない

p.126 証明書にホスト名が不足:www.example.comの他にexample.comも証明書に必要

●自己署名証明書とプライベートCA

p.126 これらはMITM攻撃で使われる証明書と区別がつけづらい
p.127 それでも利用される理由

  • 証明書の購入、設定、更新作業が大変
  • 2010年台前半まで証明書が高価だった
  • 証明書が無料であるべきと考えて購入を拒否する人がいる
●アプライアンス製品で使われている証明書

p.127 Webベースの管理インターフェースには安全な通信が必要:しかし、出荷時には利用するホストやIPアドレスは不明
p.127 エンドユーザーが証明書をインストールする必要があるが、利用頻度やUI不足で設定されない

●期限切れの証明書

p.127 筆者の調査では不正な証明書の57%が期限切れ:所有者の更新忘れ

●設定ミス

p.127 サーバは完全に有効なチェーンを用意する必要がある[1]
p.127 ブラウザは不完全なチェーンの再構成を試みるが、他のユーザーエージェントは実行しない

p.127 2013年の研究:39億のHTTPS接続を調査、1.54%で証明書の警告
p.127 イントラネットや社内アプリなどはもっと酷いはず

5.6.2 証明書の警告の効果

p.127 2008年に筆者がMozillaに提案:不正な証明書を迂回できなくしてはどうか→拒否された
p.128 MITM攻撃の最大の問題:ユーザーが気付かない、証明書の警告が「よくあること」になっている
p.128 今日の主要なブラウザは介在型interstitial/割り込み型interruptiveでブラウザウインドウ全体で警告を出す[2]
p.128 Firefoxのテレメトリによると、不正な証明書のサイトへ進んだユーザーはわずか33%
p.128 Chromeの場合ユーザーの70%が警告をやりすごしている:警告のデザイン変更(Firefoxの真似)により、56%まで定価

5.6.3 やりすごせる警告ではなく例外を挙げる

p.129 Firefoxは証明書の例外設定をさせているから成功している?[3]
p.129 自己署名証明書は必ずしも悪くない:自宅のサーバなど、個人利用や理解ある技術者の小規模な集団なら利用可
p.129 TOFU(Trust On First Use)=鍵継続管理(key continuity management):誰かが攻撃して証明書を入れ替えたら、例外に設定されていない証明書なので再び警告が表示されるようになる

5.6.4 緩和策

p.129 エコシステム全体についてはできることはほとんどない
p.129 HSTS(HTTP Strict Transport Security)に対応する:ブラウザに証明書の警告を抑制する機能もある[4]

脚注
  1. SSL PulseのMonthly Scan: August 04, 2021の結果によると、13万以上のサイトを調査したところ、1.6%にあたる2238サイトが不完全な証明書チェーンを提示した。 ↩︎

  2. 本書ではSafariがダイアログで警告を出すのみ、とされているが、2021年現在のバージョン14.1.2ではウインドウ全体で警告を出す方式になっていた。確認に利用したのは https://badssl.com/ ↩︎

  3. MacのFirefox92.0で確認したところ、Chromeの動作とさほどかわらず2クリックで問題のあるサイトを開くことができた...どこかのバージョンで動作が変わったのだろうか ↩︎

  4. RFC6797でHSTSが定められており、8.4で証明書に問題がある場合ユーザーエージェントは接続を終了すべし(MUST)とされている。 ↩︎

KIDANI AkitoKIDANI Akito

5.7 セキュリティインジケーター

p.129 ウェブページのセキュリティに関する付加的情報を提供するUI

  • このページは暗号化されている
  • このWebサイトを運営している法人が判明している[1]
  • このページでは不正な証明書が使われている
  • このページの暗号化に何らかの問題がある

p.130 ユーザーの大部分が無視しているとの調査結果
p.130 モバイルプラットフォームでは画面が小さいことから、セキュリティインジケーターも省略されがち:フィッシングに対して脆弱
p.132 インジケーターは無用ではない:2014-16のSHA256証明書への移行に有効な役割を果たした

脚注
  1. EV証明書はChromeでは2019年9月、Firefoxでは2019年10月以降もはや専用表示がなくなった。参考:https://ssl.sakura.ad.jp/column/ev-ssl2/ ↩︎

KIDANI AkitoKIDANI Akito

5.8 混在コンテンツ

p.132 TLS:単一の接続を保護し、ネットワークレベルでデータを安全にたもつ
p.132 FTPやHTTP:複数の接続が違いに関係する、安全かどうかはユーザーエージェントの実装にかかっている
p.132 HTTPS:HTML、画像、スタイルシート、JavaScriptなどが複数の接続で取得される、混在コンテンツと呼ばれる問題が発生する
p.132 混在コンテンツ:同一ページだけでなく、Webサイト全体でも起こる(平文のページと安全なページの混在 → HTTPSストリッピング攻撃、機微情報の未暗号化などの問題につながる)

5.8.1 根本的な原因

p.132 Webの猛烈な進化スピードに起因

●パフォーマンス

p.132 初期SSLの劣悪なパフォーマンス:平文が好まれていた
p.133 かつての解決策は高価なハードウェアアクセラレータのみ
p.133 現在の懸念は主にネットワークの遅延(パフォーマンスの詳細は9章で後述)

●マッシュアップ

p.133 自分のところだけで全てのコンテンツを提供するわけではない
p.133 特殊な例:Google Analyticsなどサードパーティのコードを利用
p.133 コストは劇的に下がるが、セキュリティにとっては悪夢:サイトの制御がほぼ完全にサードパーティの手に渡る
p.133 Webサイトのユーザーも、自分のデータがどこに格納されるかわかりにくくなる
p.133 サードパーティが暗号化されていないことも:Google AdSenseがHTTPSに対応したのが2013年9月[1]

●インフラのコスト

p.133 CDNの流行:1箇所からWebサイトを配信してもパフォーマンスが悪く競争力を維持できない
p.134 暗号化の負担:CPU、RAM、キャッシュ、証明書、鍵管理など
p.134 IPアドレスの問題:HTTPSを利用する仮想ホスティング(1つのIPで複数サイト)[2]は未対応のクライアントが一定数ある[3]ため、サイトごとにIPが必要だが、IPv4アドレスの欠乏問題もある。

p.134 上記の背景により、ブラウザが混在コンテンツの問題を許容してきた

5.8.2 影響

p.134 2種類のリソース:受動的混在コンテンツ(画像など:混在表示mixed displayとも)と能動的混在コンテンツ(HTMLやJS:混在スクリプティングmixed scriptingとも)
p.134 能動的混在コンテンツがより危険:HTML、CSS、Flash[4]、Java[5]など
p.134 受動的混在コンテンツもページの完全性を損ねる:偽の画像によるフィッシングや、ブラウザの脆弱性を利用してスクリプトとして処理させる(古いIEにおけるコンテンツスニッフィングの悪用)
p.135 同一ホストでの混在コンテンツの場合、受動的攻撃者はクッキーにもアクセス可能

5.8.3 ブラウザでの対処

p.135 当初のブラウザベンダー:サイトの開発者が正しく実装すると考えていた
p.135 2010年代になって、混在コンテンツを制限するようになったが妥協的:受動的混在コンテンツを許可し、能動的混在コンテンツは許可しない

  • Internet Explorer:95年のIE5から混在コンテンツを検出し警告、IE9で能動的混在コンテンツをブロック(2011年)
  • Firefox:混在コンテンツの検出と警告は可能だった、能動的混在コンテンツのブロックはバージョン23から(2013年)
  • Chrome:バージョン38から能動的混在コンテンツをブロック(2014年)
  • Safari:バージョン9から混在コンテンツをブロック(2015年)

p.136 ブラウザのプラグインは特に危険:好きなリクエストをなんでも発行できる
p.136 W3Cで混在コンテンツの扱いを標準化する動き:2021年現在まだドラフト版

5.8.4 混在コンテンツの広がり

p.136 2011年の筆者の調査:Alexa[6]上位100万サイト中、22.41%に安全でないコンテンツが利用されていた
p.136 2013年の研究:Alexa上位10万サイト中の約2万サイト中、43%が混在コンテンツを含む

5.8.5 緩和策

p.137 サイトを正しく実装することで対処できる
p.137 Webサーバーの設定による緩和策2つ:HSTSとCSP

HSTS(HTTP Strict Transport Security)

p.137 強制的に安全なリソースを取得させる仕組み
p.137 ユーザーのミス(ポート80でアクセス)やページの実装ミス(リンクがhttp)にも対応可能
p.137 リソースが自分の管理下にあるホストにある場合のみ有効

CSP(Content Security Policy)

p.137 安全でないリソースをブロックできるセキュリティポリシー
p.137 ブロック以外にも検出を報告する機能もある(report-onlyモード)
p.137 安全でないリンクを暗号化が有効なリンクに書き換えることもできる((upgrade-insecure-requests ディレクティブ)[https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests])

脚注
  1. WikipediaによればGoogle AdSenseのリリースは2003年。https://en.wikipedia.org/wiki/Google_AdSense ↩︎

  2. Server Name Indicationのこと。 ↩︎

  3. 例えば、JavaがSNIに対応したのはJava 7(2011年7月リリース)。 ↩︎

  4. 2019年のChromeをはじめとして、順次各ブラウザでFlashのサポートが打ち切られている。参考:https://www.itmedia.co.jp/pcuser/articles/2012/01/news043.html ↩︎

  5. Wikipediaによると、JavaアプレットについてはChromeが2015年、Firefoxが2017年にサポートを打ち切り、2017年にリリースされたJava 9では非推奨となった。 ↩︎

  6. Alexaはアマゾンの子会社でウェブサイトを順位づけするシステム。https://www.alexa.com/ ↩︎

KIDANI AkitoKIDANI Akito

5.9 Webの完全な暗号化を目指して

p.138 Webの暗号化は部分的、個々のサイト管理者が責任を持つ
p.138 大手ブラウザベンダーを中心にWebの完全な暗号化を目指す過渡期
p.138 2017年のGoogleとMozillaの報告:全体の50%がHTTPS
p.138 ブラウザベンダー:暗号化が有効な場合だけブラウザの先進的機能を提供し、サイト管理者に暗号化を促す

●セキュリティ警告のスコープを拡大

p.138 セキュリティインジケータの範囲拡大:HTTPが安全でないという警告
p.138 2017年1月、Chrome56とFirefox51からHTTPでパスワードやクレジットカード番号入力するページに警告表示
p.138 Googleは2017年中にHTTPでフォーム送信するページに対象拡大を宣言[1][2]

●暗号化だけで利用可能な強力な機能

p.139 利用者の地理データのような情報をHTTPで使えなくする[3]

脚注
  1. このサイトによると、2017年10月のChrome 62で対応が完了した模様。 ↩︎

  2. Googleのブログによると、2020年9月にはChrome 86で混在フォーム(HTTPSページ上のHTTPフォーム)についても同様の警告が表示されるようになった。この機能については問題があったらしく、直後のバージョンで無効化され、そのつぎのバージョンで再度有効化されたと言われている。参考サイト ↩︎

  3. 註に挙げられていたページによると、地理データ以外には、デバイスの向き、AppCache、通知、カメラやマイクの利用、Encrypted Media Extensions (EME:HTMLで動画などのコンテンツを保護するためのAPI標準化仕様)が挙げられている。 ↩︎

KIDANI AkitoKIDANI Akito

5.10 EV証明書

p.139 SSLの黎明期にはすべての証明書発行時に厳格な検証が要求された
p.139 現在そのような検証はEV証明書についてのみ必要:2007年にCA/B Forumが規定
p.139 EV証明書の利点

  • ドメイン所有者の本人性が厳密に確認されて、その識別子が証明書にエンコードされている
  • 検証手順が手動であり、証明書の捏造が困難[1]

p.139 これらの利点は現実的な恩恵となるかは疑問:セキュリティインジケータは気付かれていない

参考:最近のChromeだとグリーンのインジケータはなくなり、EV証明書を表示する前に法人名が表示される

p.140 問題点:ページ単位で検出されるので、スクリプトなどのリソースでもEV証明書が利用されているとは限らない(コストを理由に、サブドメインではDV証明書が使われるケースも多い)
p.140 これを利用した攻撃手法がある(詳細はこちらのスライド

  • 他のドメイン名のサイトからリソースを持ち込む:詐称したDV証明書でマルウェアを仕込む
  • クッキー泥棒:既存のクッキーを盗む、新しいクッキーを設定する
  • 永続的マルウェア注入:キャッシュの利用を強要し、マルウェアを稼働させ続ける
脚注
  1. 厳密には捏造ではないが、別の州に同名の会社を設立して証明書を取得する手法が紹介されていた。参考サイト ↩︎

KIDANI AkitoKIDANI Akito

5.11 証明書の失効

p.140 理想的には証明書の失効確認すべきだが、とても難しい問題

5.11.1 クライアントの対応が不十分

p.140 ブラウザがなにをどのタイミングで実行しているか理解するのは困難
p.140 メーリングリストやバグ報告、ソースコードを掘り起こす必要がある
p.140 OCSPステープリングのような新しい機能はなかなかブラウザに搭載されない[1]
p.141 コマンドラインツールでは失効はもちろん証明書検証もままならない:デフォルトでは失効確認をしない[2]
p.141 失効確認は設計どおりには機能していない:2011年の一連の事件で、確実な手段はブラックリストだった

5.11.2 失効確認の標準化に伴う主な問題

p.141 CRLとOCSPの設計上の欠陥3つ

●証明書と問い合わせの分断

p.141 CRLもOCSPもCAが割り当てたシリアル番号で問い合わせる=手元の証明書とCAが参照している証明書が同一か完全には確認できない

●ホワイトリストではなくブラックリスト

p.141 CRLはブラックリストそのもの。初期のOCSPはCRLから情報を取得していた。
p.141 OCSPレスポンスでは問い合わせられたシリアル番号について何も知らなくてもgood(not revoked)扱いにしていた:2013年8月以降CA/B Forumで禁止された[3]
p.141 2011年のDigiNotar事件で現実の問題に:OCSPレスポンダが全てを失効扱い+ルート証明書をブラウザから削除して対応

●プライバシー

p.142 自分がどんなWebサイトを閲覧しているか、という情報がCAに知られてしまう
p.142 CRLには大量の証明書が含まれるので侵害度合いはやや低め
p.142 OCSPレスポンダへのトラフィックモニタリングでWebサイト閲覧同行を監視できる
p.142 この問題への対策がOCSPステープリング

5.11.3 CRL

p.142 当初の失効確認の唯一の仕組み:スケールしづらいためOCSPが作られた

■CRLの長さに伴う問題

p.142 GoDaddy社によると、同社の失効情報は2007年に158Kバイト、2013年に41Mバイト。
p.142 近年は、複数のCRLを利用することでサイズを抑えている。
p.143 大量データのダウンロードはモバイルで問題:帯域やCPU
p.142 差分CRL(delta CRL):Windowsでサポートされているがあまり使われていない

■CRLの新鮮さ

p.143 Netcraftの調査によると、McAfeeのサイトの中間証明書が失効に気づかれないまま放置された
p.143 このときOCSPの情報はなく、CRLには失効情報があったが無視されていた
p.143 Baseline Requirementsの記述

  • CAは12ヶ月に一度CRLを再発行しなければならない
  • CAは下位CA証明書の失効から24時間以内にCRLを再発行しなければならない
  • nextUpdateフィールドの値をthisUpdateから12ヶ月より後にしてはいけない

参考:RFC5280

   CertificateList  ::=  SEQUENCE  {
        tbsCertList          TBSCertList,
        signatureAlgorithm   AlgorithmIdentifier,
        signatureValue       BIT STRING  }

   TBSCertList  ::=  SEQUENCE  {
        version                 Version OPTIONAL,
                                     -- if present, MUST be v2
        signature               AlgorithmIdentifier,
        issuer                  Name,
        thisUpdate              Time,
        nextUpdate              Time OPTIONAL,
        revokedCertificates     SEQUENCE OF SEQUENCE  {
             userCertificate         CertificateSerialNumber,
             revocationDate          Time,
             crlEntryExtensions      Extensions OPTIONAL
                                      -- if present, version MUST be v2
                                  }  OPTIONAL,
        crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
                                      -- if present, version MUST be v2
                                  }

p.143 中間証明書のCRLは12ヶ月間新鮮とみなされる

  • 数百万サイトで利用されるので、長期間(12ヶ月)保持したい
  • CRLはオフライン保管のルート証明書の鍵で署名されるので、頻繁には利用できない

p.143 サーバー証明書のCRLの場合は、10日以内とされている

5.11.4 OCSP

■OCSPリプレイ攻撃

p.144 OCSPオリジナル設計はリプレイ攻撃に強い:ナンスが含まれる
p.144 オリジナル設計はスケールせず、軽量版OCSPプロファイルでバッチ処理とキャッシュが導入:キャッシュのため、リプレイ攻撃耐性は断念
p.144 現在、クライアントはナンスを使わないし、サーバーも無視する
p.144 唯一の防御は有効期限のみ(nextUpdate):数時間から数日、DV証明書の方が長くEV証明書が短い
p.144 Baseline Requirementsの規定では10日間(中間証明書は12ヶ月)

■OCSPレスポンスの隠蔽

p.144 OCSPレスポンスの失敗はブラウザでは無視される
p.144 これを利用して、全てのレスポンスを失敗させて失効確認を隠蔽できる
p.145 OCSPレスポンスは通常電子署名が付与されるが、失敗の場合署名がつかない:これを利用して攻撃が可能[4]

■クライアントにおけるOCSP対応

p.145 古いプラットフォームやブラウザはデフォルトではOCSPを利用しない:Windows XP、10.7以前のOS X
p.145 iOSはOCSPと(おそらくCRL)をEV証明書にしか使わない
p.145 Chromeは2012年にOCSPを打ち切り、CRLSetsと呼ばれる軽量な仕組みを採用(CA証明書ばかりなので、セキュリティ的には劣る)
p.145 MozillaはCRLSetsを参考にOneCRLを導入

■レスポンダの可用性とパフォーマンス

p.146 OCSPは初期の問題から、ブラウザ側でソフトフェイル(失敗しても無視)扱い
p.146 Netcraftのレポートでパフォーマンスがわかる:https://uptime.netcraft.com/perf/reports/performance/CRL

●可用性

p.146 ハードフェイルの場合にはレスポンダがダウンすると自サイトもダウン
p.146 ソフトフェイルの場合も、ネットワークパフォーマンスに影響
p.146 認証が必要なWiFIネットワーク(キャプティブポータル)でOCSPレスポンダに到達できないことが多い

●パフォーマンス

p.146 そもそもOCSPはパフォーマンスが悪い:OCSPが終わらないと本来のWebサイトへ進めない
p.147 OCSPレスポンダのパフォーマンスがCAの最大の差別化要因

●正確性

p.147 あるCAでは証明書の生成からOCSPレスポンダの更新まで40分かかる場合も(その間リクエストが失敗する)

■強制OCSPステープリング

p.147 パフォーマンスと可用性を満たすかもしれないOCSP Must-Staple
p.148 ステープリングを強制できれば、証明書の盗用(攻撃)にOCSPレスポンスも必要となる
p.148 2016年のFirefox 45から強制OCSPステープリングが利用可能[5]

■サーバ側でのOCSP対応

p.148 主要OSSでOCSPステープリングがデフォルト有効なWebサーバはない[6]
p.148 対照的に、MicrosoftのIISではデフォルトで有効になっている
p.148 実装が貧弱:レスポンスの取得が遅い
p.148 OCSPレスポンスが得られない場合にHTTPリクエストへの応答を停止するサーバーも。

脚注
  1. Wikipediaによると、OCSPステープリングにはIEが2012年、Firefoxが2013年、Chromeが2014年に対応した。 ↩︎

  2. curlの場合、OCSPステープリングの機能はデフォルトではオフになっている。参考サイト。この機能には2017年に脆弱性が見つかっている↩︎

  3. Baseline Requirements 1.8.0, 2021.08.25の57ページに該当の記述あり。同じページには、セキュリティ対応のため、そのようなシリアル番号についてのリクエストを監視すべしとの規定も。 ↩︎

  4. RFC6960によると、malformedRequest、internalError、tryLater、sigRequired、unauthorizedのステータス。 ↩︎

  5. メーリングリストのやりとりで、Chromeについては2016年時点では対応予定なしとされている。その後2019年にもやはり対応されていない様子。 ↩︎

  6. 参考:ApacheのOCSPステープリング設定方法 ↩︎

KIDANI AkitoKIDANI Akito

第6章 実装の問題

p.149 ソフトウェアは必然的に安全でない

  • 言語とライブラリがセキュリティを考慮して書かれていない
  • コストの問題:時間的にも金銭的にも最小コストが要求されセキュリティがないがしろにされがち

p.149 「暗号は破られるのではなく迂回される」

6.1 証明書の検証における欠陥

p.149 証明書に関して確認すべき2つの事項

  • 証明書が意図したホスト名に対して発行されているか
  • 証明書が有効で信頼できるか

p.150 証明書を確認するコードを開発する際、実在の証明書チェーンだけで開発すると重要な検証機能を作り損ねる
p.150 検証の細部に悪魔が潜んでいる

  1. サーバ証明書が意図したホスト名に対して有効か
  2. 証明書チェーンの全てが、期限切れになっておらず、かつ、署名が有効である
  3. 中間CA証明書は場合によって以下の要求を満たす必要がある
  • 他の証明書に対する署名に使えること
  • 他のCA証明書に対する署名に使えること[1]
  • 末端の証明書におけるホスト名の署名に使えること

参考:RFC5280


      id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
      KeyUsage ::= BIT STRING {
           digitalSignature        (0),
           nonRepudiation          (1), -- recent editions of X.509 have
                                -- renamed this bit to contentCommitment
           keyEncipherment         (2),
           dataEncipherment        (3),
           keyAgreement            (4),
           keyCertSign             (5), // 他の証明書に対する署名に使えるかどうかのビット
           cRLSign                 (6),
           encipherOnly            (7),
           decipherOnly            (8) }

   id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }
   BasicConstraints ::= SEQUENCE {
        cA                      BOOLEAN DEFAULT FALSE, // 上記5が立っている場合、TRUE=本証明書はCA証明書
        pathLenConstraint       INTEGER (0..MAX) OPTIONAL } // 0の場合、他のCA証明書には署名できない

p.150 他にも、鍵が協力か、弱い署名アルゴリズム(MD5やSHA1)が利用されていないか、なども確認必要

脚注
  1. 註に、エンドエンティティ証明書を発行するCA証明書は下位CA証明書を発行できてはいけない、とされている。しかし、RFCを掲載しているIETFのサイトの中間証明書「Starfield Secure Certificate Authority - G2」はpathLenConstraintの値が設定されていない=CA証明書を発行できる、ように見える。これって良いのか?CA/B Forumの使ってるGo Daddyの中間証明書も同様にpathLenConstraintの値がない。Baseline Requirementsでは「MAY be present」だからいいのか? ↩︎

KIDANI AkitoKIDANI Akito

6.1.1 ライブラリとプラットフォームにおける検証の不備

p.150 ライブラリの証明書検証の欠陥は決して珍しくはない

・Microsoft社のCryptoAPIにおけるBasic Constraints確認に欠陥があった事例(2002年

p.150 ルート証明書が信頼できればどんな証明書でも信頼してしまうバグ:中間者攻撃が可能になる
p.150 Windows以外にも影響(Mac上のOfficeやIEにも影響:参考、KDEのデフォルトブラウザKonquerorにも影響(どうして?))
p.151 2002年にMoxie Marlinspikeが発見、同氏はsslsniffというMITMツールを公開

・GnuTLSにおける証明書チェーン検証の欠陥(2008年)

p.151 チェーンの末尾にルート証明書を追記すると不正な証明書が信頼されてしまうバグ:中間者攻撃ができる
p.151 本来はチェーンの一部でないので無効

・OpenSSLにおけるDSAおよびECDSA署名検証の欠陥(2009年)

p.151 Googleのセキュリティチームが発見
p.151 DSAとECDSA署名検証時の境界値チェックが不十分で、MITM攻撃の可能性あり

・iOSにおけるBasic Constraintsの確認に欠陥があった事例(2011年)

p.151 iOS4の一部のバージョンで、証明書を下位CA証明書として利用してよいかをチェックしていなかった:エンドエンティティ証明書で証明書が発行できてしまっていた

・iOSおよびOS Xにおける接続検証の欠陥(2014年)

p.151 iOS6とiOS7、OS X 10.9のTLS接続認証のバグ:DHEおよびECDHEの接続がMITMで乗っ取られる
p.151 この記事によれば、goto文が連続で出現する行があったため、以降の行がデッドコードとなったことが問題:同記事によれば、TLS1.2の場合は別の関数を利用するため影響なし
p.152 TLSハンドシェイク中の問題のため、脆弱性のあるクライアントだけに標的を絞ることが可能
p.152 独自のライブラリを利用するChromeやFirefoxなどのクロスプラットフォームなアプリは影響を受けず

・GnuTLSにおける証明書チェーン検証の欠陥(2014年)

p.152 2つの脆弱性が発見された
p.152 1つは、X509バージョン1(既に廃止されたバージョン)の証明書が任意の中間CA証明書として扱われるバグ
p.152 もう1つは、先のApple同様のバグ:参考例外が発生すると間違ったエラーコードが返るが、証明書検証が成功してしまう

・OpenSSLのChangeCipherSpecインジェクション攻撃(2014年)

p.152 能動的な攻撃者がChangeCipherSpecメッセージ(値が1だけのメッセージ)をインジェクションして、予測可能なマスターシークレットのネゴシエーションを強制できる
p.152 ChangeCipherSpecはハンドシェイクプロトコルの一部でないため認証されないのが原因(=他のハンドシェイクメッセージのように、ハッシュ化して擬似乱数生成関数にかけてverify_dataとして扱われない)
p.153 攻撃にはクライアントとサーバーがOpenSSLである必要があるが、クライアントとしてはあまり利用されていないので影響は限定的:例、Android 4.4
p.153 SSL Pulseによれば、影響のあったサーバーは全体の14%

・OpenSSLにおける証明書チェーン検証の欠陥(2015年)

p.153 ネットワーク上の攻撃者が末端の証明書を中間CA証明書として利用可能だった
p.153 Metasploit:Metasploit Frameworkは、ペネトレーションテストを行うためのフレームワーク。

フランケン証明書によるテスト

p.153 研究者らが特殊加工した「フランケン証明書」でライブラリをテスト:元の証明書にランダムにフィールドや拡張を追加・変更したもの
p.153 それほど使われていないライブラリ(GnuTLSなど)で深刻な問題が見つかる

KIDANI AkitoKIDANI Akito

6.1.2 アプリケーションの検証における欠陥

p.153 2012年の論文でSSL証明書の検証に欠陥状況が明らかに
p.154 MITM攻撃に対して安全でない:Amazon EC2のJava SDK、AmazonとPaypalの小売事業者向けSDK、osCommerceやZenCart、Ubercartなどの統合型ショッピングカート、Javaの各種ミドルウェア(Apache Axis, Axis2, Coudhaus XFire)など
p.154 ライブラリのAPIがそもそもまずい:例)OpeSSLでホスト名の検証は開発者が実装する必要あり
p.154 安全でないライブラリがある、それを使い続けているのが問題
p.154 正しい実装もある(JavaのJSSE)が、開発者は開発中は証明書検証を無効にしがちなので本番環境で有効に戻っているか心配

6.1.4 ホスト名の検証における問題

p.154 ホスト名に対し証明書が有効であるかを検証するのは困難
p.154 複数の脆弱性の事例:2009年のBlack Hat USAで2件報告あり
p.154 C/C++で文字列の終端を示すNullバイトが問題

Hello world!\0
(\0がNullバイト)

p.155 一方で証明書はASN.1の標準記法に従う=文字列の表現方法がC/C++と異なる

Nullバイト(\0)を含む不正なホスト名:
    www.paypal.com\0.thoughtcrime.org

p.155 Black Hat USA 2009でのMoxie Marlinspikeの攻撃報告

  1. 上記ホスト名の証明書(なりすまし対象ホスト+Nullバイト+攻撃者のドメイン)
  2. CAはthoughtcrime.orgを有効なドメインと確認し証明書発行(現在は対策済みのはず)
  3. 脆弱なクライアントへのMITM攻撃が可能

p.156 脆弱なクライアントの例:GnuTLS、NSSライブラリ[1]、MicrosoftのCryptoAPI
p.156 結果として、IEやFirefoxも影響あり

脚注
  1. Netscope社が始めたNetwork Security Servicesプロジェクト。現在はMozillaが管轄。参考:https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS ↩︎

KIDANI AkitoKIDANI Akito

6.2 乱数生成における欠陥

p.156 乱数生成は安全な通信にとって本質的な構成要素
p.156 鍵の生成には乱数が必要、鍵はTLS接続のたびに生成される
p.156 欠陥のある乱数生成器(RNG)では256ビットの鍵長でも32ビットの鍵空間しかない場合も:総当たり攻撃が可能になってしまう

6.2.1 Netscape NavigatorにおけるRNGの欠陥(1994年)

p.156 SSLの設計をしたネットスケープコミュニケーションズ社のフラッグシップ製品
p.156 ブラウザ起動時からの経過時間をマイクロ秒で表した値と、OSのプロセスID・親プロセスIDから単純なアルゴリズムで乱数生成
p.156 1995年リバースエンジニアリングにより問題が発覚
p.156 攻撃の条件は2つ:当時のマシンで25秒程度で破れる(プロセスIDが分からなくても総当たりできるレベル)

  • 犠牲者と同じUnixマシンにアカウントをもつ(プロセスIDを特定できる)
  • ネットワークパケットからブート後の秒単位の起動時間を特定できる

参考:先日のKasperskyの件

KIDANI AkitoKIDANI Akito

6.2.2 DebianにおけるRNGの欠陥(2006年)

p.157 2006年9月に混入、2007年リリース、2008年に発見された問題
p.157 影響範囲が広い:Debianは広く普及しているLinuxディストリビューション、かつ、他のベース(例:Ubuntu)
p.157 根本原因:RNGにエントロピーを供給する箇所から1行コメントアウトしてしまった → エントロピーがプロセスIDのみの16ビット、事実上暗号がない(秘密鍵が容易に推測できる)
p.157 OpenSSHの鍵にも影響:配置場所が固定なので問題の有無を容易に確認できた
p.157 TLSの鍵への対応が困難:筆者はリモートからサーバの公開鍵をテストするツールを公開(SSL Lab)、CA側でも証明書リクエストの公開鍵をテストして対策
p.157 わかったこと:暗号に不慣れなひともOSSの暗号関係コードを修正してしまっている

p.158 乱数生成については、Windows 2000でも2007年に問題が発見されている:これはWindows XPにも影響があった。2013年にはNetBDS6.0(前年リリース)でも同様の問題があった。

KIDANI AkitoKIDANI Akito

6.2.3 組み込みデバイスにおける不十分なエントロピー

p.158 2002年の研究:RSA鍵(SSL/TLS用)の0.5%は安全ではない、DSA鍵(SSH用)の1.03%は安全ではない
p.158 主な原因はRNG
p.158 判明した問題はすべて組み込みデバイスのもの、サーバーで利用されるものは安全だった

●デフォルトの鍵であった

p.159 製品メーカーがデフォルトの鍵で出荷:全員が同じ鍵を使うため、台無し

●エントロピーが低いので鍵が使いまわされた

p.159 デバイスが初回起動時に鍵を生成する:エントロピーが不足し、予測可能となる
p.159 画面なしデバイスのLinuxのブートをシミュレーションした研究で、起動後数秒はエントロピーが不足することが明らかに

●素因数分解可能な鍵

p.159 RSA鍵の多くで、2つの素数のうち1つが同じ値であった
p.159 原因はエントロピーが小さい場合のOpenSSLコード

p.159 暗号処理を伴うアプリはOSから十分なランダムさを得られると想定していたが、そうではない:かつ、ランダムさが不十分であることをアプリ側で知ることができない
p.160 自分で対処するアプリ開発者もいるかもしれないが、うまくいかないのでやめた方がよい

KIDANI AkitoKIDANI Akito

6.3 Heartbleed

p.160 2014年4月に公となったOpenSSLの脆弱性:TLSプロトコル拡張のHeartbeetプロトコルの実装の欠陥
p.160 暗号技術の問題ではなく、OSSの品質の問題(OpenSSLの資金難と低いコード品質)
p.160 Linux FoundationがCore Infrastructure Initiativeという計画で資金提供:OpenSSLでは 2 名のフルタイム開発者が資金提供を受けて開発
p.160 一方で、OpenBSDプロジェクトではLibreSSLをフォークして急ピッチで改修を進めた

KIDANI AkitoKIDANI Akito

6.3.1 影響

p.160 返信するデータの長さの確認を怠っている(複数箇所で)=攻撃者がデータを引き出しうる
p.160 1回のHearbeatリクエストでサーバーのメモリ64Kバイトを取得可能
p.160 サーバーのメモリ上のデータ、特に秘密鍵や、セッションチケットの鍵、SSL/TLSセッションの鍵、パスワードなどが攻撃対象
p.160 OpenSSL 1.0.1〜1.0.1fまでが影響を受ける:Netcraftの推定では全世界で17%(約50万台)

KIDANI AkitoKIDANI Akito

p.161 SSL/TLS史上まれな迅速さでパッチ適用:問題の深刻さ、無料診断ツール、メディアの注目
p.161 脆弱性公表から1ヶ月後には1.36%に減少:メジャーなサイトでは0.8%
p.161 脆弱性公表当初は、秘密鍵の抜き取り可能性は証明されていなかったが、のちに可能と判明
p.161 VPNサーバからTLSセッション鍵を抜き出し、多要素認証の迂回に成功した事例も
p.161 その他:社会保障番号(カナダの税務署)、パスワード(イギリスの育児サイト)
p.161 攻撃ツールも多数:数分で脆弱なサーバを攻撃可能、自動で秘密鍵を発見できるツールも
p.161 バグの詳細は12.14節で後述

6.3.2 緩和策

p.161 一番有効なのはセキュリティパッチ
p.162 Heartbeatプロトコルを無効にしてビルドする対策もある:その後依存するパッケージの再コンパイルが必要
p.162 OpenSSLを利用するアプライアンス製品はそれぞれパッチ適用が必要
p.162 次の対策として、流出した可能性のあるデータの対策
p.162 データ対策1:サーバの秘密鍵の入れ替え、新しい証明書取得、古い証明書の失効
p.162 Netcraftの調査では、上記3つの対策のいずれかが漏れているケースが86%
p.162 データ対策2:セッションチケットの鍵を入れ替える、ユーザーのパスワード変更
p.162 ほかにも、データベースのパスワードなども流出した可能性がある
p.162 PFS(前方秘匿性)に対応していると救いがある:過去の通信が復号されないので。
p.162 OpenSSLクライアントも脆弱なので、サーバーからクライアントプロセスのメモリを抜き出すこともできる[1]

Ticketbleed

p.163 2017年2月、F5社の製品でHeartbleedと類似の脆弱性発見
p.163 セッションIDが32バイトでハードコーディングされていた:1バイトのデータを送りつけると31バイト分のメモリを入手可能

脚注
  1. pacemakerのreadmeによると、wget 1.15、curl 7.36.0、git 1.9.1などが脆弱 ↩︎

KIDANI AkitoKIDANI Akito

6.4 FREAK

p.163 2015年1月、OpenSSLから深刻度LowのCVE-2015-0204が公開:最高強度のRSAでハンドシェイク中に輸出用の弱いRSA鍵を受け入れてしまう
p.163 3月、研究者らが上記を利用してMITM攻撃に成功、深刻度をHighへ変更
p.163 FREAK=Factoring RSA Export Keys(輸出用RSA鍵の素因数分解)

6.4.1 輸出用暗号

p.163 1998年9月まで、アメリカは強力な暗号技術の輸出を規制:暗号処理は40ビット、鍵交換については512ビットまでに制限
p.163 上記制限におさめるための特別設計が輸出用暗号スイート
p.163 RSAではパフォーマンスのため認証と鍵交換が組み合わさっている:認証については強力な暗号が許可されていたが、認証と鍵交換を切り離せない
p.163 解決策:意図的に弱いRSA鍵を用意するようSSL/TLSプロトコルを拡張した(認証は強力なRSA、鍵効果では常に弱い512ビット鍵を利用)
p.163 Server-Gated Cryptography(SGC):米国企業向け証明書に特別な識別情報を埋め込み、クライアントに再ネゴシエーションをさせる方式
p.164 2001年1月、アメリカが輸出制限を緩和し、輸出用暗号スイートは廃止された
p.164 廃止されたが、ライブラリのコードには残っており、これが動作してしまった

KIDANI AkitoKIDANI Akito

6.4.2 攻撃

p.164 RSA鍵交換:ランダムなプリマスターシークレットをサーバの公開鍵で暗号化して、クライアントが送信
p.164 クライアントが輸出用暗号スイートを要求した場合

  1. サーバが弱いRSA鍵を生成、サーバの強い鍵で署名
  2. 署名した鍵をServerKeyExchangeメッセージでクライアントに送信
  3. クライアントは弱い鍵でプリマスターシークレットを暗号化して送信

p.164 鍵が弱くとも、署名が強力なので、ネットワーク上の攻撃者は鍵を能動的攻撃に利用できない
p.164 受動的攻撃は容易:全てのメッセージを記録し、弱い鍵に総当たり攻撃をしてプリマスターシークレットを取得して、通信を復号可能(数時間以内)
p.164 FREAK攻撃は、クライアントが輸出用暗号スイートをサポートしている必要がない:脆弱なライブラリはRSA鍵交換中にServerKeyExchangeメッセージを受け入れてしまい(参考:通常RSAではこのメッセージは不要)、弱い鍵が使われてしまう
p.164 OpenSSLでは上記挙動が機能として実装されていた(SSL_OP_EPHEMERAL_RSAオプション):サーバのRSA鍵がプリマスターシークレットの保護に使われるので、PFSがない
p.164 攻撃者はサーバーからServerKeyExchangeメッセージが送られるようにしないといけないが、障害が2つある

  • ServerKeyExchangeメッセージに対する署名は攻撃対象サーバの強力なRSA鍵で暗号化されている(client_randomを暗号化する必要がある:参考
  • サーバーからの適切なFinishedメッセージを捏造しなければならない

p.165 攻撃者が中間者攻撃で、ClientHelloのランダム値をコピーし、輸出用暗号スイートを要求(=輸出用暗号に対応しているサーバのみ攻撃可能):参考
p.165 攻撃者はServerKeyExchangeをクライアントに再利用する:ClientHelloのランダム値をコピーしているのでクライアントで検証がパスする

p.166 Finishedメッセージの捏造:鍵交換で512ビットにダウングレードできているのでこれを利用して総当たり攻撃、プリマスターシークレットを取得して接続を制御可能
p.166 2017年現在では、512ビット鍵は決して弱すぎるわけではない:リアルタイムで破れる組織もあるが、誰もが心配する必要はないー>2021年には250ドルで破られた事例
p.166 ただし、パフォーマンス改善のために、鍵の使い回しをしているケースもあり、問題が深刻
p.166 今回の例では、研究者がAmazon EC2を利用し100ドル7時間で鍵を突破した

KIDANI AkitoKIDANI Akito

6.4.3 影響と緩和策

p.166 OpenSSLを使っているブラウザ:Android(数十億台)など
p.166 ブラウザ以外にもモバイルアプリにも影響[1]
p.166 他にも、AppleのSSLライブラリSecure Transport(OS X 10.8.x以降、iOS8.2以降で修正)、MicrosoftのSSLライブラリSchannelにも同様の脆弱性が発見された:これらのライブラリはデプロイ形態が多様なので、脆弱性有無の切り分けが困難、SSL Labsなどでのクライアント用のテスト実施が確実
p.166 Firefoxだけは影響なし
p.167 古い不要な機能は削除すべき好例:攻撃対象領域が狭まり、リスクも低減する
p.167 FREAK脆弱性の公開前は、インターネット上の安全なサーバのうち1/4が弱い暗号スイートを提供
p.167 そのうち、2/3が弱い鍵を接続のたびに使いまわしており、脆弱であった

FREAKを発見したmiTLS

p.167 miTLSはMicrosoft ResearchとInria Joint Centre[2]のプロジェクト
p.167 TLSの脆弱な実装を多数発見[3]

脚注
  1. 100万人以上ユーザーのいるAndroidアプリ1200以上が脆弱、iOSアプリの上位14000のうち771件が脆弱。参考 ↩︎

  2. INRIA:Institut National de Recherche en Informatique et en Automatique(フランス国立情報学自動制御研究所)。1979年設立、OCamlなどを開発。Wikipediaより ↩︎

  3. サイトによると、SKIP-TLSという攻撃がある(CVE-2014-6593)。通常、DH鍵交換ではServerKeyExchangeメッセージが必須だが、RSA鍵交換ではこれは不要となる。しかし、いくつかの実装では暗号スイートで必須となるメッセージが誤ってスキップされてしまう。これは、ライブラリが異なる暗号スイートのコードをコピペしているため発生する。JDKにデフォルトで付属するJSSEクライアントは、中間者攻撃に対して全く安全ではなかったことになる。Google、Paypal、AWSなどのJava SDKも影響を受けた。これはJava 8u25、7u72、6u85、5.0u75以前が影響を受ける。参考 ↩︎

KIDANI AkitoKIDANI Akito

6.5 Logjam

p.167 FREAKはRSA鍵交換に対する攻撃:DHE鍵交換への攻撃も時間の問題と考えられた(DHEでも輸出用暗号スイートは512ビットの鍵を利用)
p.167 2015年5月:Logjam攻撃発表
p.167 Log=DH鍵交換の原理「一方向関数の離散対数問題(Discrete Logsrithm Protocol)」に由来

6.5.1 安全ではないDHE鍵交換に対する能動的な攻撃

p.168 最初の手法:FREAKを模倣し、リアルタイムに破れる弱いDHE鍵交換を強制する中間者攻撃
p.168 FREAKとの違い3つ

  • サーバが安全ではないDHパラメータを使ってしまう
  • DHE鍵がサーバでキャッシュされている
  • クライアントが弱いDHパラメータを受け入れてしまう
サーバが安全ではないDHパラメータを使ってしまう

p.168 DHE鍵交換:サーバは一時的な鍵を生成し、認証のために自身の秘密鍵で署名する
p.168 Logjam攻撃:サーバが輸出用暗号スイートを使う場合に弱い一時的鍵を利用させる
p.168 安全でないDHパラメータが利用される場合、受動的攻撃(全記録し後日復号など)もありうる:サーバで通常より高速なECDHEやRSA鍵交換が選択され、DHEがネゴシエーションされる可能性は低い

DHE鍵がサーバでキャッシュされている

p.168 能動的攻撃の成功条件:モダンブラウザがハンドシェイクを諦めるまでのおよそ1分以内に、適切なFinishedメッセージを生成できる
p.168 1分以内に512ビットDHE鍵を破るのは、熟練した攻撃者でも困難
p.168 しかし、鍵がキャッシュされているサーバの場合は、事前に鍵を破ることでリアルタイム攻撃が可能
p.168 15%のサーバで鍵がキャッシュされていたが、512ビットのDHパラメータを利用しており脆弱だったのは0.1%
p.168 MicrosoftのIISは一時的な鍵を2時間キャッシュ

クライアントが弱いDHパラメータを受け入れてしまう

p.168 攻撃公開当初は、全てのメジャーなブラウザで512ビットの鍵交換が許可され、脆弱だった
p.168 輸出用暗号スイートに対応していないクライアントも攻撃可能:DHパラメータの強度指定はサーバが行うため

p.169 Logjamが明らかにしたTLSプロトコル自体(実装ではない)の欠陥:DHパラメータのサーバ署名が再送できてしまう(リプレイ攻撃)
p.169 攻撃者:クライアントの乱数を別のハンドシェイクで再利用し、本来選択されない暗号スイートをネゴシエーションさせる

6.5.2 安全ではないDHE鍵交換に対する事前計算攻撃

p.169 Logjam攻撃公表時、DHパラメータの大半は1024ビット、一部768ビットで十分強い:しかし、政府機関なら事前計算攻撃の余地あり
p.169 DH鍵交換:ドメインパラメータ(素数pと原始根g)はTLS1.2以下ではネットワーク越しに送信され攻撃者も閲覧可能
p.169 DH鍵交換を破るには、鍵交換で生成される一時的な秘密鍵2つのうち1つが必要
p.169 DH鍵交換への攻撃:NFS(Number Field Sieve、数体ふるい法)を2段階にわける。前半が素数pのみに依存する
p.169 512ビットの素数pへの事前計算は1週間、その後の鍵交換は60秒未満で可能

KIDANI AkitoKIDANI Akito

6.5.3 国家機関が弱いDH鍵交換に対する脅威となる場合

p.170 Logjam論文の共著者による推定

事前計算(1コア/年) 個々の攻撃(1コア)
DH-512 10 10分
DH-768 36,500 2日
DH-1024 45,000,000 30日

p.170 ただし、事前計算は並列化と更なる最適化が可能:768ビットDHパラメータは研究者レベルでも攻撃可能
p.170 1024ビットの場合、数億ドルかかるハードウェアでも攻撃完了に1年かかる計算
p.170 実際には、標準グループ[1]のDHパラメータが使いまわされる=同一の素数が多数のサーバで利用されるため、1回の事前計算で何万台ものサーバを攻撃可能
p.170 標準グループを使うべき理由:実装者がセキュリティプロトコルを実装するとミスが起こりやすい
p.170 Logjamの本質的な問題:利用されている標準グループが弱すぎた

6.5.4 影響

p.170 最悪のケース:DHEでネゴシエーションし安全でないDHパラメータを使うサーバ(受動的攻撃に弱い、後日復号される)
p.170 次点:安全でないDHパラメータを使うが通常RSA/ECDHE鍵交換が行われる
p.171 受動的攻撃の場合、1024ビットのDHグループを1つ破ると、HTTPSサーバの6.56%に影響:10グループだと約10%
p.171 能動的攻撃の場合、1グループで12.8%、10グループで23.8%

p.171 モダンブラウザのFalse Start機能:TLS完全性検証より前の段階でHTTPリクエストを送る(パフォーマンス重視)ため、そこから機微情報を得られる場合もある

p.171 TLS以外のプロトコルへの受動的攻撃もある:SSHやIPSecでは1024ビットのDHパラメータが使われることが多い
p.171 筆者の推定では、標準グループの1つを破ると、25.7%(36億台)のSSHトラフィックを、60%以上のIPSecとフラフィックを受動的に復号・傍受可能

NSAは1024ビットのDH鍵交換を破れるのか

p.171 Logjam論文の共著者らの考えでは可能:NSAの年間予算は100億ドルを超える
p.171 事実、NSAはVPNトラフィックを解読できるらしいとスノーデンのリーク文書に記載:復号のアーキテクチャについては言及なし

脚注
  1. RFC3526(2003年)RFC7919(2016年)で定められている ↩︎

KIDANI AkitoKIDANI Akito

6.5.5 緩和策

p.171 輸出用暗号スイートを無効にすれば十分
p.171 DHEを含む暗号スイートについては、2048ビットのDHパラメータを使う
p.172 Java 6のクライアントは1024ビットを超える強度に対応していない問題がある
p.172 1024ビットのパラメータを使う場合、標準グループを使用しない方が良い
p.172 あるいはDHEを無効化すべし:RSAやECDHEよりかなり遅い(パフォーマンスについては第9章参照、RSAはPFSがないので使うべきではない)
p.172 DHEを無効にすると、ごく一部の通信でPFSがうしなわれる程度の影響[1]
p.172 ブラウザベンダもDHパラメータの最低強度を768ビットまたは1024ビットに引き上げた:長期的にはDHのサポートはなくなりECDHEに移行するはず

脚注
  1. ECDHEに対応していないクライアントがRSAを利用するから、ということか? ↩︎

KIDANI AkitoKIDANI Akito

6.6 プロトコルダウングレード攻撃

p.172 TLSハンドシェイク時にパラメータに影響を与え、弱いプロトコルや暗号スイートを強制する能動的な攻撃
p.172 SSL2.0ではハンドシェイクの完全性が保証されず、このような攻撃に弱い
p.172 一方、ブラウザはプロトコルのバージョン間の相互運用のためにセキュリティ面で妥協することも

6.6.1 SSL3.0のプロトコルロールバック対策

p.172 SSL3.0((RFC6101)[https://datatracker.ietf.org/doc/html/rfc6101#section-5.6.9])では、ClientHelloのフォーマットが変更され[1]、Finishedメッセージにおけるハンドシェイクメッセージの完全性検証が導入された。

SSL2.0のCLIENT-HELLO
uint8 V2CipherSpec[3];
struct {
    uint8 msg_type;
    Version version;
    uint16 cipher_spec_length;
    uint16 session_id_length;
    uint16 challenge_length;
    V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length];
    opaque session_id[V2ClientHello.session_id_length];
    Random challenge;
} V2ClientHello;

参考:RFC2246

SSL3.0のClientHello
struct {
    ProtocolVersion client_version;
    Random random; // CHALLENGEから名称・位置変更
    SessionID session_id;
    CipherSuite cipher_suites<2..2^16-1>;
    CompressionMethod compression_methods<1..2^8-1>; // 追加
} ClientHello;

p.173 SSL3.0に対応したサーバは互換性のあるクライアントとの通信時に自動的にSSL3.0にアップグレードすることになっていた:3つの問題点

  1. 多くのサーバでSSL2.0しか解釈されずSSL3.0のフォーマットが使えない
  2. サーバがSSL3.0に対応していてもMITM攻撃の場合はSSL2.0のみのサーバに見せかけられる
  3. SSL2.0を使う場合ハンドシェイク完全性が保証されずMITM攻撃が容易

p.173 上記の問題点をふさぐためのプロトコルロールバック対策:双方がSSL3.0を解釈する場合に、攻撃を検知できる仕組み
p.173 SSL3.0クライアントがSSL2.0にフォールバックする際、RSA鍵交換[2]のPKCS#1ブロック末尾を8バイトの0x03でパディング(SSL2.0ではランダムな8バイト、無視される):SSL3.0を解釈するサーバはこれを検知してハンドシェイクを中断できる
p.173 抜け穴が1つ:SSL2.0では最悪の場合40ビットのRSA鍵しかないため、現在の計算機なら簡単に突破可能

脚注
  1. SSL2.0の仕様を見ると、見慣れたTLS1.2のハンドシェイクとかなり異なるのが分かる。 ↩︎

  2. SSL2.0では認証と鍵交換のための仕組みはRSAのみだったので、ロールバック対策としてRSAのみカバーすれば十分だった。SSL3.0ではDH、DHEやFORTEZZAが追加された ↩︎

KIDANI AkitoKIDANI Akito

6.5.3 相互運用性の問題

■バージョンに対する不寛容

p.174 SSL2.0では進化が想定されておらず、未知のプロトコルバージョンが扱えなかった:Netscapeのリファレンス実装SSLREFでは大きいバージョンの接続を拒否
p.174 SSL3.0でのクライアントのバージョンの扱い: ServerHelloメッセージのserver_versionフィールドにクライアントで提案された値とサーバでサポートされる最大値のいずれか小さい方が入る

SSL3.0のServerHelloメッセージ
struct {
      ProtocolVersion server_version;
      Random random;
      SessionID session_id;
      CipherSuite cipher_suite;
      CompressionMethod compression_method;
} ServerHello;

p.174 TLS1.0から後方互換性に関する記述が増えたが、明確な指針はTLS1.2になってから示された:TLS1.2をサポートしないサーバは、TLS1.2のリクエストに対して古いバージョン番号で応答し、クライアントが同意すれば古いバージョンのプロトコルでネゴシエーション継続
p.174 ただし、仕様が曖昧だったので、多くのサーバではTLS1.2以下の場合接続を拒否した結果、ブラウザでTLS1.2対応が進んだ時に相互運用性の問題が生じた
p.174 このため、最初にTLS1.2に対応したIE[1]では、TLS1.1/1.2を初期設定では無効化してリリースされた
p.174 この問題の解決のための拡張がRenegotiation Indication(TLS1.2のリリースから2年後の2010年):RFC5246(TLS1.2)の要件を改めて規定
p.175 レコードプロトコルのTLSバージョンにも不寛容の問題:ライブラリによってTLS1.0だったり最良バージョン(TLS1.2)だったり
p.175 TLS1.3にとって深刻な問題:レコードプロトコルで新しいバージョンを利用すると、TLS1.3だと10%以上、TLS2.0だと70%のサーバがハンドシェイクを拒否する試算
p.175 TLS1.3ではレコードプロトコルでのバージョンはTLS1.2に固定し[2]、拡張を利用。

■拡張についての不寛容

p.175 SSL3.0/TLS1.0にはプロトコルを改訂せずに新機能を追加する仕組み(拡張)がなかった:ClientHelloの末尾に付加的データを追加できる(ただしSSL3.0/TLS1.0のサーバの大半は付加的データを指定するクライアントの接続を拒否)[3]
p.175 これは、SSL3.0から8年後の2003年にRFC3546でTLS拡張となり、2008年のTLS1.2で統合された

■相互運用性に関する他の問題

●長いハンドシェイクメッセージに対する不寛容
p.176 ClientHelloメッセージには長さの上限がないが、初期のクライアントは対応する暗号スイートが少ないためClientHelloが短かった
p.176 OpenSSL1.0.1でTLS1.1/1.2をサポートするようになり、拡張への対応からClientHelloの長さが著しく増加
p.176 F5社のBIG IPロードバランサでは255バイト以上512バイト未満のハンドシェイクメッセージを処理できず、TLS1.2普及の足枷となった

●任意の拡張に対する不寛容
p.176 SNI拡張及びStatus Request拡張(OCSPステープリング)で、ネゴシエーションに失敗することがある(明確な理由はない)

●フラグメンテーションの適切な処理の失敗
p.176 フラグメンテーション:分割され、レコードプロトコルの複数のメッセージで配送される
p.176 大半の実装ではアプリケーションデータ・サブプロトコルの分割には対応しているが、それ以外のメッセージのフラグメンテーションについては処理に失敗する
p.176 同様に長さゼロのレコードの処理に失敗する実装もある:このため予測可能なIV(初期化ベクター)問題への対策が頓挫

脚注
  1. バージョンはおそらくIE8(Windows 7以降)。 ↩︎

  2. 本文中では1.0となっているが、RFC8446を読むと0x0303なのでTLS1.2が正しい。ClientHelloでは互換性のため0x0301(TLS1.0)をバージョンとして利用してもよいとされている。 ↩︎

  3. SSL3.0のRFC6106を見ると、確かに「5.6.1.2. Client Hello」の節の最後にForward compatibility noteとして構造体として記載された以外のextra dataを追加して良いと記載がある。 ↩︎

KIDANI AkitoKIDANI Akito

6.6.3 プロトコルの自発的なダウングレード

p.176 ブラウザは自発的なダウングレードで相互運用性の問題に対応:徐々にバージョンを引き下げて成功するまで試す
p.176 サーバが不寛容でなくても、プロキシやファイアウォール、アンチウイルスソフトなどにより接続がフィルタリングされることもある
p.177 筆者による2014年6月時点での調査:全ての主要なブラウザでSSL3.0へのダウングレード可能
p.177 IE6ではSSL2.0までダウングレード可能
p.177 TLS1.2より古いバージョンのプロトコルの問題点

  • SSL3.0はPOODLE攻撃で暗号化された小さなデータ(クッキーなど)を抜き出される
  • GCM, SHA256, SHA384の暗号スイートに未対応
  • 楕円曲線暗号に対応していないため、PFSがない
  • TLS1.0以前はBEAST攻撃や脆弱なRC4ストリーム暗号[1]を使える(それぞれ詳細は7章)
  • Microsoft社のSSL3.0スタックではAESに未対応で、RC4と3DES[2]の暗号スイートのみ

p.178 モダンブラウザはPOODLE攻撃の発見を受けてSSL3.0へのダウングレードをやめ、SSL3.0を無効化
p.178 大きな流れとして、フォールバックを完全になくす方向にあるため、ダウングレードは解消されつつある過去の問題

脚注
  1. RC4を禁止するRFC7465は2015年に規定された。 ↩︎

  2. 3DESはDESを強化して1998年に登場したが、2018年に米国立標準技術研究所(NIST)が2023年以降の利用を禁止した。参考CRYPTREC暗号リストでも、運用監視暗号リストに入れられ、互換性維持のためのみ利用を許可されている。 ↩︎

KIDANI AkitoKIDANI Akito

6.6.4 TLS1.0以降のロールバック対策

p.178 SSL3.0以降はマスターシークレットが384ビット(48バイト)となったため、総当たり攻撃不可(SSL2.0では輸出規制のため40ビットの場合があった)
p.178 TLS1.0以降のRSA鍵交換のロールバック保護の仕組み:PreMasterSecret内のバージョン番号をClientHello.client_versionとする

RFC5246(TLS1.2)のClientKeyExchangeメッセージ
      struct {
          select (KeyExchangeAlgorithm) {
              case rsa:
                  EncryptedPreMasterSecret;
              case dhe_dss:
              case dhe_rsa:
              case dh_dss:
              case dh_rsa:
              case dh_anon:
                  ClientDiffieHellmanPublic;
          } exchange_keys;
      } ClientKeyExchange;
RFC5246(TLS1.2)のEncryptedPreMasterSecret
      struct {
          ProtocolVersion client_version;
          // The latest (newest) version supported by the client.  This is
          // used to detect version rollback attacks.
          opaque random[46];
          // 46 securely-generated random bytes. 
      } PreMasterSecret;

      struct {
          public-key-encrypted PreMasterSecret pre_master_secret;
      } EncryptedPreMasterSecret;

p.178 DH鍵交換の場合は以下の通りバージョン番号がカバーされていない

RFC5246(TLS1.2)のClientDiffieHellmanPublic
      enum { implicit, explicit } PublicValueEncoding;

      implicit
         If the client has sent a certificate which contains a suitable
         Diffie-Hellman key (for fixed_dh client authentication), then
         Yc is implicit and does not need to be sent again.  In this
         case, the client key exchange message will be sent, but it MUST
         be empty.

      explicit
         Yc needs to be sent.

      struct {
          select (PublicValueEncoding) {
              case implicit: struct { };
              case explicit: opaque dh_Yc<1..2^16-1>;
          } dh_public;
      } ClientDiffieHellmanPublic;

      dh_Yc
         The client's Diffie-Hellman public value (Yc).

p.178 プロトコルの実装を行う開発者たちがバージョン番号を適切に使えていない問題
p.178 Opera開発者Yngve Pettersenの発言「多くのクライアントとサーバは正しく実装されていない」
p.179 TLS1.2の仕様でも明言「古い実装ではネゴシエーションされたバージョンを代わりに使っている」[1]
p.179 ロールバック防御の仕組みがあるが、結局は自発的ダウングレードを利用して攻撃可能

6.6.5 プロトコルの自発的なダウングレードを悪用した攻撃

p.179 MITM攻撃でデータを変更せずに、SSL3.0以上の接続をブロックすれば十分:別の対策が必要

脚注
  1. RFC5246 7.4.7.1 RSA-Encrypted Premaster Secret Message "Unfortunately, some old implementations use the negotiated version instead, and therefore checking the version number may lead to failure to interoperate with such incorrect client implementations." ↩︎

KIDANI AkitoKIDANI Akito

6.6.6 現代的なロールバック攻撃への防御

p.179 能動的な攻撃者によってダウングレードさせられてしまう

p.179 2011年提案のSCSV(Signaling Cipher Suite Value)[1]

追加されたCipherSuite
        TLS_FALLBACK_SCSV          {0x56, 0x00}

p.179 SCSVでクライアントの最良プロトコルを伝達できる:ClientHello.client_versionで最良でないプロトコルを指定する時に、ClientHello.cipher_suitesに以下を追加することで達成する[2]
p.180 サーバー側がバージョンのずれを検知した場合、以下のアラートで接続を終了させる

追加されたアラート
        enum {
          /* ... */
          inappropriate_fallback(86),
          /* ... */
          (255)
        } AlertDescription;

p.180 SSL3.0では拡張に対応していないため、このような暗号スイートによるシグナリングが利用された
p.180 2012年には同様のシグナリングが提案された

Adam Langley氏の提案した暗号スイート
 TLS_CAPABLE_SCSV (0x00fe)

p.180 サーバーではなくクライアントで検知すべしという議論も:更新が必要なのは一部のユーザーエージェントなので、デプロイが楽という理由
p.180 RFC5764(再ネゴシエーション)をベースにした提案:RFC5764では将来のバージョン番号への不寛容が禁止されている(0.14%がこれに違反していた)、提案者がOperaに実装
p.180 2013年4月から9月にかけての議論がRFC7507として結実:Chrome 33が最初に実装
p.181 SCSV方式の場合サイト(サーバー)の対応が必要なので、普及に時間がかかる

6.6.7 将来の相互運用性の問題を防ぐGREASE

p.181 開発者が仕様を十分に読まず、自分達に都合のいいように実装してしまうことが原因:問題のあるコードを強制的に更新できず、何の対処もできないこともある
p.181 Chromium開発者David BenjaminはTLSプロトコルに対して不正な値をランダムに混入させ続けるクライアントGREASEを実装(Chrome 55から利用):不寛容なサーバは問題を起こす

脚注
  1. 2015年にRFC7507 TLS Fallback Signaling Cipher Suite Value (SCSV) for Preventing Protocol Downgrade Attacksとなったが、2021年3月のRFC8996 Deprecating TLS 1.0 and TLS 1.1で廃止された(TLS1.3では ServerHello.Randomを用いた別の仕組みでカバーしているため) ↩︎

  2. 以下の場合は、cipher_suitesにSCSVを含めてはいけない:セッション再開時に最良でないプロトコルを選択する場合、または、クライアントが最良のバージョンをClientHello.client_version で指定する場合。 ↩︎

KIDANI AkitoKIDANI Akito

6.7 強制切断攻撃

p.181 攻撃者によるメッセージ配送妨害
p.181 SSL2.0は強制切断攻撃に対して脆弱:SSL3.0でclose_notifyが追加され解消
p.181 RFC5246(TLS1.2):双方がclose_notify警告を送り、これを受け取ったら接続を閉じ書きこみ待ちの情報を破棄すべし
p.182 close_notifyはalertサブプロトコルなので暗号化されており完全性検証が可能
p.182 ただし、接続終了の手続きに違反している例は多い:Internet Explorerなど[1]
p.182 上記の違反により、正しく実装されたアプリで強制切断攻撃と誤検知されてしまっている
p.182 過去の経緯から、強制切断攻撃に対する防御は事実上ない

  • SSL3.0:最初に接続を終了する側は相手のclose_notifyを待つ必要なし
  • TLS1.1:接続が適切に終了されなかった場合に、セッションを破棄しなくなった

6.7.1 強制切断攻撃の歴史

p.182 2007年、BerbecaruとLioy:ブラウザはページの一部を表示したあとの強制切断時に、ページが不完全であることを明示せず
p.182 2013年、SmythとPironti:電子投票システムやWebメール(GoogleやMicrosoft)への攻撃(利用者に気づかれず、ログアウトに失敗させ、利用者がログインしたままにできてしまう)
p.183 HTTPのメッセージに送信データ長が含まれているにもかかわらず、Webをとにかく動くものにしているため、TLS層でこのような攻撃が可能

6.7.2 クッキーカッター攻撃

p.183 2014年に効果的な強制切断攻撃手法が判明
p.183 HTTPリクエスト/レスポンスに任意の長さのデータを挿入し、TLSレコード長を制御(BEAST攻撃の応用、詳細は7.2節)、レコードの分割点を変えHTTPリクエストヘッダやレスポンスヘッダの一部を削り取る
p.183 レスポンスヘッダ

[サーバからのレスポンス]
Set-Cookie: JSESSIONID=XXXXXX; Path=/; Secure; HttpOnly(改行コード、CRLF)

[強制切断攻撃後のレスポンス]
Set-Cookie: JSESSIONID=XXXXXX; Path=/;

p.183 改行コードがなくなっている不完全な状態でも、ブラウザが無視して処理を継続してしまう場合も(Android Chrome 27Safari Mobile 7.0.2など:2013年ごろのバージョン)
p.184 攻撃は手が混んでいるが、自動化すれば現実的

  • 攻撃対象のWebサイトとセッションを確立していない(=古いクッキーがない)利用者を攻撃する
  • HTTPレスポンスに任意のデータを挿入できるエントリーポイントを探す(できればリダイレクトされるログインAPIなど、画面表示のないもの)
  • レスポンスヘッダを2つのTLSレコードに分割するパディングを送信する(16384バイトを超えるようにする)
  • 1つめのTLSレコードの後でHTTPS通信を閉じる(TCPのRSTシグナルを送る)
  • Secure属性のないクッキーを抜き出す(クッキー窃取攻撃)

p.185 HSTSのレスポンスヘッダも攻撃対象:max-ageパラメータを切り詰めて最大9秒にしたり、includeSubDomainsパラメータを無効化し、HTTPSストリッピング攻撃などが可能
p.185 クッキーカッターはブラウザの対応で対処可能:修正しているベンダもいるが、大半のブラウザの実情ははっきりしない

脚注
  1. あるサイトでは、「MS-IE/OEのTLS/TCP処理では、「close_notify アラート」を送信せずに、TCPクローズを行う。しかも、そのTCPクローズでも、FIN/ACK送信ではなく、RST(リセット)送信でクローズすることもある。」と書かれていた。参考 ↩︎

KIDANI AkitoKIDANI Akito

6.8 デプロイにおける弱点

p.185 サーバでの実装に明確な指針がないため、一般的な慣習が悪用可能な弱点につながってしまう

6.8.1 仮想ホスト混同(Virtual Host Confusion)

p.185 仮想ホストで証明書を共有=秘密鍵も共有:セキュリティは一番弱いサイトと同じレベル
p.185 サイトが別のポートやIPアドレスにあっても、攻撃される可能性
p.185 弱いサイトをジャックした攻撃者は、他のサイトへの接続を弱いサイトに向けることで能動的攻撃が可能
p.185 攻撃者は弱いサイトのWebサーバでHTTPのHostヘッダを無視できる
p.186 この問題は2010年にRobert Hansenが最初に指摘[1]:実現の可能性はかなり低いとされている
p.186 2014年にDelignat-LavaudとBhargavanが論文にまとめた:現実的な悪用方法や、有名サイトになりすます方法を発見[2]
p.186 HTTP以外のプロトコル、例えばSMTPにも適用可能[3]

6.8.2 TLSセッションキャッシュの共有

p.186 Delignat-LavaudとBhargavanが見つけたもう一つの問題:関係ない2つのサーバ/サイト間でのセッションキャッシュ共有を悪用した、証明書認証のバイパス
p.186 本来のサーバ以外ともセッションを再開できる(正しい証明書を持っていない場合でも)
p.186 どれか1つのサイトを乗っ取ると他のサイトも攻撃できる(仮想ホスト混同と同じようにリダイレクトを使う)
p.186 サーバーアプリケーションがセッションとホストのチェックをしていないのが問題
p.186 ホストごとにチケット鍵を変えることでセッションチケットは対策可能

脚注
  1. ワイルドカード証明書を使っている2つのサイトのDNSを乗っ取り、XSS脆弱性のないサイトへのリクエストを脆弱性のあるサイトに送信させ ↩︎

  2. SPDYでTLSセッションが使いまわされる点が影響しているらしい。論文中ではFacebook、LinkedIn、Dropboxが取り上げられていた。 ↩︎

  3. ワイルドカード証明書を利用しているサイトへの攻撃、という意味では2021年に発見されたALPACA攻撃と類似するが、DNSを乗っ取り自前のサーバへ誘導する点でこちらの攻撃の方が手間がかかりそう。 ↩︎

KIDANI AkitoKIDANI Akito

第7章 プロトコルに対する攻撃

p.187 1994年、SSL1は安全でないとみなされ、Netscapeはバージョン2をリリース:Eコマースの台頭の火付け役
p.187 1996年、セキュリティ上の問題を解決すべくバージョン3リリース
p.187 1999年、ほぼ変わらないTLS1.0リリース:2008年のTLS1.2リリースも1.0が多く使われ続ける
p.187 TLS1.3:これまでの問題を全て回避できるよう設計
p.187 本章で取り上げる問題:2009年の安全でない再ネゴシエーション、2011年のBEAST、2012年のCRIME、2013年のLucky 13、RC4、TIME、BREACH、2014年のトリプルハンドシェイク問題とPOODLE、2016年のSLOTH、DROWN、Sweet32

KIDANI AkitoKIDANI Akito

7.1 安全でない再ネゴシエーション(TLS Authentication Gap)

p.187 2009年8月にMarsh Rayが発見、修正と公表に向け調整開始:11月、Martin Rexが発見し未調整のまま公表[1]

7.1.1 なぜ再ネゴシエーションが安全でなかったのか

p.188 同じTCPコネクション上で新旧TLSストリームに継続性がない:再ネゴシエーションの度に別のクライアントがサーバとやり取りしても検証されない
p.188 アプリは通常暗号化層とやり取りしない:HTTPリクエストで再ネゴシエーションが起きても気がつかない
p.188 再ネゴシエーション前のデータが渡されたり、前後でクライアント証明書や接続パラメータが変わったりすることも:悪用してMITM攻撃が可能
p.188 悪用の3つのステップ

  1. 犠牲者からサーバへのTCPコネクションに介入してリクエストを一時停止
  2. 攻撃者がサーバと新しいTLSセッション開始+攻撃データをサーバに送信
  3. 攻撃者は透過的プロキシとして1のリクエストを再開:サーバは再ネゴシエーションと解釈し攻撃者のリクエストしたレスポンスを返す

p.188 本来TLSで守られるべきアプリケーションデータの完全性が破られている(リクエストが改竄されている)

7.1.2 再ネゴシエーションを引き起こす方法

p.189 この脆弱性が発見されるまで、多くのサーバはクライアントによる再ネゴシエーション開始を許容
p.189 例外的にMicrosoftのIISはバージョン6[2]以降再ネゴシエーションを許容せず
p.189 クライアントが開始できなくても、クライアント証明書またはSGC[3]に対応しているサイトは攻撃できた

脚注
  1. 公開に至る経緯は2021年12月のLog4Shellと似てる気がする。 ↩︎

  2. Wikipediaによると「Windows Server 2003 と Windows XP Professional x64 Edition に付属するバージョン」とのこと。 ↩︎

  3. 米国企業向け証明書に特別な識別情報を埋め込み、クライアントに再ネゴシエーションさせ強い暗号を選択する方式。2000年の規制緩和以降衰退。 ↩︎

KIDANI AkitoKIDANI Akito

7.1.3 HTTPに対する攻撃

p.189 当初議論されていた攻撃は1種類だったが他の可能性がわかった
p.190 Thierry Zollerが攻撃ベクターの追跡・文書化、概念実証のための攻撃設計に尽力

■任意のGETリクエストの実行

p.190 攻撃ペイロード+犠牲者のクレデンシャルを含むリクエスト(X-Ignore:までが攻撃ペイロード)

GET /path/to/resource.jsp HTTP/1.0
X-Ignore: GET /index.jsp HTTP/1.0
Cookie: JSESSIONID=....

p.190 攻撃者はこのように部分的なヘッダ行を含む攻撃ペイロードを用意:末尾に改行がないので犠牲者が発効するリクエストヘッダ先頭行を無効化
p.190 攻撃者はリクエスト対象を選べるが、HTTPレスポンスは犠牲者のところに返る
p.190 これだけだとCSRF(XSRF)と類似しており、安全でない再ネゴシエーションの重要性を見過ごす結果に

■クレデンシャル泥棒

p.190 問題の公開から数日で改良版の攻撃が登場:さらに2、3日で暗号化されたデータ取得が可能に
p.191 Anil Kurmusの概念実証ではTwitterが攻撃対象となった:status=までが攻撃ペイロード

POST /status/update.xml HTTP/1.0
Authorization: Basic [攻撃者自身のクレデンシャル]
Content-Type: application/x-www-form-urlencoded
Content-Length: [推測される長さ]

status=POST /status/update.xml HTTP/1.1
Authorization: Basic [犠牲者のクレデンシャル]

p.191 これにより自身のツイートとして犠牲者のクレデンシャルを入手可能
p.191 Content-Lengthは機微情報を含むのに十分な長さにすればよい:残りのデータは他のHTTPリクエストとして処理される(おそらく不正なフォーマットのリクエストとして無視される)

■ユーザのリダイレクト

p.191 リダイレクトに応答するリソースがある場合、以下の攻撃が成立する可能性がある

  • 悪意あるWebサイトへと送る:オープンリダイレクトをフィッシングに利用(元のデザインを模倣+ドメイン名を似せる)
  • 平文HTTPへと接続をダウングレードする
  • リダイレクトされたPOSTによるクレデンシャルの奪取:307ステータスは同じリクエストメソッドを利用=POSTのボディもリダイレクトされるため、標的サイトのアカウントがなくても攻撃可能(銀行などが攻撃対象になり得る)
■XSS

p.192 Windowsのマイナなブラウザ[1]ではTRACEメソッドに対するmessage/httpタイプのレスポンスをHTMLとして扱うものがある
p.192 TRACEメソッドはリクエストをレスポンスに含めるため、攻撃ペイロードを利用してXSSが可能

脚注
  1. Thierry Zollerによれば、TRIDENTエンジンを利用しているサードパーティのブラウザ、とのこと。昔使ってたLunascapeはTRIDENT/Gecko/WebKitを切り替えることができたのでもしかしたら影響があったかも? ↩︎

KIDANI AkitoKIDANI Akito

7.1.4 他のプロトコルに対する攻撃

p.193 再ネゴシエーションの前後で状態をリセットしないプロトコルはHTTP以外も脆弱

●SMTP

p.193 SMTP:安全でない再ネゴシエーションに対して脆弱、ただしトランザクションが複数のコマンド・レスポンスで構成される[1]ため悪用は困難
p.193 メールサーバPostfix:再ネゴシエーションに対する脆弱性なし
p.193 SMTPの場合多くのサーバが有効な証明書を使っておらず、クライアントもこれを検証していないため、再ネゴシエーション以前にMITMが用意であったという事情がある

●FTP

p.193 FTPサーバによっては、ファイル転送方法に問題があり、暗号化が無効化される可能性あり

7.1.5 アーキテクチャに起因する攻撃

p.193 SSLオフロード[2]:暗号化対応の方法がないサービスを暗号化したり、中核サービスからTLS処理を切り離して[3]パフォーマンス改善を図る手法
p.193 実際のWebサーバに問題がなくとも、オフロード処理を行うサーバに問題があればシステム全体が脆弱となる[4]

7.1.6 影響

p.194 CSRFからクレデンシャル盗難までさまざまな攻撃が可能
p.194 エンドユーザのブラウザを直接攻撃するよりも、サーバを狙う方が遥かに容易
p.194 完全性を損なう(データが改竄される)ことの副作用:犠牲者がサーバを攻撃しているように見せかけるという攻撃ベクターが生まれる
p.194 エンドユーザ側での対応(ブラウザの設定変更など)も必要[5]

脚注
  1. Wikipediaによると、6往復程度必要。 ↩︎

  2. SSLアクセラレーションとも。 ↩︎

  3. TLS処理を行うハードウェアをSSLアクセラレータと呼ぶ。ClientHelloのサイズ問題でTLS1.2普及の妨げとなったとされるF5社のBIG IPなどがそう。 ↩︎

  4. 実際に、SSLオフロードをサポートするFortiOSには問題があったらしい。 ↩︎

  5. 手元のFirefox 92.0.1で確認したところ、security.ssl.require_safe_negotiationはfalseのままだった。SSL Pulseによると、まだ一部の安全でない再ネゴシエーションを許可するサイトが存在するので自衛のためにも設定した方がよさそう。 ↩︎

KIDANI AkitoKIDANI Akito

7.1.7 緩和策

●安全な再ネゴシエーションにアップグレードする

p.194 2010年初頭にRenegotiation Indication拡張が公開された:RFC5746[1]
p.194 ネットワークセキュリティ業界では再ネゴシエーションに価値なしと判断
p.194 TLS1.3では再ネゴシエーションが廃止されている

●再ネゴシエーションを無効にする

p.194 Renegotiation Indication拡張登場までの半年ほどは無効にするのが唯一の対策だった
p.195 クライアント証明書認証を利用する場合は無効にできない

7.1.8 問題の発見と修正を時間軸に見る

p.195 TLSのような複雑なエコシステムでは問題の修正に徹底的な協力と時間が必要
p.195 プロトコルの修正には6ヶ月かかった
p.195 ライブラリとOSにパッチを当てるのにさらに12ヶ月:大勢がパッチを適用するのにさらに24ヶ月
p.195 Opera社の調査:サーバの50%でRFC登場から1年以内に安全な再ネゴシエーションのパッチ適用、さらに3年かけて83.3%
p.196 プロトコルの欠陥解消には約4年かかる
p.196 SSL Pulse2014年6月の調査では88.4%が安全な再ネゴシエーションに対応、6.1%が安全でない、6.8%が再ネゴシエーションに非対応[2]
p.196

脚注
  1. 詳細は本書2.12.6参照。 ↩︎

  2. 2021年12月の調査では99.3%が安全な再ネゴシエーションに対応、0.2%が安全でない。非対応は0.7% https://www.ssllabs.com/ssl-pulse/ ↩︎

KIDANI AkitoKIDANI Akito

7.2 BEAST

p.196 2011年夏、TLS1.0以前で暗号化されたデータの一部を抜き出す攻撃手法BEASTが発表
p.196 TLS1.0の予測可能な初期化ベクター(IV)が利用された:TLS1.1では修正ずみだが、発見当時TLS1.1対応ブラウザが事実上存在しなかった[1]
p.196 IVの問題は10年間知られていたおり悪用が現実的でないと考えられていたが、DuongとRizzoが実用案を公開
p.196 BEASTによってブラウザベンダーがTLSプロトコルにほとんど注意を払っていないことが明らかに

7.2.1 攻撃の方法

p.196 攻撃対象はTLS1.0以前のCBC(Cipher Block Chaining)モードの暗号:予測可能なIVの場合、安全でないECB(Electronic Code Book)モードと同じになる

■ECBオラクル[2]

p.197 ブロック暗号ゆえ、決定論的性質を回避できない:同じデータが同じ形に暗号化されてしまう
p.197 攻撃者が好きなデータで暗号化を試し、過去のデータを推測可能

  1. ブロックを観察する:アルゴリズムによるが、AES-128なら16バイト(=128ビット)
  2. 16バイトの平文を暗号化し、観察対象と比較(これを繰り返す)

p.197 平均して2^127回推測が必要

■予測可能なIVのCBC

p.197 CBCでは暗号化前のメッセージをマスクするためにIVを使い、平文を隠すことでECBの脆弱性を回避
p.197 効果的なIVはメッセージに対して予測不可能である必要がある
p.197 SSL3.0/TLS1.0のCBCでは最初のブロックにのみ予測不可能なデータを使う
p.197 TLS1.0以前ではTLSレコードが、攻撃者が観察可能な前の暗号化ブロックをIVとして利用するため問題
p.198 TLS1.1/1.2では、レコードごとにIVが変わるので問題なし
p.198 TLS1.0はブロック単位での選択平文攻撃に脆弱(blockwise chosen-plaintext attack):CBCが事実上ECBに引き下げられる

脚注
  1. Windows 7(2009年リリース)のIE7ではTLS1.1がデフォルトでは無効になっていた。参考:Wikipedia ↩︎

  2. ここでいうOracleは神託、転じてブラックボックスな仕組みのこと。つまり暗号アルゴリズム。 ↩︎

KIDANI AkitoKIDANI Akito

p.199 C2 = E(M2 (+) IV2):(+)は排他的論理和によるマスク、M2に秘密情報が含まれる
p.199 攻撃者が作るメッセージ3は次のように書ける:M3 = Mg (+) C1 (+) C2
p.199 上記より、C3 = E(M3 (+) C2) = E(Mg (+) C1 (+) C2 (+) C2)となる
p.199 排他的論理和を2回当てると消えるので:C3 = E(M3 (+) C2) = E(Mg (+) C1)となる
p.199 MgがM2と同じであれば、暗号化した結果C3がC2と一致

KIDANI AkitoKIDANI Akito
■実際の攻撃

p.199 ブロック全体(典型的には16バイト)を推測可能にするHTTPの特性

  • 機微データのサイズが小さい(パスワード、セッションID)
  • 機微データの文字種類が限定されている(セッションIDは16進数でエンコードされがち)
  • クッキーの前には必ず「Cookie: 」の文字列がある

p.199 DuongとRizzoの発見:リクエスト中の機微データの位置を変更しやすい+暗号化・送信タイミングの制御
p.199 データ位置変更はURLに文字を追加することで達成可能
p.199 送信タイミングの制御にはJavaScriptではなくJava appletが利用できた(別のSameOrigin制約を回避するための脆弱性を悪用)
p.200 HTTPリクエストを操作して単一ブロックに15バイトの既知情報と1バイトの機微情報が含まれるようにする:平均2^7回で推測可能、16進数の数字であれば平均8回で推測可能

7.2.2 クライアントサイドの緩和策

p.200 問題が最初に発見され(脅威と見做されていなかっ)た2004年のOpenSSLの対策:TLSレコードの前に空のTLSレコードを挿入
p.200 予測可能なIVを空のレコードで消費する作戦
p.201 しかし、IEが長さ0のTLSレコードをうまく扱えないと判明し、断念
p.201 2011年、ブラウザベンダは1/n-1分割によりBEAST対策を実施
p.201 アプリケーションデータの1バイトを最初のTLSレコードに、残りを2つ目のTLSレコードに配置する
p.201 1バイトのみが予測可能なIVの犠牲になる
p.201 Chromeが最初に(2011年)1/n-1分割を適用したが、多くの大手サイトと通信できず一度は断念した
p.202 Chrome/Operaが2011年末、FF/IEが2012年初め、Safariが2013年に1/n-1分割に対応
p.202 任意の平文を挿入できなければ攻撃が困難なため、多くのクライアントサイトのツールでは1/n-1分割に非対応

7.2.3 サーバサイドの緩和策

p.202 膨大な数のクライアントの更新サイクルをコントロールするのは不可能:多くのユーザが脆弱なブラウザを利用
p.202 Chromeの自動アップデートに他のブラウザが追随し事態はある程度改善された
p.202 2013年までサーバサイドでのBEAST対策としてはストリーム暗号のRC4の利用:2013年にはRC4の脆弱性が知られ、サーバサイドのBEAST対策がなくなる[1]

7.2.4 歴史

p.203 予測可能なIVの問題点は1995年から知られていた
p.203 しかし、広く認識されておらずSSL3.0(1996年)で導入されてしまった
p.203 2002年にSSHプロトコルで再発見され、前述の空レコードによる対策が提案されたが互換性がなく無効化された
p.203 2004年にはTLSで予測可能なIVを悪用する方法が提示された
p.204 TLS1.1(2006年)では各TLSレコードに無作為なIVが使われることで問題を解消:しかし、これに対応するブラウザが少なかった
p.204 2011年BEASTが問題となり、1/n-1分割で対策された
p.204 主要なブラウザがTLS1.2をデフォルトにしたのは2013年後半になってからのこと

7.2.5 影響

p.204 プロトコル起因の問題のため、脆弱性発見当時は全てのクライアントが脆弱だった
p.204 攻撃者はサーバが送るデータを制御できる訳ではないので、サーバからのデータストリームに対して攻撃することは不可能
p.205 攻撃に必要なサーバの条件:CBCを含む暗号スイートが優先されること、TLS圧縮が無効であること
p.205 圧縮されるとBEAST攻撃はより困難になる(圧縮自体に別の問題があり対応するサーバは半数程度と推測)
p.205 BEAST発見時の攻撃可能条件:犠牲者の近くからのMITM攻撃+犠牲者がJavaプラグインを利用+(犠牲者が平文サイトを閲覧or攻撃者のサイトを閲覧)+対象サーバがCBC利用・圧縮非利用
p.205 現在では状況が変化:Java自体に対策が実施済み(Oracle, 2011年10月)、かつJava appletが無効化(Chromeでは2015年)、TLS1.2がデフォルトになりつつある

脚注
  1. ストリーム暗号として2016年にはChacha20がTLSで利用可能となった。RFC7905↩︎

KIDANI AkitoKIDANI Akito

7.3 圧縮サイドチャネル攻撃

p.206 圧縮結果のメッセージ長を観測するサイドチャネル攻撃
p.206 攻撃者が自分のデータを送信して、秘密情報と一緒に圧縮できる場合に効果的
p.206 この推測材料を圧縮オラクルと呼ぶ

7.3.1 圧縮オラクルの仕組み

p.206 攻撃者は、送りつけるデータを変化させ、一緒に圧縮される秘密データについての情報を得る
p.206 一般的な圧縮アルゴリズム[1]:繰り返される文字列を除去する(例:LZ77[2]
p.207 オラクルが存在する=秘密情報と一緒に任意のデータを圧縮できる
p.207 圧縮が効けば、出力の長さが短くなり、推測が正しいことがわかる

[コラム]情報が漏れるのはTLSプロトコルの欠陥なのか

p.207 文書化されているプロトコルの限界:TLS1.2の仕様でも、レコードの長さが保護されない点に注意すべしと書かれている
p.207 ブラウザベースの攻撃:攻撃者のリクエストが本物のユーザのセッションで送信される点を利用
p.208 送信元の環境を分離しようとするとWebの大半は機能しなくなる:選択的な分離(セキュリティ空間をサイトが宣言する形(CORSとか?))
p.208 メッセージ長の隠蔽は効果に疑問が残る

7.3.2 攻撃の歴史

p.208 2002年にJohn Kelseyが論文で初めて言及:ただし機微情報への攻撃は困難
p.208 2007年、(TLSではないが)暗号化されたIP電話の会話を特定するアルゴリズムが開発[3]:最終的には90%の正確さで英文を識別可能[4]
p.208 2011年のSPDYメーリスでの議論:imgタグのsrc属性で標的サイト を指定することで、攻撃可能(URLがクッキー文字列にマッチして、圧縮結果が変わるまで試す)

脚注
  1. 本文中では「不可逆圧縮アルゴリズム」と書かれているが内容的に「可逆〜」の誤りか。 ↩︎

  2. 1977年に、Abraham LempelとJacob Zivによって開発されたアルゴリズム。改良版のLZSSを改良したDeflateがgzipなどに利用されている。(Wikipediaより) ↩︎

  3. 論文によると、帯域幅を節約するVBR(Variable Bit Rate)によってエンコードされた通話では十分に保護されない。たとえば、英語かスペイン語か?という問題は86%程度の正解率になる(そもそも通話の両端がどこか、ということを観測すれば言語はある程度推定できる)。これを可能にしているのは、たとえばSpeexコーデックでは母音や強い音は、sやfなどの摩擦音よりも高いビットレートが必要になり、簡単な音は少ないビットで、エンコードしにくい音は多くのビットでエンコードされる。VBRエンコーダは最適なビットレートを選択するので、暗号化されたパケットサイズからビットレートを予測することができる。NISTはVoIPに追加のセキュリティとしてAESなどによる暗号化を推奨している。 ↩︎

  4. 論文によると、フレーズによって正確さが変わるらしい。 ↩︎

KIDANI AkitoKIDANI Akito

7.3.3 CRIME

p.209 2012年、BEASTの発見者でもあるDuongとRizzoがCRIMEを発見・命名
p.209 クッキーの1文字を6回のリクエストで暴けたとされている
p.209 仕組みはBEASTと同じ(むしろ簡単):複数のリクエストを犠牲者のブラウザから送信させ、ネットワーク上でパケットを観察(=推測)

■TIME

p.209 2013年3月改良されたTIMEが発表される:ローカルネットワークへのアクセスが不要に
p.209 imgタグとonLoad/onReadyStateChangeイベントハンドラを利用(JavaScriptマルウェア)
p.210 TCPの輻輳ウィンドウを利用して圧縮された出力における差分を観察

  • 輻輳ウィンドウに収まるデータは一度に送信される(初期値はクライアントにより異なる:初期値5Kバイトなら3パケットまとめて送信可)
  • 残りのデータは最初に送ったパケットのACKが帰ってから次のパケットとして送信される=RTTが1回多くなる
  • リクエストの送信データを大きくすることで輻輳ウィンドウ初期値を特定可能
  • RTTが1回多くなることをJavaScriptマルウェアで計測する
  • 1バイト圧縮できれば、RTTが少なくなり、秘密情報の推測が正しいことがわかる

p.211 HTTPリクエストは上記のように比較的簡単に攻撃できる:HTTPレスポンスは難しい

  • レスポンスの圧縮はサーバ側で行われるので、サーバ側の輻輳ウィンドウ初期値の特定が困難
  • オラクルの存在が必要:レスポンスに含まれる秘密情報の横に、任意のデータを挿入できるか
  • クライアントとサーバ双方の輻輳ウィンドウが関係するため遅延の原因を知るのが困難

p.211 とはいえTLSと違いHTTPレベルのレスポンス圧縮は普通に利用されており同様の攻撃は有効
p.211 TIMEは概念実証の域を出ていない:乗り越えるべき障壁が多い

  • 輻輳ウィンドウの境界はコネクションの時間がたつにつれ大きくなる
  • サーバはコネクションを再利用する上、ブラウザ(JavaScript)側からは制御できない
  • よって、コネクションが閉じられるのを待って何度も輻輳ウィンドウの値の確認を試す必要がある
BREACH

p.211 2013年8月、HTTPレスポンスを標的とする別のサイドチャネル攻撃BREACHが発表
p.212 攻撃例として、Outlook Web AccessのCSRF対策トークンを30秒未満で95%精度で取得

■攻撃の詳細

p.212 前提条件:犠牲者のネットワークトラフィックへのアクセス+犠牲者のブラウザでのJS実行
p.212 リクエストに攻撃ペイロード(秘密情報と同じ文字列になるよう推測された任意のデータ)を含め、それがレスポンスに含まれれば攻撃可能
p.212 いくつか追加で対処が必要:Huffmanエンコーディング、ブロック暗号、コンテンツの多様性

●Huffmanエンコーディング

p.212 HTTPの圧縮に使われるDEFLATE=LZ77(攻撃対象)+Huffmanエンコーディング
p.213 Huffmanエンコーディング:文字種類によって登場頻度が異なることを利用した可変長エンコード方式
p.213 通常1文字1バイトだが、頻繁に登場する文字は短い記号(1バイト未満)を使うようにする(参考:Wikipedia
p.213 Huffmanエンコーディングにより推測の成否に関係なく得られる長さが変わる[1]

●ブロック暗号化方式

p.213 BREACHはストリーム暗号を想定(データの長さが暗号文にそのまま反映される)
p.213 ブロック暗号が使われるとパディングが必要になるためリクエストが複数回必要になる

●レスポンスのコンテンツの多様性

p.213 HTTPレスポンスへの攻撃はマークアップやエンコーディングの多様性から攻撃が難しい
p.213 攻撃がない場合でもレスポンスの大きさが様々に異なり推測が困難

脚注
  1. 対策として1回の推測で2回のリクエストを投げれば良いらしいが、理屈がよくわからない...orz ↩︎

KIDANI AkitoKIDANI Akito
CRIMEとTLSレコードサイズ

p.213 TLSレコードは16Kバイト(16384バイト)=圧縮後の最大サイズ
p.213 攻撃者は無作為な文字列をURLにすることで、先頭16Kバイトを完全に制御可能
p.214 現実には圧縮の設定やライブラリのバージョンを考慮する必要があるが、比較的簡単な手法

■さらなる攻撃の進化

p.214 2016年BREACHを容易にするRuptureフレームワークが公開
p.214 2016年HEIST攻撃が発表[1]

■TLSの圧縮とSPDYに対する影響

p.214 TLS/SPDYともにCRIMEの攻撃対象はヘッダの圧縮=クッキー用
p.214 CRIMEには能動的なMITM攻撃(ネットワークトラフィックへのアクセス)が必須
p.214 クライアント側での制御も必須:imgタグを使うと簡単(ソーシャルエンジニアリング、平文サイトHTML改竄)
p.214 圧縮は至るところで行われている:詳細が出たのがTLSとSPDYだけ
p.215 CRIME発表当時SSL Pulseの結果で、TLS圧縮対応は42%、SPDY対応は2%(Google, Twitter等大手サイト含む)
p.215 ブラウザとしてはChromeだけが圧縮に対応していた:FFは実装のみでリリースなし
p.215 SSL Pulseの閲覧者基準で、圧縮対応クライアントは7%
p.215 多くのベンダーがTLS圧縮を無効にするパッチを当てた
p.215 影響範囲:Basic認証のパスワードやセッション用クッキーが取得可能

■HTTPレスポンスの圧縮に対する影響

p.215 HTTP圧縮は影響が全く別

  1. 攻撃対象領域がかなり広い
  2. 攻撃者の事前準備が多く、得られる成果も少ない

p.216 HTTP圧縮の攻撃対象領域:多くのサイトが有効にしており、無効にした場合のコスト面が大変
p.216 標的Webサイトの内情を知らないといけない(秘密情報がどこにあるのか)
p.216 結果的に、CSRF対策トークンが狙われやすい[2]

脚注
  1. 資料によると、JavaScriptのperformance.getEntries()メソッドを利用して通信のタイミングを観測できるらしい。 ↩︎

  2. リクエストを見たら大体わかるから、か? ↩︎

KIDANI AkitoKIDANI Akito

7.3.4 TLSおよびSPDYへの攻撃に対する緩和策

p.216 攻撃発表前は30%程度のユーザ(≒Chromeユーザ)が圧縮に対応していた:自動アップデートで圧縮は無効に
p.216 OpenSSLも圧縮に対応していたがブラウザではなくマルウェアが挿入されるとは考えづらい
p.216 2014年7月時点のSSL Pulseによると、10%程度のサーバが圧縮に対応(古いApacheか)
p.216 長さを隠すTLS拡張の実装が検討された:RFC Draft(正式なRFCにはなっていない模様)

※TLS1.3ではTLSレコードにパディングが追加されレコード長を隠すことが可能

struct {
  opaque content[TLSPlaintext.length];
  ContentType type;
  uint8 zeros[length_of_padding];
} TLSInnerPlaintext;

p.216 SPDYの圧縮についてもChrome/Firefoxで無効化済み

7.3.5 HTTPの圧縮への攻撃に対する緩和策

p.217 HTTPの圧縮への攻撃は簡単ではないし、解決が難しい

  1. HTTPの圧縮を無効にできる余地がない
  2. アプリケーションの変更が必要だがコストに見合わない

p.217 可能性がある対策は以下5つ

●リクエストのレートを制御する

p.217 TIME/BREACHの発見者は大量リクエストが必要で何回か逮捕された
p.217 ユーザーのセッションにおけるリクエストレート制限は有効:コスト的にもOK

●長さを隠す

p.217 レスポンスの本当の長さを隠す(HTMLなら空白は無視されるのでパディングを挿入しても問題ない)
p.217 BREACHの発見者によるとリクエスト回数を増やすと無作為なパディングも統計解析で無効化可能
p.217 Webサーバレベルで適用可能な点で優れている

●CSRF対策用トークンのマスキング

p.217 CSRFトークンの各バイトに対応するランダムバイトを作り、排他的論理和をとった結果とランダムバイトをトークンとして埋め込む[1](毎回レスポンスに含まれるトークンが変わるので、圧縮を何度も試すことができない)

●部分的に圧縮を無効にする

p.218 標的サイトと異なるドメインで実行されるJavaScriptによって攻撃が行われる=リファラ情報が標的サイトのドメインではない
p.218 よって、このようなリクエストへのレスポンスの圧縮を無効にすれば対策可能(パフォーマンスは若干低下

●文脈を隠す

p.218 機密性の高いデータと攻撃者のデータを同じ文脈で圧縮しない(実現は難しい, 特に後付けは。)
p.218 Black Hat Europe 2016でデータをマスキングするツールCTXが発表された[2]

脚注
  1. この対策はBREACHのサイトで緩和策として挙げられている、と著者がtwitterで述べていた:https://twitter.com/ivanristic/status/1248184646833246210 ↩︎

  2. 資料p.34によると、圧縮無効化するとレスポンスサイズが11倍で数秒の遅れが生じるが、機密データをマスキングすると20%程度サイズが増加、CTXを使うと5%程度のサイズ増加で済む。 ↩︎

KIDANI AkitoKIDANI Akito

7.4 Lucky 13

p.218 CBCを含む暗号スイートに対し、平文の小さな一部(要するにクッキーやBasic認証)を解読できる攻撃が2013年2月に発表
p.218 HTTP以外でもパスワードによる認証を使っている場合影響がある可能性あり
p.218 根本原因は、CBCのパディング(というかCBCの暗号文が?)がTLSの完全性検証の対象外である点:パディングを攻撃者がいじれる
p.218 攻撃者はJavaScriptマルウェアで平文から1バイトを見つけ出すのに8192回のリクエストが必要

7.4.1 パディングオラクルとは

参考:https://rintaro.hateblo.jp/entry/2017/12/31/174327
m3=Dec(c3)⊕c2
平文(n番目) = 暗号文(n番目)を復号し、暗号文(n-1番目)と排他的論理和をとる

p.219 ブロック暗号のパディングが合っているかどうかの情報(サイドチャネル)を攻撃者が取得して情報を解読する攻撃
※PKCS#7パディングでは平文がブロックに対し1バイト足りなければ0x01を1個、2バイト足りなければ0x02を2個...と足す

p.219 攻撃者は暗号文(n-1番目)の末尾のバイトを書き換えて送信
p.219 上記によって計算された結果の平文(n番目)の末尾のパディングが正しく0x01になるまで繰り返す
-> m3'=Dec(c3)⊕c2' となるm3'(0x01)とc2'(書き換えた値)が判明したことになる
-> Dec(c3)=m3'⊕c2'となるから、m3=Dec(c3)⊕c2と合わせると:m3=(m3'⊕c2')⊕c2
-> 暗号文を復号できなくても=Dec(c3)が分からなくても、暗号文(c2)とオラクル(m3', c2')から平文(m3)が判明する
p.219 これを末尾が0x02 0x02、0x03 0x03 0x03、...となるように増やしていくと全体を明らかにできる
p.219 たくさん推測する+推測が成功したかどうか知る:プロトコルによってはパディングのエラーが丸見え
p.219 サーバの挙動(レスポンスタイミング)からパディングの成否を観察するタイミングオラクル攻撃が必要なケースもある
p.219 パディングオラクルを回避するには:データの完全性を検証すればよい(例:認証つき暗号、GCMとか)

7.4.2 TLSに対する攻撃

p.219 2001年のSerge Vaudenay論文でTLSなどへのパディングオラクル攻撃を発表
p.219 TLS1.0ではパディングのエラーはdecryption_failed(TLSアラートプロトコル、21)、MACエラーはbad_record_mac(同20):TLSアラートプロトコルなので暗号化されて通知される=攻撃者が区別できない、悪用困難

p.219 2003年Canvelらによるタイミングオラクル攻撃の改良:OpenSSL攻撃に成功(パディングが正しくない場合OpenSSLがMAC計算をスキップするのでレスポンスが少し早まるのを利用)
p.219 IMAPサーバを攻撃し、1時間でパスワードを取得
p.220 TLSに対するパディングオラクル攻撃は、推測に失敗しエラーになると、アラートプロトコルの仕様によりTLSセッションが破棄されるため、同じ暗号化ブロックに対する書き換えを連続で実行できない
p.220 Outlook + IMAPの場合、デフォルトで5分に1回、アカウントのフォルダ(inbox, outbox, ..)ごとにリクエストを投げる:全てにパスワード(攻撃対象の機微データ)が含まれている

p.220 OpenSSLなどはCBCモードのセキュリティを改善し、漏洩を最小となるよう修正(パディングが正しくなくてもMAC計算をスキップしないように)
p.220 また、TLS1.1でdecryption_failedアラートは非推奨に

https://datatracker.ietf.org/doc/html/rfc4346

RFC4346
   Note: Differentiating between bad_record_mac and decryption_failed
         alerts may permit certain attacks against CBC mode as used in
         TLS [CBCATT].  It is preferable to uniformly use the
         bad_record_mac alert to hide the specific type of the error.

p.220 ただし、MACのパフォーマンスはデータサイズにある程度依存するので、タイミングの差はサイドチャネルとして残ってしまう
p.220 これが2013年にLucky 13と名付けられた攻撃

7.4.3 影響

p.220 極めて些細なタイミング差の検出のため、標的サーバの近くに攻撃者がいる必要がある
p.220 研究者の実験では攻撃者と標的サーバが同一ローカルネットワークに存在
p.220 DTLSに対してはタイミング差を増幅する技術が効果的(TLSには効かない)

KIDANI AkitoKIDANI Akito
●自律的なシステムに対する攻撃

p.221 自律的なシステム:サーバとのやりとりが頻繁、多くは接続失敗時の再試行機構をもつ(ので攻撃しやすい)
p.221 格好の標的IMAP:機微データ(パスワード)が毎回同じ場所に格納[1]
p.221 16バイトの推測に840万回の接続が必要
p.221 オラクルの推測失敗=TLSエラー、セッション切断:毎回フルハンドシェイクなので時間かかる

●一部の平文が判明している場合の攻撃

p.221 あるブロックの最後の1バイトが判明している場合、残りの各バイトがおよそ65536回(2^16)で解読可能

●JavaScriptマルウェアを使ったブラウザに対する攻撃

p.221 HTTPクッキーが標的:リクエスト中のクッキー位置を操作して、暗号ブロックのうち1バイトのみ不明であるよう並べ替える
p.221 1バイト復元するのに8192回(2^13)必要

7.4.4 緩和策

p.221 AlFardann/Patersonは各ライブラリ修正をまってから公表:パッチ当てればOK
p.221 CBCを含む暗号スイートが弱い:とはいえCBCを使わないのは簡単ではない
p.221 CBC(ブロック暗号)がダメならストリーム暗号を使えばよい:しかしRC4には別の問題(7.5節参照)
p.221 対策1:他のストリーム暗号(ChaCha20)が追加される予定:2016年追加済み(RFC7905
p.221 対策2:TLS1.2の認証付き暗号(GCM)を使う
p.222 Encrypt-then-MACを使うプロトコル拡張もあるが普及度不明(RFC7366[2]

脚注
  1. RFC 3501によると、loginコマンドのパラメータがユーザー名とパスワードになっている。 ↩︎

  2. encrypt_then_mac 22 (0x16)のTLS拡張。extension_dataは空でよい。 ↩︎

KIDANI AkitoKIDANI Akito

7.5 RC4[1]の弱点

p.222 RC4:1987年にRon Rivestが1987年に設計した暗号[2]
p.222 数多くの欠陥にもかかわらず最も普及した暗号の1つ:簡潔に実装でき、ソフトウェアハードウェア双方で高速に実行できるため
p.222 破られたとはいえ、実用的と言えるほど攻撃が改良されておらず、いまだに使われている(惰性や無知もその理由)
p.222 ただし、TLS1.2では代替策(ChaCha20)があるのでRC4を使うべきではない

7.5.1 鍵スケジューリングの弱点

p.222 鍵スケジューリングのアルゴリズムの弱点(2001年のFluhrer, Mantin and Shamir attack):一部分が分かれば初期出力の多くを決定できる
p.222 2001年、WEPプロトコル[3]はこの弱点により破られた
p.222 WEPはマスター鍵と類似したセッション鍵を利用していたため、解読された
p.222 TLSでは毎回異なる鍵を利用するため、同じ問題は発生しない:結果として広く利用され続けた
p.222 筆者の2010年の調査では98%のサーバがRC4に対応、他のスイートより高優先度
p.223 2011年のBEAST攻撃の結果、ブロック暗号が安全でなくなり、2013年3月(RC4脆弱性発表時点)ではトラフィックの50%、2014年6月には26%をRC4が占めた

7.5.2 初期に発見された単一バイトの偏り

p.223 鍵ストリームに他の値より頻出する値があることは2001年から知られていた
p.223 特に、2つ目のバイトは128分の1の確率で0になる(期待値は256分の1)
p.223 0への偏りは危険:排他的論理和をとっても元の値が変わらないため
p.223 複数のTLS接続を攻撃し、2つめのバイトで最も頻出するものが平文と同じ、と推測できる
p.224 TLSのFinishedメッセージなどは接続のたびに変化するので攻撃対象として不適切
p.224 一方、HTTPクッキーやパスワードは毎回同じ場所に格納されるので攻撃しやすい

脚注
  1. 概要はp.7: https://zenn.dev/link/comments/2f223c3504bd38 ↩︎

  2. 正式名称はRivest Cipher 4。彼は1992年にMD5も設計している。他にもRC2, RC5, RC6, MD2, MD4, MD6など。 ↩︎

  3. 1997年に登場した無線ネットワークのセキュリティのためのプロトコル。 ↩︎

KIDANI AkitoKIDANI Akito

7.5.3 先頭256バイトにおける偏り

p.224 2013年3月のAlFardanらの論文で新たな弱点と2つの攻撃発表(先頭256バイトの偏りと二重バイトの偏り)
p.224 2^44通りの鍵ストリームを分析:先頭256バイトのあるバイトは10, 23になりやすい
p.224 2^32件のデータサンプルがあれば先頭256バイトをほぼ解読できる:パスワードやクッキーなどデータの文字種類が少ない場合、2^28で十分
p.225 RC4は元々2^128のセキュリティを保証していたが先頭256バイトはこの限りではない
p.225 先頭256バイトの攻撃は深刻だが、下記制約のため理論上の攻撃でしかない

●接続の数

p.225 2^28のサンプルを手に入れるには時間がかかる
p.225 論文で引用された実験では能動的な攻撃で500接続/秒以上で16時間かけて2^25接続
p.225 受動的な攻撃の場合、1接続/秒だと2^28接続に8年以上かかる
p.225 MITM攻撃で接続頻度を上げても、新しい接続が必要なので接続のたびにフルハンドシェイクが必要なので時間がかかる

●攻撃位置

p.225 上述の通りMITM攻撃が必須

●攻撃の範囲

p.225 先頭256バイトのみが対象:価値のあるデータが出ることは稀
p.226 主要ブラウザではクッキーが220バイトより後に配置されるためほぼ攻撃対象外(TLSの先頭36バイトは意味がない[1]
p.226 Basic認証もChromeのみ先頭100バイトの位置にくるだけ

7.5.4 二重バイト攻撃

p.226 RC4の暗号化ストリームに一定間隔で連続して、連続したバイトの偏りが現れる
p.226 複数サンプルを取得するのに同じ鍵を利用できる=同一の接続上で複数サンプルを取得できる
p.226 同じ平文を何度も暗号化する必要があるため受動的攻撃は不可能
p.226 512バイトのPOSTリクエストを13*2^30回暗号化すると16バイトの平文を解読できる:3.25テラバイトのデータを送受信する必要がある(10万リクエスト/分でも83日かかる)ため、現実的ではない

7.5.5 その後の改良された攻撃

p.226 2015年3月のGarmanらの攻撃:セッションクッキー摂取に2^26回で十分
p.226 2015年3月のImperva社の攻撃:RC4で不適切鍵が生成されTLS先頭64バイトが危殆化する可能性
p.227 ->受動的こうげきにより2^24回に1回の頻度でTLS接続が破られうる
p.227 2015年7月VanhoefとPiessensの句げき:16種類の文字からなるクッキー摂取に75時間(BEAST同様、JavaScriptマルウェアにより攻撃を高速化:ただし秒間4450リクエスト、やや非現実的)

7.5.6 緩和策:RC4か、BEASTか、Lucky 13か、POODLEか

p.227 RC4への攻撃は深刻だが実世界で起こる可能性は低い:とはいえ安全マージンが小さく、使用中止すべき
p.227 問題は(2013年当時は)安全な代替手段がないこと

●相互運用性

p.227 RC4はかなり普及しており、それ以外をサポートしていないクライアントもごく僅かだが存在[2]
p.227 これらのクライアントをサポートするため、最下位の暗号スイートとしてRC4を残すのはあり

●安全性

p.227 RC4を無効化した場合TLS1.0以前のバージョンでCBCモードとなる:BEAST, POODLE, Lucky 13の可能性(TLS1.2にすべき(認証暗号GCMが使える))
p.227 BEAST POODLE RC4どれも実行は困難
p.228 弱い暗号スイートを全て避けるのが今のところベストな方法
p.228 Facebookは2017年6月時点でRC4を許可していた[3]
p.228 2015年MicrosoftはWindows 8.1でRC4を非推奨に、Firefoxも同様の方針を採用(一部のサイトのみ許可)
p.228 SSL Pulseの2015年のデータでは、全体の0.6%のサイトがRC4にしか対応していない
p.228 2015年2月、RFC7465が発行:TLSにおけるRC4利用を禁止
p.228 2016年中にモダンブラウザはRC4を完全廃止:RC4の攻撃は過去のものとなった

脚注
  1. と書かれているが正直よくわかっていない...このサイトによると Since the first 36 bytes of plaintext are formed from an unpredictable Finished message when SHA-1 is the selected hashing algorithm in the TLS Record Protocol, these first 36 bytes cannot be recovered. ということなので、暗号化して送られるのはFinishedハンドシェイクメッセージ以降であり、Finishedのサイズが36バイト、という話か?SSL3.0では36バイトだったがTLS1.2では12バイトだったはず...謎。 ↩︎

  2. Cloudflareのブログによると、2007年頃にリリースされた古い携帯電話やアプリが該当する。それ以外にも、アメリカで多くのVPNなどのプロキシがRC4を利用していた。 ↩︎

  3. 2022年3月現在、SSLLabsで確認したところ、www.facebook.comはTLS1.0, 1.1をサポートしているものの、RC4のサポートは廃止されているようだった。SSL Pulseの過去データを参照すると2017年には30%近くのサイトがRC4をサポートしていたが、2022年現在では6%まで減少している。 ↩︎

KIDANI AkitoKIDANI Akito

7.6 トリプルハンドシェイク攻撃

p.229 2009年の再ネゴシエーション脆弱性->プロトコル修正(拡張"renegotiation_info" (0xff01)):安全な再ネゴシエーション(7.1節)
p.229 2014年にトリプルハンドシェイク攻撃で破られる(結果として2018年のTLS1.3では再ネゴシエーション廃止)

7.6.1 攻撃

p.229 再ネゴシエーションの安全性確保:クライアントが以前の接続のverify_data提示(クライアントしか知らない、暗号化されて転送される)
p.229 TLSの弱点2つを組み合わせ3ステップで攻撃

■ステップ1:未知の鍵が共有される弱点

p.229 RSA鍵交換の弱点:未知の鍵共有(Unknown Key-Share)
p.229 マスターシークレットの生成過程

  1. クライアントがプリマスターシークレットと、乱数生成し送信
  2. サーバーが乱数生成し送信
  3. 3つの値からマスターシークレット計算
    p.229 乱数は平文で送信、プリマスターシークレットはサーバー公開鍵で暗号化
    p.230 最初のハンドシェイク:悪意あるサーバーでクライアントのPMSと乱数を奪い、同じ値で攻撃対象サーバーに接続し、マスターシークレットを共有する[1]
    p.230 この時点では何も悪さができない

p.231 DHE鍵交換に対する攻撃もある:TLSの主な実装では素数でないDHパラメータが許容されていた[2]
p.231 ECDHE鍵交換であれば任意のパラメータが使えずnamed curveのみ利用可能[3]

脚注
  1. マスターシークレットの共有が問題となるので、RFC 7627では、仕様を変更してハンドシェイクメッセージを含めてマスターシークレットを計算し、接続ごとにユニークなマスターシークレットを得られればこの問題は発生しない、と書かれている。 ↩︎

  2. https://mitls.org/pages/attacks/3SHAKE の説明によれば、問題のDHパラメータが素数かどうかを計算するのは計算コストが高すぎるためそのような実装になっていた。 ↩︎

  3. ステップ1のRSA/DHEの未知の鍵共有は、RFC 7627のIntroductionの説明によると、攻撃対象のサーバがRSA/DHE以外をサポートしていても、自身のClientHelloで送る暗号スイートをRSA/DHEに限定することで回避可能。かつ、ECDHEであっても、脆弱なケースがあるとのこと。 ↩︎

KIDANI AkitoKIDANI Akito
■ステップ2:完全同期

p.231 ステップ1の2つの接続はサーバの証明書が異なる:Certificateメッセージを含むハンドシェイクメッセージをハッシュ化したverify_dataの値が異なる
p.231 verify_dataをextension dataとして利用するrenegotiation_info拡張の仕様のため、再ネゴシエーションを攻撃できない
p.231 しかし、セッションリザンプションのハンドシェイクを利用し、クライアントのセッションリザンプションのリクエストを標的サーバに転送するとCertificateメッセージが省略されるためverify_dataが同一となり攻撃が可能になる[1]
p.231 攻撃者はクライアントにもサーバにもデータを送信できる

■ステップ3:なりすまし

p.232 クライアント証明書を要求するサーバのリソースへリクエストし、なりすますことが可能
p.232 再ネゴシエーション後はトラフィックが改めて暗号化されるため、攻撃者は中身を見れなくなる

7.6.2 影響

p.233 最も簡単な悪用方法:犠牲者の認証情報で標的サーバにリクエストを送る
p.233 しかし、攻撃はそれほど簡単ではない

  • エントリーポイントの探索
  • ペイロードの設計
  • 再ネゴシエーション後の攻撃結果確認不可・同一接続上での連続攻撃不可

p.233 再ネゴシエーション前の双方にデータ送信可能な状況の方が危険(事実上のフィッシング):JavaScriptコードを挿入可能

7.6.3 要件

p.233 1つ目の要件:クライアント証明書を使っているサイトにのみ攻撃可能
p.233 2つ目の要件:普段使ってない怪しいサイトでクライアント証明書を利用させる必要性
p.233 クライアント証明書を使っているサイトが少ないのでとトリプルハンドシェイク攻撃を適用できるケースはそれほど多くないは、狙えるのは機微情報

7.6.4 緩和策

p.234 プロトコルの問題:2015年9月発行のRFC 7627 Transport Layer Security (TLS) Session Hash and Extended Master Secret Extensionで対処
p.234 当初は再ネゴシエーションの強化も検討されたが頓挫
p.234 ブラウザベンダーの短期的解決策

  • 再ネゴシエーション後の証明書変更を検出して接続切断
  • 安全でないDHパラメータを拒否
    p.234 IEではライブラリも修正されたので古いバージョンでも安全なはず
    p.234 ただし、SASL[2]、PEAP[3]、Channel ID[4]を利用している場合に危険が残る
    p.234 対策は以下の3点
  • すべてのアクセスにクライアント証明書を要求する:最初の接続を実行しにくくする
  • 再ネゴシエーションを無効にする
  • ECDHE暗号スイートのみを有効にする(RSA/DHEを無効にする):Android 2.X/IE9onWindowsXPあたりでは使えないが...
脚注
  1. RFC 7627によると、verify_dataが同一となることで、renegotiation_info拡張だけでなく、RFC5929で定義されているTLSチャネルバインディングも影響を受ける。同じくFinishedメッセージの唯一性に依拠している中間者攻撃を防ぐ仕組みのため。 ↩︎

  2. Wikipediaより:Simple Authentication and Security Layer(SASL)は、インターネットプロトコルにおける認証とデータセキュリティのためのフレームワーク。RFC 4422など。LDAPやXMPPのほか、POP、IMAP、および SMTP ユーザーアクセスプロトコルの追加認証メカニズムとして機能する(参考)。 ↩︎

  3. Protected Extensible Authentication Protocol。WiFiネットワークのセキュリティを強化するために使用されるセキュリティプロトコルで、EAP(拡張認証プロトコル)の速度とトランスポート層セキュリティ(TLS)トンネルを組み合わせたもの(参考)。MSのものとCISCOのもので仕様が微妙に異なるらしい(参考)。 ↩︎

  4. インターネットドラフトで定義されている(リンク)。クライアント証明書が暗号化開始前に送られることを問題視して、別のクライアント認証の手法を提案している。 ↩︎

KIDANI AkitoKIDANI Akito
RFC 7627 Transport Layer Security (TLS) Session Hash and Extended Master Secret Extension
  • 過去のハンドシェイクメッセージを利用して新しい拡張マスターシークレット(extended master secret)を計算
RFC7627 拡張マスターシークレットの計算
   master_secret = PRF(pre_master_secret, "extended master secret", session_hash)[0..47];
  • 以下の点で通常のマスターシークレットの計算と異なる
    • ラベルがmaster secretではなくextended master secret
    • ClientHello.random + ServerHello.randomではなくsession_hashを利用
  • session_hash = Hash(handshake_messages):Finishedメッセージのverify_data計算時に計算されるものと同一。セッション再開の場合は新規セッションが作られないので、session_hashも存在しない。
  • session_hashにはClientHello.random + ServerHello.randomに加えて、利用可能な暗号スイートや鍵交換情報、利用された証明書などのハンドシェイク時のログが含まれる
  • 新しいTLS拡張extended_master_secret(0x0017)を利用する:extension_dataは空なので16進数表記で00 17 00 00となる
  • このRFCに対応済みのクライアントは全てのClientHelloでTLS拡張extended_master_secretを送信する
  • それを受け取った対応済みのサーバもServerHelloで同じ拡張を送信する
  • 双方が利用に合意した場合、新しいセッションでは拡張マスターシークレットを利用する
  • クライアントは拡張マスターシークレットを利用していないセッションを再開する場合は、セッションリザンプションのハンドシェイクではなくフルハンドシェイクを実施せよ
  • セッションリザンプションを実施する場合はTLS拡張extended_master_secretが必須:新旧セッションで同拡張がない場合や同拡張の有無に差異がある場合[1]は、handshake_failureアラートプロトコルを送信
  • SSL3.0はTLS拡張が使えないのでサポート対象外
脚注
  1. 例外的に、オリジナルセッションでextended_master_secret拡張を使わず、再開セッションでextended_master_secretを利用する場合は、handshake_failureアラートを送信せず、代わりにフルハンドシェイクを実施する。 ↩︎

KIDANI AkitoKIDANI Akito

7.7 POODLE

Padding Oracle On Downgraded Legacy Encryptionの略。

p.235 2014年10月、Googleのセキュリティチームが発見したSSL3.0の脆弱性
p.235 CBCモードでパディングが保護されないのが根本原因:パディングオラクル攻撃が可能
p.235 SSL3.0ではパディング構成と検証の規則がゆるい:パディング部分に何を入れても良いことになっていた=改竄検知不可
p.235 TLS1.0ではパディング長を示す値が入るよう修正済
p.236 最後のブロックが全てパディングの場合、末尾のバイトがAES(16バイトずつのブロック)なら15、3DES(8バイトずつのブロック)なら7となることを利用する
p.236 最後のブロックが全てパディングの場合、MAC検証対象外なので、暗号文を書き換えてもSSL3.0では検出されない
p.236 最後のブロックの暗号文を書き換えて復号時エラーがでない=正しいパディング値(15or7)が得られたことになる(256分の1の確率で成功する)
p.236 CBCにおける暗号化:暗号化 ( 平文n ⊕ 暗号文n-1 ) = 暗号文n
p.236 CBCにおける復号:復号(暗号文n) ⊕ 暗号文n-1 = 平文n
p.237 攻撃対象のブロックをiとし、その末尾のバイトのみ考える:暗号化(平文i[15] ⊕ 暗号文i-1[15]) = 暗号文i[15] ...(A)
p.237 AESの場合末尾のバイトが15となる:攻撃対象のブロックをパディングブロック(暗号文の末尾)に配置(参考:SSL v3.0の脆弱性「POODLE」ってかわいい名前だけど何?? | BLOG - DeNA Engineering

  • 復号(暗号文i[15]) ⊕ 暗号文n-1[15] = 15
  • すなわち:復号(暗号文i[15]) = 15 ⊕ 暗号文n-1[15] ...(B)
    p.237 (A)の左辺と(B)の右辺より
  • 平文i[15] ⊕ 暗号文i-1[15] = 15 ⊕ 暗号文n-1[15]
  • すなわち:平文i[15] = 15 ⊕ 暗号文n-1[15] ⊕ 暗号文i-1[15]
  • 2回の排他的論理和をとるだけで1バイトが推測できる

7.7.1 実用的な攻撃

p.237 他の脆弱性同様、POODLE悪用には複雑な準備が必要

  1. 犠牲者のブラウザから標的サーバへ任意のリクエストを送信できる
  2. 犠牲者のブラウザからネットワークを制御する(BEAST攻撃同様)
    p.237 具体的にはPOSTリクエストで送信先URLとリクエストボディを制御し、任意データを埋め込んで最後尾暗号ブロックがパディングのみとなるようにする
    p.238 Cookieを攻撃する例
  • HTTPリクエストのうち..Cook「ie: JSESSIONID=B」3DF..の部分が暗号ブロックとなる
  • 不明なのはクッキーの最初の1文字:これが16バイト目に来ている
    p.238 送信データを1バイトずつ減らしていき、暗号ブロックが1つ減る(16バイト減る)タイミングでパディングオンリーのブロックの存在が確定する
    p.238 送信先URL(クッキーの前)とリクエストボディ(クッキーの後ろ)を調整して、残りのクッキーを推測できる
    p.238 1文字256回で成功する:200文字推定しても5万回以下のリクエスト数でクッキーがわかる、本章の他のどの攻撃より効率的!

7.7.2 影響

p.238 当時の主要なブラウザはTLSハンドシェイク失敗時にセルフダウングレードする設計:ほぼ全てのサーバがTLS1.0に対応しており、TLS1.0以降にダウングレード攻撃対策があっても意味がない(詳細は6.6節 プロトコルダウングレード攻撃
p.239 クッキーの値やパスワードなどの小さくて価値ある情報が攻撃対象

p.239 2014年12月、TLS実装上の問題でPOODLEの影響があることが明らかに:パディング検証の実装漏れ
p.239 ハードウェアアクセラレーションカードなどで発見
p.239 2014年12月のSSL Pulseの結果では10%のサーバがPOODLEに脆弱[1]

7.7.3 緩和策

p.239 ブラウザベンダはSSL3.0撤廃を加速:Safari以外がフォールバックを無効に、SafariはSSL3.0のCBCスイートを無効に[2]
p.239 IE11が遅れて2015年4月にSSL3.0をデフォルトで無効に
p.239 ダウングレードに関する対策:TLS_FALLBACK_SCSVというシグナリング用の暗号スイート(RFC7507)(詳細は6.6.6 現代的なロールバック攻撃への防御
p.240 SSL3.0を完全に無効にするのが最大の防御:Windows XPのIE 6などデフォルトでTLS1.0が有効でないケースは注意が必要
p.240 SSL3.0でSafariのようにCBC無効化する場合、RC4の別の問題に注意が必要
p.240 PCI DSS v3.1(2015年4月公開)では2016年6月30日以降SSL利用不可[3]
p.240 2015年6月のRFC 7568でSSL3.0使用廃止(非推奨)

脚注
  1. SSL Pulseデータの推移 (SSL Pulse Trends)によると、以降は割合が低下し2017年には1%以下に。なお、その後2019年に、Sleeping POODLE、Golden POODLE、Zombie POODLEなどの脆弱性が発見されている。 ↩︎

  2. RC4の脆弱性が既に2013年に見つかっているのに強気だな...と思いました。 ↩︎

  3. このときTLS1.1までの利用も禁止する予定だったようだが、2018年6月30日までずれ込んだ(PCI DSS v3.2(2016年4月公開)↩︎

KIDANI AkitoKIDANI Akito

7.8 SLOTH

p.240 SLOTH:Security Losses from Obsolete and Truncated Transcript Hashes、ナマケモノ
p.240 2016年1月、Karthikeyan Bharagavan、Gaetan Leurent[1]がハッシュ関数解析結果を公表:MD5やSHA1に起因する弱点が大量に存在
p.240 トランスクリプト衝突攻撃と呼ばれる攻撃の新種:ここでは選択プレフィクス・トランスクリプト衝突攻撃[2]
p.241 参考:本書4.5節 偽造SSL証明書

7.8.1 TLSに対する攻撃

p.241 TLS以外にもIKE[3]、SSH[4]、TLSチャネルバインディング[5]など複数のプロトコルが攻撃対象

TLS1.2のクライアント認証を破る

p.241 TLS1.2のクライアント認証に対する選択プレフィクス衝突攻撃:プロトコルメッセージに任意のデータを挿入して攻撃(CertificateRequestメッセージのDistinguishedNameに任意データを挿入可能)

      struct {
          ClientCertificateType certificate_types<1..2^8-1>;
          SignatureAndHashAlgorithm
            supported_signature_algorithms<2^16-1>;
          DistinguishedName certificate_authorities<0..2^16-1>;
      } CertificateRequest;

p.241 MD5は衝突方法が判明しているので、攻撃者がクライアント証明書を利用した接続を可能
p.241 中間者攻撃をする攻撃者->犠牲者のCertificateRequestメッセージと、攻撃者->サーバのClientHelloメッセージ(ここでMD5をsignature extensionに指定)のハッシュ値を衝突させる(CertificateVerifyメッセージで利用される)
RFC5246 TLS1.2

      struct {
           digitally-signed struct {
               opaque handshake_messages[handshake_messages_length];
           }
      } CertificateVerify;

※衝突の詳細な仕組みは日本語の解説記事参照:ハッシュ衝突でTLSを破るSLOTH攻撃(CVE-2015-7575)とは何か - ぼちぼち日記

p.241 中間者攻撃で、不正なDHパラメータ(素数でないもの)を利用することで、合意される鍵を推測可能にする
p.241 TLS1.1以前は署名にMD5+SHA1を利用していたが、TLS1.2ではネゴシエーションによってMD5のみが利用可能になるという弱点(必要なハッシュ数が2^77個から2^39まで減少):Javaで悪用可能
p.242 およそ1時間で接続を突破可能[6]

TLS1.2のサーバ認証を破る

p.241 サーバ証明書の場合は任意のデータ挿入ができず前述の攻撃は不可
p.241 ただし、MD5を使っているサーバ証明書であれば事前に2^x個の署名を集めることで2^(128-x)回の
試行で突破できる
p.241 Alexa上位100万サイトのうち、30%がMD5を利用
p.241 Firefox/Chromeで使われるNSSライブラリや、GnuTLS、BouncyCastle:サーバが要求した場合MD5署名を受け入れてしまっていた

TLS1.3のサーバ認証を破る

p.242 TLS1.3のリビジョン10:MD5が利用されていて潜在的に脆弱
RFC8446 TLS1.3(2018.08)ではMD5が完全に取り除かれている

TLS1.0およびTLS1.1のハンドシェイクに対する攻撃

p.242 2^77の試行でTLS1.0/1.1のハンドシェイクへの選択的プレフィクス攻撃が可能
p.242 ダウングレードに要する労力から見て現実的ではない

7.8.2 影響

p.243 各プロトコルの弱点、ライブラリの手抜きが明らかに
p.243 弱いハッシュ関数の利用継続がどのように悪用されるかが理解された[7]
p.243 いずれの攻撃も現実的でなく短期的影響はない

脚注
  1. フランスのINRIAの研究者。INRIA:Institut National de Recherche en Informatique et en Automatique(フランス国立情報学自動制御研究所)。1979年設立、OCamlなどを開発。 ↩︎

  2. 他に、Generic Transcript Collision (総称トランスクリプト衝突)攻撃がある:サーバが同じDHパラメータを使う場合のQUICプロトコルなどに影響。参考:元論文 ↩︎

  3. Internet Key Exchange:インターネット鍵交換。IP パケットレイヤーでの通信を暗号で保護するIPsecで利用されるプロトコル。X.509証明書、DH鍵交換などTLSと共通する要素を用いる。 ↩︎

  4. Secure Shell:ネットワークサービスを安全に運用するための暗号化ネットワークプロトコル。1995年にバージョン1が、2006年にバージョン2が策定されたが、それぞれ互換性がない。 ↩︎

  5. RFC 5929(2010.07) ↩︎

  6. ハッシュ衝突でTLSを破るSLOTH攻撃(CVE-2015-7575)とは何か - ぼちぼち日記によると、攻撃者がサーバとハンドシェイク中にハッシュ衝突を試みる間、犠牲者にはfatalでないwarningレベルのアラートを送り続けてTLSセッションを維持するらしい。 ↩︎

  7. MD5を中心に説明されているが、SHA1でも試行回数が増えるだけでほぼ同じ問題がある。参考:INRIAのサイト ↩︎

KIDANI AkitoKIDANI Akito

SSL2.0のハンドシェイク

出典:DROWN: Breaking TLS using SSLv2

Internet Draft: The SSL Protocol

5.2.1 Assuming no session-identifier

client-hello            C -> S: challenge, cipher_specs
server-hello            S -> C: connection-id, server_certificate,
                                  cipher_specs
client-master-key       C -> S: {master_key}server_public_key
client-finish           C -> S: {connection-id}client_write_key
server-verify           S -> C: {challenge}server_write_key
server-finish           S -> C: {new_session_id}server_write_key

5.2.2 Assuming a session-identifier was found by both client & server

client-hello            C -> S: challenge, session_id, cipher_specs
server-hello            S -> C: connection-id, session_id_hit
client-finish           C -> S: {connection-id}client_write_key
server-verify           S -> C: {challenge}server_write_key
server-finish           S -> C: {session_id}server_write_key

5.2.3 Assuming a session-identifier was used and client authentication
is used

client-hello            C -> S: challenge, session_id, cipher_specs
server-hello            S -> C: connection-id, session_id_hit
client-finish           C -> S: {connection-id}client_write_key
server-verify           S -> C: {challenge}server_write_key
request-certificate     S -> C: {auth_type,challenge'}
                                  server_write_key
client-certificate      C -> S: {cert_type,client_cert,
                                   response_data}client_write_key
server-finish           S -> C: {session_id}server_write_key
[ClientHello]
char MSG-CLIENT-HELLO
char CLIENT-VERSION-MSB
char CLIENT-VERSION-LSB
char CIPHER-SPECS-LENGTH-MSB
char CIPHER-SPECS-LENGTH-LSB
char SESSION-ID-LENGTH-MSB
char SESSION-ID-LENGTH-LSB
char CHALLENGE-LENGTH-MSB
char CHALLENGE-LENGTH-LSB
char CIPHER-SPECS-DATA[(MSB<<8)|LSB]
char SESSION-ID-DATA[(MSB<<8)|LSB]
char CHALLENGE-DATA[(MSB<<8)|LSB]

[ClientMasterKey]
char MSG-CLIENT-MASTER-KEY
char CIPHER-KIND[3]
char CLEAR-KEY-LENGTH-MSB
char CLEAR-KEY-LENGTH-LSB
char ENCRYPTED-KEY-LENGTH-MSB
char ENCRYPTED-KEY-LENGTH-LSB
char KEY-ARG-LENGTH-MSB
char KEY-ARG-LENGTH-LSB
char CLEAR-KEY-DATA[MSB<<8|LSB]
char ENCRYPTED-KEY-DATA[MSB<<8|LSB]
char KEY-ARG-DATA[MSB<<8|LSB]

[ClientFinished]
char MSG-CLIENT-FINISHED
char CONNECTION-ID[N-1]
[ServerHello]
char MSG-SERVER-HELLO
char SESSION-ID-HIT
char CERTIFICATE-TYPE
char SERVER-VERSION-MSB
char SERVER-VERSION-LSB
char CERTIFICATE-LENGTH-MSB
char CERTIFICATE-LENGTH-LSB
char CIPHER-SPECS-LENGTH-MSB
char CIPHER-SPECS-LENGTH-LSB
char CONNECTION-ID-LENGTH-MSB
char CONNECTION-ID-LENGTH-LSB
char CERTIFICATE-DATA[MSB<<8|LSB]
char CIPHER-SPECS-DATA[MSB<<8|LSB]
char CONNECTION-ID-DATA[MSB<<8|LSB]

[ServerVerify]
char MSG-SERVER-VERIFY
char CHALLENGE-DATA[N-1]

[ServerFinished]
char MSG-SERVER-FINISHED
char SESSION-ID-DATA[N-1]
KIDANI AkitoKIDANI Akito

7.9 DROWN

p.243 Decrypting RSA using Obsolete and Weakened eNcryption(ちょっと無理があるような...
p.243 2016年3月発表:SSL2.0とTLSのクロスプロトコル攻撃[1]
p.243 RSA鍵交換のTLSハンドシェイクを受動的に記録して暗号文を破るのに利用
p.243 攻撃亜種が複数

  • RSA鍵再利用サーバへの攻撃
  • SSL2.0サーバとホスト名共有するサーバへの攻撃
  • QUICプロトコルへの攻撃

7.9.1 TLSに対する攻撃

p.243 汎用的だが実行困難な攻撃と、設定に依存するが実現可能性が高い攻撃がある

汎用型DROWN

p.243 Bleichenbacherパディングオラクル攻撃の一種[2]:SSL2.0サーバがオラクルとなる
p.243 SSL2.0サーバのオラクルを利用して、TLSを攻撃[3]:TLS接続1000+SSL2.0サーバ接続40,000、オフライン計算2^50回が必要
p.244 EC2インスタンスで8時間[4]でTLSセッションを突破
p.244 公表時点で全サーバの約79%が脆弱

特定型DROWN

p.244 OpenSSLの欠陥(CVE-2015-3197[5], CVE-2016-0703[6]CVE-2016-0704[7])を利用しリアルタイム攻撃が可能
p.244 必要な接続数は8000、単一コアCPUでも1分未満で攻撃可能
p.244 公表時点で全サーバの約26%が脆弱

7.9.2 影響


https://drownattack.com/ より

p.244 設定が完全でSSL2.0に未対応なサーバでも、他のサーバと設定を共有すると脆弱になってしまう:問題は4つ

  1. 汎用型DROWNに対する直接的な脆弱性:受動的攻撃の対象
  2. 特定型DROWNに対する直接的な脆弱性:能動的攻撃も可能
  3. RSA鍵の再利用を使って脆弱性を移す

p.244 SSL2.0サーバとRSA鍵を共有していると攻撃を受ける
p.244 SMTPとHTTPSでRSA鍵が共有されがち、かつ、SMTPではSSL2.0が有効になっていることが多い

  1. 証明書のホスト名の重なりを使って脆弱性を移す

p.244 特定型DROWNに脆弱なサーバのサーバ証明書に載っている任意のホストに攻撃可能(中間者攻撃

7.9.3 緩和策

p.245 SSL2.0を無効にすれば即座に安全:OpenSSLのパッチも要チェック
p.245 RSA鍵の再生成もある程度有効(特定DROWNが相手の場合、4の問題のため無意味)
p.245 相手の証明書から自ホストを削除してもらうか、証明書発行元CAに連絡

脚注
  1. プロフェッショナルSSL/TLSの紙版ではクロスプラットフォームと表記されているが、redhatwikipediaではcross-protocolと書かれているのでこちらが正しいと思割れる。 ↩︎

  2. RSAによる暗号化は、SSLv2では、SSLv3/TLSのプリマスターシークレットに相当するマスターキーの暗号化に使われる。マスターキーの一部は暗号化されず平文のまま送信されることもあり、これはクリアキーデータと呼ばれる。特に、輸出規制暗号スイート(例えばSSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5)を利用する場合、暗号化されるシークレットマスターキーは5バイトのみとなる。RSAの暗号文は、最初が00 02のバイトで始まるPKCS#1 v1.5(RFC2313)形式である:最初が00 02で始まらない場合、サーバが復号できず、エラーメッセージを返したり接続を切断したりする。PKCS#1 v1.5の場合、 00 02(00でないパディング文字列8バイト以上) 00 (復号されたシークレットマスターキー) という形式なので、エラーにならなかった場合は10バイト目以降の最初の00以降が目的のデータと分かる。 ↩︎

  3. TLSのプリマスターシークレットは48バイトあるため、TLSのClientKeyExchangeそのままでは、48バイトの強度をもつSSLv2の暗号スイートが存在しないため、「トリマー」によって変換してSSLv2サーバに投げつけ、結果をみることでPKCS#1 v1.5形式かどうか判定し、暗号文を少しずつ解読する。 ↩︎

  4. 論文によると、 g2.2xlargeのスポットインスタンス150台、g2.8xlargeのスポットインスタンス50台で、コストは440ドル。 ↩︎

  5. 論文によると、2010年にSSLv2を無効にしようとしたが、コードは削除されておらず、SSLv2が利用可能だったという脆弱性。 ↩︎

  6. 中間者攻撃によりマスターキー(TLSでいうマスターシークレット)を特定されてしまう脆弱性。 ↩︎

  7. 輸出暗号スイート利用時に、マスターキーの一部を上書きされてしまう脆弱性。 ↩︎

KIDANI AkitoKIDANI Akito

7.10 Sweet32[1]

p.245 2016年8月、INRIAの研究者らが64ビットの短いブロック暗号を利用し続けることの問題を提起
p.245 ブロック暗号が安全なのは2^(n/2)回まで:誕生日のパラドクスにより衝突可能性が高まる
p.245 2つのメッセージが同じ暗号文に暗号化される(衝突する)場合

CBCモード
// 基本:i番目の平文とi-1番目の暗号文の排他的論理和をとり、暗号化するとi番目の暗号文
Ci = E(PiCi-1)

// i番目の暗号文とj番目の暗号文が同じ場合
PiCi-1 = PjCj-1
// 式変形すると以下のようになる
// 暗号文はネットワーク上で手に入れておくことで、i番目の平文がわかればj番目の平文もわかる
PiPj = Ci-1Cj-1

7.10.1 事前条件

p.246 概念的には前述のように簡単だが、実行は難しい
p.246 ネットワーク上で暗号化されたデータを長時間に渡り観察することが必要

●暗号

p.246 現代的なブロック暗号は128ビット以上:3DES、Blowfishは64ビット[2]
p.246 TLSでは3DESが広く利用可能であった:実際の利用は1~2%程度か[3]
p.246 BlowfishはTLSでは利用できない:OpenVPNではデフォルト利用可能

●ブラウザの制御

p.246 Sweet32は選択プレフィクス衝突攻撃の一種:暗号化データの制御が必要
p.246 HTTPSリクエストに含まれるクッキーを狙って、既知の平文をURLに含めてリクエストを送信させる

●攻撃に必要なデータの量

p.246 DESは64ビットのブロック暗号なので、2^(64/2)=2^32がメッセージ数の上限となる
p.246 8バイトのメッセージ2^32個で32Gバイトとなる[4]
p.247 論文中ではHTTPクッキー16バイトを攻撃対象とし、780Gバイトのデータを集めた
p.247 高速なネットワークでも、数時間から30時間程度必要
p.247 必要なデータ量が多いため、攻撃の有用性が著しく損なわれる:標的アプリケーションのHTTPセッションが切れる可能性が高い

●接続の持続性

p.247 暗号文の衝突には、全てのデータが同一鍵で暗号化される必要がある=TLSおよびTCPコネクションが同一である必要がある
p.247 しかし、ApacheやNginxでは同一コネクションのリクエスト数にデフォルトで上限がある[5]:IISは上限なし
p.247 VPNは長期間に渡り同一のコネクション利用+誕生日パラドクスを避けるため、同一暗号鍵を使い回すことも多い

7.10.2 Impact

p.247 クッキーを奪われセッションハイジャックされる可能性
p.247 Basic認証も同様だが、最近はあまり使われていない

7.10.3 緩和策

p.247 64ビットのブロック暗号を使わないのがベスト:必要になるとすればWindows XPなど
p.247 3DESを無効化できない場合、暗号スイートの中で優先度を最下位にする
p.247 同一TCPコネクション上でのHTTPリクエスト上限の設定も有効

脚注
  1. HPによると、アメリカの成人式的なSweet sixteenと2^32個のメッセージで64ビットのブロック暗号が誕生日のパラドクスにより破られる点を掛けている。 ↩︎

  2. プロフェッショナルSSL/TLSでは触れられていないが、3G通信で利用されるKASUMIも64ビットブロック暗号であり、影響を受ける。元論文より。KASUMIは三菱電機の松井充らが1995年に開発したMISTY-1を元にした暗号。 ↩︎

  3. 3DESに関する情報はSSL Pulseでは得られなかった...ほとんど利用されていないということだろうか。 ↩︎

  4. 暗号技術のすべて p.118-119:128ビットのブロック長の場合、16バイト×2^64=256Eバイト必要になるため、誕生日攻撃は現実的ではない。 ↩︎

  5. ApacheではmaxKeepAliveRequestsのデフォルトは100。Nginxではhttp2_max_requestsのデフォルト値は1000。 ↩︎

KIDANI AkitoKIDANI Akito

7.11 Bullrun

p.248 2013年9月に明らかになったBullrun計画はアメリカNSA(国家安全保障局)の機密計画

  • サーバにハッキングして侵入し秘密鍵を奪い暗号化された通信を破る
  • 製品とセキュリティ標準を弱めて暗号化された通信を破る

p.248 年間2億5千万ドルのBullrun計画が明らかになったことで、標準の開発が疑わしくなった
p.248 イギリスGCHQも同様のEdgehill計画を実施

7.11.1 Dual EC DRBG

p.248 Dual Elliptic Curve Deterministic Random Bit Generator:擬似乱数生成器のためのアルゴリズムの1種
p.248 2005年にISOで標準化されたが、2007年にバックドアの可能性が指摘されたが注目されず
p.248 2013年にBullrun計画が明らかになると、Dual EC DRBGはバックドアと見做される
p.248 NISTは2012年のSP800-90A[1]を更新し、Dual_EC_DRBGを利用しないよう推奨
p.248 2013年のロイターの報道:NSAからRSA Security社への1000万ドルの支払いにより、同社のTLS実装BSAFEのデフォルトPRNGとしてDual EC DRBGが採用された
p.249 Dual EC DRBGはFIPS 140-2(アメリカの暗号モジュールのセキュリティ要件)で要求されている
p.249 プリマスターシークレットを生成するPRNGにバックドアを仕込む=マスターシークレットを見つけ、TLSセッションを破ることができる
p.249 RSA Securityの実装BSAFEでは非標準的なTLS拡張[2]がありPRNGからのデータを公開するようになっていた

脚注
  1. 2022年4月現在の最新版は2015年6月のSP 800-90A Rev. 1。ハッシュ関数に基づくHash_DRBG、HMAC_DRBGとかブロック暗号に基づくCTR_DRBGがある。 ↩︎

  2. Extended Randomはインターネットドラフトで2009年ごろ提案されていたが標準化されていない。 ↩︎

KIDANI AkitoKIDANI Akito

第8章 デプロイ

8.1 鍵

p.251 適切な鍵のアルゴリズム、長さで数年間の強力な認証が実現できる
p.251 最も弱いリンク:鍵の運用、秘密鍵を秘密に管理すること

8.1.1 鍵アルゴリズム

p.251 TLSでは3つのアルゴリズムに対応:DSA、RSA、ECDSA

●DSA

p.251 鍵長が1024ビットに限定されるため安全でない

●RSA

p.251 広くサポートされており、完全に実用的
p.252 ただし、2048ビットの鍵はECDSAと比べてセキュリティ・パフォーマンスに劣る
p.252 3072ビットの鍵に移行するとパフォーマンスが著しく悪化する[1]

●ECDSA

p.252 これから利用が広がるアルゴリズム[2]
p.252 256ビットのECDSA鍵は128ビットの等価安全性をもつ:2048ビットのRSA鍵は112ビットの安全性しかない
p.252 加えて、256ビットECDSA鍵は2048ビットRSA鍵の2倍高速(安全性を同レベルにするには3072ビットRSA鍵が必要だが、こちらは6倍高速)
p.252 欠点としては、全てのユーザーエージェントが対応していない(モダンブラウザはOK)

8.1.2 鍵長

p.252 長期的な保護には128ビット安全性の鍵を使うべき
p.252 鍵をRSA/ECDSA両方デプロイする案もある:維持費、手間がかかる
p.253 鍵長を選ぶ際のポイント

  • 現時点で安全か
  • 鍵を失効させる時点で安全か
  • 秘密情報を、鍵の失効後どれくらい秘密にしておきたいか

8.1.3 鍵の管理

p.252 理論的には鍵長が重要だが、同じくらい鍵の管理が重要
p.252 暗号が破られなくともサーバに侵入して秘密鍵を盗まれたら終わり

  • 秘密鍵を秘密にしておく:CAに生成させては意味がない
  • 乱数生成器に対する考慮:リブート直後のサーバはエントロピーが不足
  • 鍵をパスワードで保護する:鍵を格納するシステムが破られた時の防御
  • 関係ないサーバ間で鍵を共有しない:内部的なアクセス制限にも有効
  • 鍵を頻繁に変更する:鍵にアクセスできるメンバの離職時には変更すべし、古い鍵は完全に消去すべし(特にPFSがない場合)
  • 鍵を安全に格納する:サーバ鍵でなく、中間CAやプライベートCA、公開鍵ピンニングで使われる鍵。HSMを利用すべし[3]
脚注
  1. とはいえ、certbotのissueでは3072ビットもしくは4096ビットをデフォルトにすべし、という議論が行われている(IoTデバイスではパフォーマンス問題が辛い、という意見もあった)。暗号鍵管理の推奨事項をまとめたNISTのSP 800-57 Part 1 Rev. 5でも、3072ビットであれば2030年以降も利用可能とされている。SSL Pulseの2022.01の結果によると、6.6%のサイトが3072ビットの鍵を、6.6%のサイトが4096ビットの鍵を利用している。2048ビットは86%。この結果にはECDSAの鍵長も変換されて含まれているとのこと。 ↩︎

  2. あまり見たことがなかったが、GitHubではECDSAが使われている(公開鍵がRSAでなく楕円曲線になっている)。 ↩︎

  3. AWSのCloudHSM(スペースなし)とか、GoogleのCloud HSMとか、Azure Dedicated HSMとか。 ↩︎

KIDANI AkitoKIDANI Akito

8.2 証明書

8.2.1 証明書の種類

p.254 3種類:DV, OV, EV(詳しくは3.7節を参照)
p.254 EV証明書の特徴

  • CA/B Forumによる検証手順(発行手順)標準化
  • (2018年ごろまで)ブラウザのアドレスバーで強調表示された
  • 失効確認がされる[1]

8.2.2 証明書のホスト名

p.255 証明書の目的:ホスト名に対する信頼の確立
p.255 証明書ホスト名不一致警告を避けるために:自身のTLSサーバを指すDNSエントリーを証明書で確実にカバーする

8.2.3 証明書の共有

p.255 共有方法は2つ

  • 全てのホスト名を含む単一の証明書を使うケース:www.example.com, example.com, blog.example.comなど
  • ワイルドカード証明書を使うケース(CDNなどで広く利用):*.example.com, example.com

p.255 共有によりセキュリティを低下させる可能性がある:証明書の共有=秘密鍵の共有
p.255 秘密鍵の共有なので、別チームや無関係のサイトでは証明書を共有すべきでない
p.256 1つのサイトの脆弱性が他のサイトに向けて悪用される(参考:DROWN脆弱性のケース)

8.2.4 署名アルゴリズム

p.256 CAが発行する証明書への署名のセキュリティ要素:秘密鍵の強度とハッシュ関数の強度
p.256 2010年代前半まではSHA1:2016年から廃止するCAが現れ、2017年からブラウザでも信頼されなくなる[2]

8.2.5 証明書チェーン

p.256 実際に利用するのは証明書チェーン:起点となるルートへの証明書の順序付きリスト
p.256 不完全な証明書チェーンにより接続が無効となる問題がある
p.256 ユーザーエージェントによっては不完全なチェーンを再構築可能:中間CA証明書がキャッシュされているor証明書を解析する
p.256 証明書チェーンの順番が不正なケースは、ユーザーエージェントが修正可能
p.256 余分な証明書が含まれていると、TLSハンドシェイクのパフォーマンスが悪化する:ただし、歴史的理由により、複数の信頼パスがある[3]ため、最適化は難しい

8.2.6 失効

p.257 CRLとOCSPの失効情報を証明書に含めるべき(詳細は3.8節
p.257 CAが信頼できる高速なOCSPレスポンダを提供しているかが重要
p.257 ただし、ブラウザが全接続の失効確認はしていないので今後は不透明
p.257 最良の結果を得るにはOCSPステープリング:パフォーマンス、可用性、プライバシー向上(詳細は5.11節

8.2.7 適切なCAの選択

p.257 DV証明書だけであれば、どのCAでも問題ない:無償のものもある(Let's Encryptなど)[4]
p.257 重要な目的で証明書が必要なら、慎重な見極めが必要:例)公開鍵ピンニングなど

●サービス

p.257 費用がかかっても、質の高い管理インターフェースやサポートを提供するCAが良い

●信頼の範囲

p.257 ユーザが大規模な場合、ルートCAが広範囲で信頼されている必要がある:古参のCAor相互認証された新興CA
p.258 テスト用証明書でテストすべし:古いデバイス/プラットフォームではトラストストアが更新されていないケースも

●新しい技術の迅速な採用

p.258 新しい技術を積極的に採用し、弱い技術からの移行を進めているCAを選ぶべし

●セキュリティ

p.258 業務を安全に運用する能力が一番重要[5]

自己署名証明書とプライベートCA

p.258 一般公開しない場合、ここまで見てきたパブリックCA以外にも、自己署名証明書やプライベートCAという選択肢もある
p.258 自己署名証明書は安全に利用することが難しい:プライベートCAの方が適切

脚注
  1. このブログによれば、ChromeはEV証明書については常にOCSPによる失効確認を行っていたらしい(現在の状況は調べられていないので過去形で記述)。 ↩︎

  2. こちらのページがSHA-1証明書からの移行について詳しい。 ↩︎

  3. 最近だとLet's Encryptの事例が記憶に新しい。 ↩︎

  4. 類似のサービスとしてZeroSSLがあり、クラウドサービスベンダも独自のCAサービスを提供しており、AWSのACMは無料で利用できる。 ↩︎

  5. 過去に問題があった事例としては4.6節以降のComodoHackerのケースがある。 ↩︎

KIDANI AkitoKIDANI Akito

8.3 プロトコルの設定

p.258 セキュリティのみを考慮するなら、TLS1.1以前は無効化すれば良い[1]:モダンブラウザはOK、他の製品やツールは対応していない
p.259 一般的にはTLS1.0〜が必要(なのは本書執筆当時の2017年以前の話[2]が出ている)
p.259 プロトコルの選択にはブラウザのデフォルトプロトコルの考慮が必要:IE6だとSSL3.0がデフォルトだった
p.259 プロトコル選択時に考慮すべきこと:セキュリティ標準(PCI DSS[3]
p.259 古いバージョンが有効な場合、能動的な攻撃者によるダウングレード攻撃の影響を受ける(詳細は9章)

脚注
  1. 2022年1月に出た本書セカンドエディションでのこの辺りの記述が気になるところ...:https://www.feistyduck.com/books/bulletproof-tls-and-pki/ ↩︎

  2. 2021年3月にはRFC 8996 Deprecating TLS 1.0 and TLS 1.1 ↩︎

  3. 2022年5月現在の最新は2022年3月に発行されたv4.0:https://www.pcisecuritystandards.org/document_library。参考 ↩︎

KIDANI AkitoKIDANI Akito

8.4 暗号スイートの設定

8.4.1 サーバにおける暗号スイートのパフォーマンス

p.259 サーバが優先したい暗号スイートをクライアントに強制するのが重要
p.259 能動的な攻撃者:ハンドシェイクの完全性検証により、弱い暗号スイートを使わせることはできないが、プロトコルをダウングレードさせることはできる

8.4.2 暗号の強度

p.260 128ビット安全性のある暗号を使うべき:AES、CAMELLIAがあるが、認証付暗号(AEAD)が利用できるAESの方がベター
p.260 AEADを使うことで、本質的に安全でないCBCのスイートを避けられる
p.260 2012年のNSAの暗号標準Suite B:AEADのGCMの暗号スイートのみを使うことを推奨[1]

8.4.3 PFS

p.260 RSA鍵交換はPFSがないので使うべきでない:認証でのRSA利用は問題ない
p.260 パフォーマンス的にはECDHE > DHE
p.260 PFSがある=毎回の接続が異なる鍵によって保護される
p.260 PFSがない=サーバの秘密鍵に依存:秘密鍵が盗まれると全てのやり取りが復号可能
p.260 ECDHEの曲線:secp256r1やX25519など。新しい曲線を優先する設定にすべき。
p.260 DHE:2048ビットのDHパラメータを設定すべき。Java6のクライアントは1024ビットを超える強度に対応していないので注意。
p.261 方針1:政府機関に標的にされないサーバであれば弱いDHパラメータでも可、ただしユニークなDHパラメータ(破るのにかなりコストがかかる:詳細は6.5節
p.261 方針2:DHEを完全に無効化する(大半のクライアントがより高速なECDHEに対応している)

8.4.4 パフォーマンス

p.261 GCMの暗号スイートは高速:AESはプロセッサの特別な命令セットで速度を向上できCAMELLIAより高速[2]。ハードウェアアクセラレーションを利用することで、キャッシュタイミング攻撃にも耐性[3]
p.261 CBCスイートでSHA256/SHA384を完全性検証に利用するのは避けるべき:セキュリティ優位なし、速度劣化
p.261 GCMで利用するSHA256/SHA284は別物:SHA1もハッシュ関数としてHMACで使うぶんには安全
p.261 ECDHE鍵交換:ecp256r1曲線が128ビット安全性で最適なパフォーマンス

8.4.5 相互運用性

p.262 TLSクライアントはまちまち:不必要に拒絶しない設定は難しい
p.262 暗号スイートの優先順位の推奨設定

  • RC4は使うべきでない
  • 3DESを利用する場合はSWEET32攻撃を調べて慎重に判断
  • Javaのクライアントは256ビットの暗号スイートに対応していない[4]
  • Java8以前では1024ビット以上のDHパラメータ非対応:Java 7はECDHEに対応しているので問題ない[5]
  • ECDHE鍵交換:secp256e1/secp384r1が広くサポートされている。これ以外だとIEなどでネゴシエーションできない
脚注
  1. 128ビット安全性の場合であればTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, またはTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384を推奨。前者がより好ましいとのこと。192ビット安全性を求める場合は、TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384のみ。 ↩︎

  2. x86だとAESENCとかAESDECといった命令セットがある。参考:Wikipedia ↩︎

  3. 2005年の論文:特定のプロセッサや実験に利用したOpen SSLに問題があるわけではなく、AESそのものに問題がある、とのこと。 ↩︎

  4. Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Filesをダウンロードして設定すれば利用できる ↩︎

  5. IBM JavaであればJava6でもECDHEを利用できる(ただしデフォルトではオフ) ↩︎

KIDANI AkitoKIDANI Akito

8.5 サーバの設定とアーキテクチャ

p.262 強力なセキュリティを実現するために:あらゆるベストプラクティスを適用する

  • 不要なサービスの無効化
  • 定期的なパッチ適用
  • 厳格なアクセス制御

p.262 スケールアップして弱点が増えないような設計も必要

8.5.1 共有環境

p.262 共有ホストで暗号化の操作に使うべきではない
p.262 秘密鍵の危殆化を招く攻撃ベクター多数:ファイルシステム、ダイレクトメモリアクセスなど
p.262 仮想環境でも危険:同じCPUを使うのでキャッシュタイミング攻撃が可能
p.263 インフラの共有はコスト/利便性/セキュリティの妥協点
p.263 占有ハード、強固な物理セキュリティ、有能なエンジニア、運用者が不可欠

8.5.2 仮想セキュアホスティング

p.263 仮想セキュアホスティング:無関係な複数のHTTPSサーバを同一IPに配置すること
p.263 これは2006年に追加されたSNI(Server Name Indication)に依存しているため、HTTPSサーバごとに1つのIPアドレスを使う方法が主流[1]
p.263 Android初期バージョンやWindows XPのIEなどが非対応(XPは2014年でサポート切れ)

8.5.3 セッションキャッシュ

p.263 セッションキャッシュ:同じマスターシークレットを使い回し、CPUコストとネットワーク遅延を減らすパフォーマンス最適化手法
p.263 当然、セキュリティが問題になるが、セッションは有効期間があるので多くの場合許容できる
p.263 セキュリティを強化したい場合は、1時間など短い有効期間にすべし:無効化はパフォーマンス悪化の懸念あり

p.263 セッションチケット[2]:すべての接続が同じチケット鍵に依存するため、手動でチケット鍵をデプロイし定期的に交換すべし(Twitterの事例:12時間ごとにデプロイ、古い鍵は36時間後に削除)

脚注
  1. AWSのAPI GatewayやALBもSNIを利用して任意のドメインでの提供を可能にしている。参考 ↩︎

  2. 2008年1月のRFC 5077:サーバではなくクライアントがセッションを保持。 ↩︎

KIDANI AkitoKIDANI Akito

RFC5077 Transport Layer Security (TLS) Session Resumption without Server-Side State

  • 初期バージョンは2006年のRFC4507
  • ハンドシェイクは4パターン
    1. チケットなしのハンドシェイク
    2. チケットありでサーバがチケット更新するハンドシェイク
    3. チケットありでサーバがチケット更新しないハンドシェイク
    4. チケットありだが、サーバがチケットを却下するハンドシェイク
  1. チケットなしの最初のハンドシェイクは以下のシーケンス
  • 4.のケースはこれのClientHelloにチケットが追加される
         ClientHello
        (empty SessionTicket extension)-------->
                                                         ServerHello
                                     (empty SessionTicket extension)
                                                        Certificate*
                                                  ServerKeyExchange*
                                                 CertificateRequest*
                                      <--------      ServerHelloDone
         Certificate*
         ClientKeyExchange
         CertificateVerify*
         [ChangeCipherSpec]
         Finished                     -------->
                                                    NewSessionTicket
                                                  [ChangeCipherSpec]
                                      <--------             Finished
         Application Data             <------->     Application Data
  1. サーバがセッションチケットを更新する場合のハンドシェイク
         ClientHello
         (SessionTicket extension)      -------->
                                                          ServerHello
                                      (empty SessionTicket extension)
                                                     NewSessionTicket
                                                   [ChangeCipherSpec]
                                       <--------             Finished
         [ChangeCipherSpec]
         Finished                      -------->
         Application Data              <------->     Application Data
  1. 既存のチケットを使い続ける場合のハンドシェイク
         ClientHello
         (SessionTicket extension)    -------->
                                                         ServerHello // SessionTicket拡張なし
                                                        Certificate*
                                                  ServerKeyExchange*
                                                 CertificateRequest*
                                      <--------      ServerHelloDone
         Certificate*
         ClientKeyExchange
         CertificateVerify*
         [ChangeCipherSpec]
         Finished                     -------->
                                                  [ChangeCipherSpec]
                                      <--------             Finished
         Application Data             <------->     Application Data
  • SessionTicketのTLS拡張は35番
    • 値は
  • チケットには以下のように、プロトコルバージョンや暗号スイート、マスターシークレットなどの情報が含まれている
RFC5077
      struct {
          uint32 ticket_lifetime_hint;
          opaque ticket<0..2^16-1>;
      } NewSessionTicket;

      struct {
          opaque key_name[16];
          opaque iv[16];
          opaque encrypted_state<0..2^16-1>;
          opaque mac[32];
      } ticket;

      struct {
          ProtocolVersion protocol_version;
          CipherSuite cipher_suite;
          CompressionMethod compression_method;
          opaque master_secret[48];
          ClientIdentity client_identity;
          uint32 timestamp;
      } StatePlaintext;

     enum {
         anonymous(0), certificate_based(1), psk(2)
     } ClientAuthenticationType;

      struct {
          ClientAuthenticationType client_authentication_type;
          select (ClientAuthenticationType) {
              case anonymous: struct {};
              case certificate_based:
                  ASN.1Cert certificate_list<0..2^24-1>;
              case psk:
                  opaque psk_identity<0..2^16-1>;   /* from [RFC4279] */
          };
       } ClientIdentity;
KIDANI AkitoKIDANI Akito

8.5.4 複雑なアーキテクチャ

●分散セッションキャッシュ

p.264 分散したクラスタ上でサイトが動作する場合、セッションキャッシュのないサーバに当たる可能性
p.264 対策:セッションスティッキーなLBorセッションキャッシュをクラスタ間で共有
p.264 バックエンドでのセッションキャッシュ同期は平文プロトコルが使われがち:攻撃者が侵入するとマスターシークレット漏洩の危険性

●セッションキャッシュの共有

p.264 無関係なアプリ間でセッションキャッシュ共有すると攻撃対象が拡大する:避けるべき
p.264 セッションチケットを使う場合は別々のチケット鍵を使うべき

●SSLオフロードとリバースプロキシ

p.264 プロキシからアプリへのトラフィックは暗号化されていないことが多いためSSLオフロードは危険
p.264 内部ネットワークが安全だとしても、長期的に深刻な攻撃ベクターとなる

●ネットワーク上でのトラフィックインスペクション

p.264 侵入検知ツールやネットワークモニタリングツール:RSA鍵交換の秘密鍵を共有してトラフィック検査
p.264 PFS(前方秘匿性)が台無しになるだけでなく、秘密鍵の漏洩など潜在的な負債となる

●インフラのアウトソーシング

p.265 クラウドなど、第三者へのアウトソーシングは注意が必要
p.265 2014年、CDNにおけるHTTPS通信で証明書検証失敗の事例[1]

脚注
  1. 論文によると、Azure、CloudFlare、CloudFront、Fastly、Akamaiなど20のCDNが調査対象。無効な証明書、失効した証明書などがチェックされていないケースがあったとのこと。 ↩︎

KIDANI AkitoKIDANI Akito

8.6 セキュリティ上の問題への緩和策

p.265 既知のプロトコルの問題への対処法

8.6.1 Renegotiation

p.265 安全でない再ネゴシエーション:パッチを当てれば十分
p.265 クライアント証明書を使っていなければ安全でない再ネゴシエーションを無効にできる
p.265 TLS1.3では(安全な)再ネゴシエーションがサポートされないので問題ない
p.265 攻撃ツールが利用可能:XSS、CSRF、情報漏洩の危険性

8.6.2 BEAST(HTTPに対するもの)

p.265 TLS1.0以前のCBC暗号スイートの攻撃:TLS1.1以降、および新しいクライアントは対策済み
p.266 機密情報(クッキーの断片)が漏洩する可能性

8.6.3 CRIME(HTTPに対するもの)

p.266 TLSおよびSPDY初期バージョンの圧縮の仕組みで情報を取得する
p.266 機密情報(クッキーの断片)が漏洩する可能性
p.266 クライアントはほとんど圧縮に対応していないので実現性は低い

8.6.4 Lucky 13

p.266 CBC暗号スイートに対するタイミング攻撃
p.266 ライブラリが復号処理の時間を一定にすることで対策済み
p.266 ただし、CBC暗号スイートは本質的に脆弱(≒正しい実装が困難):TLS1.2で利用可能なAEADを使うべき

8.6.5 RC4

p.266 2013年に攻撃手法が発見され、以後改良が続けられている
p.266 現実的な攻撃ではないので、本当に必要な場面以外は避けるべき
p.266 2015年のRFC7465で禁止、2016年モダンブラウザが対応打ち切り

8.6.6 TIMEおよびBREACH(HTTPに対するもの)

p.267 CRIMEを拡張したHTTPの圧縮に対する攻撃
p.267 TIMEは概念上のものでツールなし:BREACHは概念実装のコードあり
p.267 BREACHはアプリレイヤが攻撃対象なので対処が面倒

  • 機微なトークンのマスキング
  • リファラ情報をもとにHTTP圧縮を無効化する

8.6.7 Heartbleed

p.268 OpenSSLの脆弱性:メモリ上のデータに対する高度な攻撃手法多数開発
p.268 複数の対策が必要

  • OpenSSLのパッチを適用する
  • 秘密鍵を新しくし、証明書を取得し直し、古い証明書を失効させる
  • セッションチケットを使っている場合はチケット鍵を変更する
  • その他メモリに存在する機微データへの対応:例)パスワード

8.7 HTTP

p.268 SSL/TLS:HTTPをはじめとしてあらゆるコネクション型プロトコルに対応
p.268 Webを安全にするための先進的な機能を以下で紹介(詳細は第10章)

8.7.1 全面的な暗号化の利用

p.269 HTTPでは暗号化は必須でないため暗号化が必要でも未対応になりがち
p.269 部分的な暗号化を安全に利用するのは不可能:例)クッキー

8.7.2 クッキーのセキュリティ

p.269 Secure属性のないHTTPクッキーは危険
p.269 ずさんな仕様によりクッキー挿入の攻撃もありうる:暗号化や完全性検証により対策が必要

8.7.3 バックエンドにおける証明書とホスト名の検証

p.269 ネイティブアプリやモバイルアプリ:証明書を検証せず能動的ネットワーク攻撃を受ける可能性
p.269 対策:利用するTLSライブラリの設定を有効にすべき
p.269 対策:公開鍵ピンニング

8.7.4 HSTS

p.270 Webサイトが強力な暗号化を要求するための標準
p.270 ブラウザに対し、TLSの利用を矯正できるほか、以下の問題を解決

  • 平文(http://)のリンク/ブックマークを利用するユーザ
  • Secure属性のないクッキー
  • HTTPSストリッピング攻撃(5.4参照
  • 混在コンテンツ(http+https)

p.270 不正な証明書の扱いも改善:ユーザが先へ進めなくなる(RFC6797 8.4

8.7.5 CSP

p.270 Content Security Policy:HTML内のリソースの取り扱いを制御する仕組み
p.270 元々はXSSに対処するために設計
p.270 平文のリンクを拒否しサードパーティによる混在コンテンツを防止できる

8.7.6 (公開鍵)ピンニング

p.270 CAは任意のドメイン名に対して証明書を発行できる:公開鍵ピンニング(HPKP)で対応可能[1]

脚注
  1. ただし、あまり使われていなかったらしく、2015年のRFC化ののち、2018年にChromeが対応をやめている(参考)。最近ではCAA DNSレコードで対応可能。CTログを利用してブラウザに証明書をチェックさせるExpect-CTヘッダによる対策もある ↩︎

KIDANI AkitoKIDANI Akito

第9章 パフォーマンス最適化

p.271 TLSが遅いという悪評は過去のもの:GoogleのエンジニアAdam Langley氏の発表によると、2010年時点でGmailのフロントエンドマシンでSSL/TLSが占めるのはごくわずか[1]

  • CPU負荷の1%以下
  • 1接続あたりのメモリは10Kバイト以下
  • ネットワークオーバーヘッドのうち2%以下

p.271 レイテンシを低下させ、必要なセキュリティを少ないCPUパワーで実現する方法を見ていく
p.272 Webアプリケーションのパフォーマンス全般について:『ハイパフォーマンス ブラウザネットワーキング』(https://www.oreilly.co.jp/books/9784873116761/)(日本語版2014年)[2]

9.1 遅延と接続の管理

p.272 ネットワーク速度の決定要因=帯域とレイテンシ
p.272 帯域:単位時間あたりのデータ量、お金で解決できる
p.272 レイテンシ:ネットワーク伝送速度の限界に由来。RTT(Rount-Trip Time)により計測
p.272 TCPコネクション:3ウェイハンドシェイク(SYN, SYN ACK, ACK)で開始。ハンドシェイク完了に1.5RTTかかる
p.272 TLSやHTTP:ACKの直後にデータ送信開始するため、実際には1RTTで開始できる
p.272 TLSの手の込んだハンドシェイク:追加で2RTT必要(ClientHello->ServerHello, Finished->Finished)[3]

9.1.1 TCPの最適化

p.273 TCPに関する微調整のポイント2つ(輻輳制御関連)
p.273 TCP接続は相手の速度が分からないので速度上限(輻輳ウィンドウ)があり、徐々に拡大するスロースタートの仕組み
p.273 短命なHTTP接続はTCP上で最適でない速度で運用されがち
p.273 TLSも輻輳ウィンドウが狭い時に長いハンドシェイクでRTTを消費しがち

■輻輳ウィンドウの初期値のチューニング

p.274 輻輳ウィンドウの初期値(initcwnd)[4]:2013年4月のRFC 6928で10セグメント(およそ15Kバイト)に拡張(それまでは2-4セグメント)

■アイドル後のスロースタートを抑止する

p.274 一定期間(例:1秒間)にトラフィックがないとスロースタートが作動し速度低下
p.274 Linuxではスロースタートを無効化できる

# sysctl -w net.ipv4.tcp_slow_start_after_idle=0

9.1.2 接続の持続性

p.274 TLSのパフォーマンス問題は主にハンドシェイク:各接続を長期間持続させることで最適化できる
p.274 TCPコネクションも持続するのでスロースタート問題もなくなる
p.274 HTTP/1.0で実験的機能としてkeep-alivesが追加:1.1でデフォルト有効に
p.274 (複数の)接続を長期間開きっぱなしにする弊害:元々Apacheではworkerが接続を管理、低速なクライアントがworkerを使い果たす問題、DoS攻撃
p.275 Nginxのようなイベント駆動型のWebサーバが主流に:Apacheも追随
p.275 長命HTTP接続のデメリット:サーバの待ち時間(タイムアウト)によりリソース消費
p.275 キープアライブの推奨値を決めるのは難しい:ユーザーエージェントにより異なる

  • IE11@Win7:30秒
  • Safari7:60秒
  • Chrome35:300秒
  • Firefox:115秒

9.1.3 SPDYとHTTP/2

p.275 Googleがさらなる改善を目指して、2009年SPDYの実験開始:TCPとHTTPの間に新しいレイヤを導入
p.276 SPDY:単一のTCPコネクション上でHTTPのリクエストとレスポンスを多重化
p.276 HTTP/2の設計につながり、2015年標準化(RFC 7540
p.276 さらなる改善(1):RFC 7431 TCP Fast Openによりハンドシェイクから1RTT削減
p.276 さらなる改善(2):TCPをバイパスし、UDP上でQUICを実現[5]

9.1.4 CDN (Content Delivery Network)

p.276 全世界を相手にする場合、CDNでエッジ部分のキャッシュとトラフィック最適化必要
p.276 WAN最適化とも
p.276 ネットワーク遅延はユーザとサーバの距離が重要
p.276 HTTPS=TCP(1RTT)+TLS(2RTT)

●エッジにおけるキャッシング

p.277 2通り:リバースプロキシで短期間キャッシュorCDNへ直接プッシュ(管理が面倒)

●接続の管理

p.277 コンテンツが動的/ユーザーごとに異なる場合はエッジでキャッシュが難しい
p.277 優れたCDN:長期間接続を再利用することで接続コスト節約可能
p.277 CDN自社ネットワーク:独自のプロトコルや接続の多重化も容易
p.277 ユーザー <-> エッジノード <-(ここを再利用)-> 最終目的地
p.277 全てのCDNが洗練された内部ネットワークを運用しているわけではない
p.278 Ilya Grigorik氏のサイトでCDNのTLSパフォーマンスまとめあり

脚注
  1. ブログの続きを読むと、(RC4の脆弱性が発見される前なので)「RC4はストリーム暗号だからパディングも必要なくて速い、Googleでも優先的に使っている」と書かれていた。他にも、OpenSSLが1接続あたり50Kバイトのメモリを必要としていたのを、5Kバイトにまで改善したりしているらしい。すごい。他にも、今は使われなくなったNext Protocol Negotiationの話も書いてあった。 ↩︎

  2. 英語版は無料で読むことができる:https://hpbn.co/ ↩︎

  3. 先のAdam Langley氏のブログに図がある:https://www.imperialviolet.org/2010/06/25/overclocking-ssl.html ↩︎

  4. initial congestion window ↩︎

  5. QUICは2021年に標準化された(RFC9000)。つい先日QUICをベースにしたHTTP/3が標準化された(RFC 9114↩︎

KIDANI AkitoKIDANI Akito

9.2 TLSプロトコルの最適化

p.278 TLSのさまざまな側面がどのようにパフォーマンスに影響するか見ていく

9.2.1 鍵交換

p.278 レイテンシに次ぐ大きなコストが鍵交換と呼ばれる暗号処理(CPU高負荷)
p.278 鍵交換のコスト=鍵長×秘密鍵アルゴリズム×鍵交換アルゴリズム

●鍵長

p.278 鍵が長いほど強力:暗号化と復号にも時間がかかる
p.278 適切だが過剰ではない鍵長を選ぶのが重要

●秘密鍵アルゴリズム

p.278 現在利用可能なアルゴリズム:RSAとECDSA[1][2]
p.279 RSAは処理速度の面で劣る:256ビットのECDSAと3072ビットのRSAはセキュリティ的に等価だがECDSAが高パフォーマンス[3]

●鍵交換アルゴリズム

p.279 選択肢は3つあるが実質一択:ECDHE、PFSのないRSA、遅すぎるDHE
p.279 ECDHEのデファクトスタンダートはsecp256r1という曲線(128ビット安全性)

p.279 秘密鍵と鍵交換の組み合わせはプロトコルで指定された以下の4つ

  • RSA(秘密鍵も鍵交換もRSA)
  • DHE_RSA
  • ECDHE_RSA
  • ECDHE_ECDSA[4]

p.280 OpenSSLによるパフォーマンステストの結果

  • RSA->ECDHE_ECDSA:PFSが得られ、処理時間が半分
  • ECDHE_ECDSA:クライアントの処理時間は増えるがサーバは最速
  • RSA->ECDHE_RSA:処理時間が若干伸びるがPFSが得られる
  • DHE鍵交換:1024ビットは低速、2048ビットはさらに遅い
False Start

p.281 Googleが2010年に提案:ハンドシェイク完了を待たずにアプリケーションデータを送信し、RTTを減らす
p.281 メリット:レイテンシが30%改善、PFSが得られる、
p.281 デメリット:ハンドシェイクの完全性が検証されず攻撃を受ける
p.281 Googleの対策:強い暗号に限定

  • 強い秘密鍵
  • PFSのある鍵交換アルゴリズム
  • 128ビット安全性の暗号スイート

p.281 抜け道:強い鍵交換が要求されない => 2015年Logjam攻撃(詳細は6.5節
p.281 2012年、GoogleはFalse Startを失敗と判断(互換性のないサーバが多かったため):しかし無効化せず
p.281 他のブラウザも対応:Firefox v28(2014年)、Mac OS X 10.9(2013年)、IE 10(2012年)[5]

9.2.2 証明書

p.282 証明書チェーンの長さと正しさがパフォーマンスに影響

●できるだけ少ない証明書を使う

p.282 証明書チェーンが増える->ハンドシェイクデータが増える->輻輳ウィンドウ初期値を超える
p.282 SSL初期はルート証明書から直接サーバ証明書を発行するCAもあったが現在では廃止
p.282 現在のベストな証明書チェーン:サーバ証明書と中間証明書の2つ[6]
p.282 CAによってはチェーンが長くなるので要確認

●必要な証明書だけが含まれている

p.282 証明書1通:ハンドシェイクが1~2Kバイト増加
p.282 意味もなくルート証明書をチェーンに含めていないか:ユーザーエージェントは通常ルート証明書のコピーを保持している
p.282 ルート証明書のサーバへのインストールをCAが指示するケースもあるためよく発生する問題

●完全な証明書チェーンを提示する

p.283 証明書が欠けている場合、ユーザーエージェントがHTTPで探索:時間がかかる

●楕円曲線の証明書チェーンを使う

p.283 ECDSA鍵はビット数が少なく、2048ビットRSA証明書より1Kバイトほどサイズが小さい

●同一の証明書におけるホスト名の数が多すぎないようにする

p.283 SNIに対応していないクライアントのために複数のホスト名を追加する
p.283 数百個のサイトで共有するケースも:証明書のサイズに影響
p.283 SNI対応・非対応クライアントで証明書を分けることも可能

クライアント証明書

p.283 どのCAを発行元として許可するか通知する設定も可能:多すぎると数キロバイトに
p.283 必須ではないので省略可

9.2.3 失効の確認

p.283 サーバ運用者の仕事:失効情報を高速に配布すること

●OCSP情報が含まれている証明書を使う

p.284 OCSPは目的のサイトについてのみ失効情報を取得できるので高速
p.284 CRLは複数の失効情報を含む:モバイル回線などでは問題

●高速で信頼できるOCSPレスポンダを備えたCAを使う

p.284 低速なOCSPレスポンダのCAも存在
p.284 失効情報の配布にCDNを使っているCAがパフォーマンスが良い
p.284 失効情報の更新タイミングも重要:特に発行直後

●OCSPステープリングを導入する

p.284 OCSPステープリング:OCSPレスポンスをTLSハンドシェイクに含めるTLS拡張
p.284 ハンドシェイクが450バイトほど増加するが、別の接続でOCSPレスポンダにアクセスする必要がなくなる
p.284 OCSPレスポンスのサイズ:署名する証明書が同じCAかどうかで決まる

脚注
  1. 既にみた通り、DSAもあるが鍵長が1024ビットのため安全ではない。 ↩︎

  2. 最近は2011年に開発されたEdDSA(エドワーズ曲線デジタル署名アルゴリズム)も利用可能。OpenSSLでは2018年リリースのv1.1.1から利用可能Javaでは2020年にリリースされたv16から利用可能↩︎

  3. SSL Pulse2022.06の結果によると、7.9%が3072ビット、6.8%が4096ビットの鍵を利用している。80%以上が2048ビット。 ↩︎

  4. 秘密鍵がEdDSAの場合もここに含まれる:参考 https://datatracker.ietf.org/doc/html/rfc8422 ↩︎

  5. その後2016年、RFC7918となる。 ↩︎

  6. TLS1.3では中間証明書を送らないことでハンドシェイクの高速化をはかる仕様が提案されている。参考 ↩︎

KIDANI AkitoKIDANI Akito

9.2.4 セッションリザンプション

p.285 セッションリザンプションは高速:暗号処理が不要で、ラウンドトリップも1回少ない(ClientHello、ServerHelloでFinished+Application Data)
p.285 サーバ側でセッションキャッシュの設定が必要:利用するかどうかはクライアント次第
p.285 GoogleのAdam Langley氏によると、セッションの重要な情報が含まれているためにセッションはディスクにキャッシュされずメモリ上にキャッシュされるため、Googleでもリザンプションの割合はわずか50%程度

9.2.5 転送のオーバーヘッド

p.285 TLSの最小転送単位=TLSレコード(上限16,384バイト)
p.285 先頭5バイトはヘッダ:コンテントタイプ(1バイト)+バージョン(2バイト)+レコード長(2バイト)
p.285 暗号化とデータ完全性のための処理によるオーバヘッドは暗号スイートにより異なる
p.285 ストリーム暗号(ヘッダ+Enc(平文+MAC)):入力1バイトにつき出力1バイト、オーバヘッド少
p.285 ブロック暗号(ヘッダ+IV+Enc(平文+MAC+パディング)):パディングとIV(ブロック長と同じ)によるオーバヘッド有、平均的なパディングはブロック長(多くは16バイト)の半分
p.285 AEAD(認証付暗号)(ヘッダ+ナンス+Enc(平文)):8バイトのナンス
p.286 広く利用されているものの中でAES-128-CBC-SHA256のオーバヘッドが最大:61/16384バイト(0.37%)
p.286 ベストはAES-128-GCM-SHA256:29/16384バイト(0.18%)
p.286 いずれもTCPのパケットあたりのオーバヘッドの方がはるかに大きい

  • IPv4:52/1500バイト前後(3.5%)
  • IPv6:72/1500バイト前後(4.8%)

p.286 小さなデータの送信をできるだけ避け、少量のデータを結合し大きな塊にして送信すべし
p.286 ネットワーク層のトラフィックをキャプチャし、パケットとTLSレコードの大きさを調べよう

9.2.6 共通鍵暗号化方式

p.287 TLSハンドシェイク終了後もCPUコストがかからないわけではない
p.287 3DES、SEED、CAMELLIAなどのアルゴリズムはやはり遅い
p.288 AES+SHA256だと遅くなる(SHA256がSHAより遅いので
p.288 CBCよりGCMの方が高速(パディングとMACが不要なので?)
p.288 RC4は高速だが安全ではないので避けるべき

p.288 Googleが2014年にChaCha20-Poly1305の実験を開始し標準化
p.288 ChaCha20-Poly1305はハードウェアアクセラレーションが可能なAESと比べると遅いが、モバイル環境では高速

9.2.7 TLSレコードのバッファに伴う遅延

p.288 アプリケーションデータ32KB=TLSレコード16KB+16KB=TCPパケットたくさん(1.5KBずつ)
p.289 復号や完全性検証の単位はTLSレコードになる:TCPパケットの一部が先に届いても処理ができない
p.289 パケットの喪失と遅延:TCPにより回復可能だがラウンドトリップ時間がかかる
p.289 輻輳ウィンドウ初期値:接続初期に大きなデータを送るとラウンドトリップ時間が増加
p.289 WebサーバによってはTLSレコードサイズを調整可能:適当にやるなら4KB(デフォルトであろう16KBは大きすぎる)、パケットに合わせるなら1400バイト前後
p.289 固定的なTLSレコードは問題がある

  • IPのMTUの変更や暗号スイートの変更の影響を受ける
  • 短すぎると転送のオーバヘッドが増える

p.290 TLSレコード長の調整はHAProxyなどに任せるのが良い(接続中に値を変えてくれる)

KIDANI AkitoKIDANI Akito

9.2.8 相互運用性

p.290 クライアントだけがプロトコルの新しい機能に対応していると、再接続などでパフォーマンスが低下
p.290 最新バージョンのプロトコルと拡張に対応したTLSスタックを使うべし

9.2.9 ハードウェアアクセラレーション

p.290 SSL黎明期の公開鍵暗号のパフォーマンスは最低:ハードウェアアクセラレーション必須
p.290 汎用CPU性能が向上し2000年ごろから不要に:ソフトウェアベースのTLS実装が十分に高速
p.290 秘密鍵の隔離のためにハードウェア暗号デバイス(Hardware Security Module)利用することはある

9.3 DoS攻撃

p.290 攻撃は安上がりだが防御にはコストと時間がかかる
p.291 主な手段ボットネット:乗っ取られたコンピュータを大規模に接続したネットワーク
p.291 数の多い個人コンピュータ、帯域の広いサーバがネットワークのノードとなる
p.291 TLSのレベルでのボットネット対策:TLS1.3拡張として提案(2015年)された(がどうも実らなかった模様)
p.291 一般的にはネットワークレベルで防御を行う

  • 接続のスロットリング(通信数制限):少数のIPアドレスからの単純な攻撃には効果あり
  • 過剰な設備投資
  • サードパーティを利用した対策:DDoS攻撃対策専門の企業に依頼

p.291 TLSのチューニングは全く無意味ではない:DoS攻撃を招く側面もある

9.3.1 鍵交換と暗号化に対するCPUコスト

p.292 ハンドシェイクにおけるクライアントのコスト<サーバのコスト=DoS攻撃に悪用できる設定
p.292 RSAが良い例:クライアントが実行する公開鍵の処理の方が、サーバの秘密鍵の処理より高速
p.292 平均的な2048ビットのRSA鍵でサーバの方が4倍処理が必要
p.292 筆者の実験:同じコンピュータ2台をサーバ、クライアントとして利用しベンチマークツールabを実行、クライアントCPU10%でサーバを圧迫可能
p.292 ECDHE_RSAの場合サーバが2.5倍の処理、ECDHE_ECDSAでは逆転しクライアントが1.5倍の処理が必要
p.292 RSA鍵交換のサポートを切ることで、PFSが得られるだけでなくパフォーマンスも良くなる

9.3.2 クライアント起源の再ネゴシエーション

p.293 クライアント起源の再ネゴシエーションは実利が少ない、かつDoS攻撃対策を困難にする
p.293 再ネゴシエーションをクライアントが利用すると、TCP接続1つで複数のTLS接続ができる:実際 の攻撃ツールとしてthc-ssl-dos
p.293 クライアント起源の再ネゴシエーションに対応していないサーバが多い:Apache 2.2.5〜、Nginx全バージョン、IISバージョン6〜
p.293 理想的にはクライアント起源の再ネゴシエーションを許可すべきでない

9.3.3 TLSに対するDoS攻撃の最適化

p.293 クライアントはハンドシェイクメッセージをハードコードして暗号処理を省略できる
p.293 サーバは全て有効なハンドシェイクとして扱ってしまう
p.294 概念実証のためのツールsslsqueeze:abよりはるかに効率的にサーバリソースを消費できる[1]

脚注
  1. sslsqueezeに関するブログでは、再ネゴシエーションが無効であっても、新たなTCPコネクションを開いて攻撃が可能とされている。 ↩︎

KIDANI AkitoKIDANI Akito

10章 HSTS、CSP、ピンニング

p.295 PKIエコシステムのセキュリティを改善する技術について
p.295 HTTP限定:HSTSとCSP
p.295 ピンニング:競合する手法が複数、ネイティブアプリでは実用的。ブラウザの場合ベンダーの協力も必要

10.1 HSTS(HTTP Strict Transport Security)

p.295 2012年11月にRFC6797として公開
p.295 モダンブラウザにおける下記の弱点に対処(詳細は第5章でも説明済み)

  • サイトがTLSに対応しているかどうか知るすべがない[1]:最近ではデフォルトがHTTPではなくHTTPSになっているのでこの点はあまり問題ないか?[2]
  • 証明書の問題に寛容:ブラウザは警告を出すだけ、ユーザーは警告を無視しがち
  • 混在コンテンツの問題:ブラウザで能動的混在コンテンツはブロックされるが、フィッシングなどの危険性が残る
  • クッキーのセキュリティ問題:Secure属性忘れ、能動的なネットワーク攻撃者に窃取される

p.296 HSTSによる解決

  • httpが透過的にhttpsに書き換えられる
  • 証明書エラーがfatalとなりユーザーが無視できなくなる

10.1.1 HSTSの設定

p.296 暗号化された通信でのHTTPレスポンスヘッダで指定

// 31536000秒=365日
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

p.297 平文による接続の際にはこのヘッダは無視される:DoS攻撃に利用できてしまうため
p.297 取り消す場合max-age=0:ただしユーザーが再訪した場合のみ有効

10.1.2 ホスト名が網羅されているか確認する

p.297 デフォルトではHSTSはレスポンスヘッダを発行したホストのみ有効
p.297 複数のサブドメインで運用している場合全てでHSTSを有効化する必要がある
p.298 ホスト名一つの場合でもwwwありなしでのアクセスがあるため要注意
p.298 リダイレクトも同様に設定:example.com:80->example.com:443->www.example.com:443(+HSTS)

10.1.3 クッキーの安全性

p.298 クッキーの仕様が緩いため、HSTSが有効でも以下のような攻撃が可能

  • ホスト名詐称:能動的なネットワーク攻撃者がサイトのサブドメイン(例:madeup.www.exmaple.com)にアクセスさせることでクッキー窃取
  • クッキー注入:Secure属性のありなしが区別されないため、Secure属性なしのクッキーで上書き可能(同様にホスト名を詐称するなど)

p.299 上記の問題はincludeSubDomainsパラメータで概ね解決

脚注
  1. DNSのSRVレコードで実装可能(RFC2782)。ただし、パラメータの拡張性がないなどの問題があるため、2018年にはDNSでALTSRVレコードというドラフトが提案された。 ↩︎

  2. Google Chromeは2021年のv90からhttpsをデフォルトにしている。参考:chromiumのブログ ↩︎

KIDANI AkitoKIDANI Akito

10.1.4 HSTSに対する攻撃

最初のアクセス時

p.299 HSTSはレスポンスヘッダで設定:初回アクセスはセキュアでない
p.299 ブラウザ側でHSTS対応サイト一覧を持っていれば対処可能(ほんとか?)

短い保持期間

p.299 6ヶ月以上HSTSが保持される場合に有効:次回アクセス時も保護される
p.299 ユーザーの再訪がなければ次回アクセスが保護されない点に注意

時計攻撃

p.299 NTP経由の攻撃:未来の時刻に設定するネットワーク攻撃でHSTSポリシーを無効化できる
p.299 一般的にNTPへのアクセスは1-2回/日:Fedoraなどは毎秒、OS Xは数分ごとに同期するため攻撃が容易
p.300 Windowsは週1回、かつ大きな時刻変更への防御機能あり

レスポンスヘッダの注入

p.300 脆弱なWebアプリケーションでは、偽のStrict-Transport-Securityヘッダを挿入される:二重になる場合先勝ち
p.300 HSTSを利用していないアプリに対して、HSTSを有効にして潜在的DoS攻撃も可能[1]

TLS強制切断

p.300 プロトコルは問題ないが、ブラウザの実装が強制切断に脆弱
p.300 max-ageパラメータの最初の数字の直後で接続を強制切断できる(クッキーカッター攻撃の一種)

混在コンテンツの問題

p.300 HSTS設計者は混在コンテンツの問題を完全には解決せず
p.300 サードパーティの混在コンテンツ問題にはCSPの導入が必要(10.2節参照)

ホスト名とポートの共有

p.300 HSTSはホスト名全体で全てのポートに対して有効になる
p.300 ポートによって管理者が異なる場合、HSTSの利用に注意が必要

10.1.5 ブラウザのサポート状況

p.301 2010年にChromeが、2011年にFirefoxがHSTSサポート
p.301 2013年にSafari、2015年にIE11がHSTSサポート
p.301 多くのブラウザがHSTS対応サイト一覧を保持:初回接続時も安全
p.301 GoogleがHSTS対応サイト一覧を取得するサービスを開始、他のベンダもこれを利用:ベンダ側の更新頻度などは不明

10.1.6 デプロイのためのチェックリスト

p.301 複雑な運用環境では、保持期間を短くして試験実行すべし:例) 300秒
p.301 全サブドメインが影響を受けるのでドメイン一覧を用意し同意を取り付ける
p.302 全サブドメイン、ルートドメイン、リダイレクトでHSTSヘッダが発行されるか確認
p.302 HTTPトラフィックをHTTPSにリダイレクトしHSTSを確実にする
p.302 サブドメイン訪問時にルートドメインへのリクエストが発行されるようにして、他サブドメインも保護する
p.302 リバースプロキシで一元管理:バックエンドのWebサーバがヘッダ注入に脆弱な可能性

p.302 正しい運用が確認できたら、保持期間を12ヶ月に伸ばし、preloadキーワードを指定する
p.302 https://hstspreload.org/ の指示に従うことでモダンブラウザでプリロード可能に

10.1.7 プライバシーに起因する問題

p.303 HSTSヘッダの情報はブラウザで永続的に保管される
p.303 HTTPのリンクを踏ませることで、過去の一定期間に訪問済みかどうか分かる
p.303 注にあるLeviathan Security Groupの記事は削除されていたが、別のサイトで内容を確認できた:ワイルドカード証明書を使ってサブドメインごとに1ビットの情報をHSTSヘッダ経由でブラウザに保管させ、別のサイト上でJavaScriptを実行し問題のサブドメインへのHTTPリクエストを発行することでブラウザに保管されている1ビットの情報を取得できる、というもの(広告事業などに利用可能)

10.2 CSP(Content Security Policy)

p.303 コンテンツのダウンロード元を指定:Webサイトの攻撃対象領域を狭める
p.303 主にXSS対策:インラインのJavaScriptを完全無効化、外部コード読み込みを制御、動的コード評価を無効化
p.303 Mozillaが最初にContents Restrictionとして導入、2012年CSPとして標準化、その後も進化[2]

Content-Security-Policy: default-src 'self'; img-src *; object-src *.cdn.example.com; script-src scripts.example.com

p.304 HSTSと異なり、CSPは参照されたページに1回のみ適用される:レスポンスヘッダ注入による永続的なDoS攻撃の危険がない

10.2.1 混在コンテンツの問題を避ける

p.304 全てのブラウザで受動的混在コンテンツが許可されている(画像など)
p.304 block-all-mixed-content:混在コンテンツを全てブロックするディレクティブ
p.304 upgrade-insecure-requests:安全でないリンクをHTTPSに更新するディレクティブ

10.2.2 ポリシーのテスト

p.305 report-only:移行期間中に利用するヘッダ(レポート自体はCSPヘッダでも受け取り可能)

Content-Security-Policy-Report-Only: default-src https:; report-uri /csp-violation-report-endpoint/

p.305 他のポリシーと並列にテストしつつ強制できる

10.2.3 レポート

p.305 report-uriディレクティブでレポート先を指定して有効化
p.305 POSTメソッドのリクエストボディにレポートが格納されてくる

{
  "csp-report": {
    "document-uri": "http://example.com/signup.html",
    "referrer": "",
    "blocked-uri": "http://example.com/css/style.css",
    "violated-directive": "style-src cdn.example.com",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports",
    "disposition": "report"
  }
}
脚注
  1. HSTSのRFC6797に説明がある:HTTPしか提供していないWebアプリに対して、HSTSヘッダを設定すると、ブラウザがHTTPSでのみアクセスするようになり、サービスが利用できなくなる。 ↩︎

  2. MDNによると、HTMLのメタタグでも設定可能(IE11を除く) ↩︎

KIDANI AkitoKIDANI Akito

10.3 ピンニング

p.306 ピンニングによる改善ポイントは主に3点:攻撃対象領域削減、鍵継続管理、認証
p.306 パブリックCAを利用する際に、ドメイン所有者がCAを指定(ピン留め)することで、その他のCAが証明書を発行するリスクを低減できる
p.306 鍵継続管理:鍵と接続先をセットで覚えておく手法
p.306 SSHでよく使われている:初回接続時に関連付けを記録(TOFU:trust on first use)
p.306 Firefoxで検証できない証明書についても鍵継続管理を利用
p.306 安全な通信路(DNS)がある場合、サーバ証明書のフィンガープリントを保持し認証に利用可能

10.3.1 何をピン留めするか

p.307 証明書と公開鍵がピン留め候補:鍵を変えずに証明書を再発行するケースもあるため、公開鍵ピンニングが実践的(複数の証明書が紐づくケースもあるため)
p.307 TLSの場合、X.509証明書のSPKIフィールド(SubjectPublicKeyInfo)が最適:公開鍵+メタデータ

SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm                   AlgorithmIdentifier,
    subjectPublicKey      BIT STRING }

p.307 SPKIフィールドに関するOpenSSLのコマンド例

// SPKIフィールドを確認する
$ openssl x509 -in server.crt -noout -text

// SPKIのハッシュ生成のためにファイルに抜き出す
$ openssl x509 -in server.crt -noout -pubkey | \
    openssl asn1parse -inform PEM -noout -out server.spki

// SHA256ハッシュ生成してbase64で符号化
$ openssl dgst -sha256 -binary server.spki | base64

10.3.2 どこにピン留めするか

p.308 サーバの公開鍵をピンニングする欠点

  • サーバは攻撃されやすい
  • 秘密鍵危殆化時や定期交換時に古いピンニングが無効化する
  • 複数の鍵と証明書を同一サイトで利用しづらい

p.308 よって、証明書チェーンの上流、中間証明書かルート証明書をピンニングすると良い
p.308 古いクライアント向けに他のCAと相互認証している中間証明書もある:ルート証明書をピンニングすると検証失敗する可能性
p.308 将来的に別の中間CAから証明書が発行されるリスクもある:CAのサポートが必要
p.309 中間証明書の変更に備えて、予備のピンニングと証明書を用意すべし
p.309 コストはかかるが、自身で中間CAを運用するのが効果的ではある

10.3.3 ピンニングを使うべきか

p.309 ピンニングの代償:識別子を持っていない相手とは通信できない
p.309 識別子を紛失した場合自らDoS攻撃を招く
p.309 ブラウザベンダの推奨ピンニング期間は数日単位:HSTS保持期間の推奨が1年なのと対照的
p.309 不正に発行された証明書による攻撃が懸念される大手サイトであればピンニングしか対策がない

10.3.4 ネイティブアプリのピンニング

p.309 ネイティブアプリは通信の両端を自分達が制御できる
p.309 プライベートなバックエンド(自分達のアプリだけが使う):自分達で証明書発行してピンニング可能
p.310 例)Javaで独自のトラストストアを生成して、自分のルート証明書だけを含める
p.310 参考:Moxie Marlinspikeのブログ記事:注に掲載されたURLから変更されているが、GitHubに公開されているPinningTrustManager.javaを使うとSPKIピンニングが実現できそう
p.310 Android 4.2以降:限定的に公開鍵ピンニングに対応

10.3.5 Chromeの公開鍵ピンニング

p.310 Chrome 12から実験的に導入(2011年):ピンニングの設定用UI
p.310 Chrome 13ではGoogleのサイトへのピンニングに対応
p.310 HSTSプリロードとピンニングは同じ仕組みを利用(ブラウザにハードコードされた情報を利用)
p.311 Chromeの公開鍵ホワイトリスト=ピンセット。ブラックリストも対応可能
p.311 手動でインストールされたルート証明書はピンニングのチェック対象外:マルウェアによるMITM攻撃の問題がある
p.311 Chromeはピンニング検証失敗をレポートする仕組みがある:DigiNotar事件やTURKTRUST事件などで表出(4章を参照)
p.312 Firefoxもデスクトップ版(v32)、Android版(v34)で対応

10.3.6 Microsoft社のエンタープライズ証明書ピンニング

p.312 2017年Windows10でEnterprise Certificate Pinningが有効に
p.312 管理者が独自にピンニングポリシーを指定可能(Windows 10ユーザーしか保護できないが...)

10.3.7 HPKP

※HPKPはCertificate Transparencyに置き換えられたため非推奨(参考:MDN

p.312 HPKPとHSTSの共通点

  • HTTPレスポンスヘッダを使って設定される(Public-Key-PInsとStrict-Transport-Security)
  • max-ageで有効期限を指定
  • includeSubDomainsパラメータでサブドメインへ拡張
Public-Key-Pins: max-age=2592000;
    pin-sha256="....(SPKIフィンガープリントをBASE64エンコードしたもの)...";
    pin-sha256="....(SPKIフィンガープリントをBASE64エンコードしたもの)..."

p.313 ピンニング対象は2つ必須:バックアップ用として証明書チェーンに存在しないものが必須
p.313 HPKPのレポート機能はCSP風:証明書チェーンがレポートされる
p.314 Public-Key-Pins-Report-Onlyヘッダで設定確認が可能:攻撃されていることがわかるだけで、攻撃を避けるのと同じくらい有用ではある

10.3.8 DANE

p.314 DANE: DNS-Based Authentication of Names Entities(RFC 6698RFC 7218
p.314 ドメイン名とドメインに対して発行された証明書の紐付けをDNSで行う
p.314 DANEのセキュリティはDNSSEC(Domain Name System Security Extensions)によるDNSの完全性機能が前提[1]
p.314 DNSSECは10年以上開発が続いているが、導入はあまり進んでいない(らしい

■DANEのユースケース

p.314 一般的なTLS認証

  • 本物のドメイン所有者にのみ証明書を発行する信頼できるCA
  • 証明書が正しいことをブラウザなどが確認する

p.315 このモデルの前提:DNSで提供される情報が信頼できない
p.315 DNSSECが有効であれば上記モデルが崩れ、CAが必ずしも必要でなくなる:以下のような利用法が考えられる

  • 自己署名証明書の安全なデプロイ:DNSでピンニングすれば、MITM攻撃者と区別できる
  • プライベートなルート証明書の安全なデプロイ:上記の例の拡張、多数のサイトを所有する場合有益
  • 証明書と公開鍵のピンニング:サイトに対する証明書発行許可と同義
■実装

p.315 TLSA Resource Recordで証明書の関連付けを配信:4つのフィールドを組み合わせる
p.315 Certificate Usage:ピンニングすべき証明書のチェーン内位置、検証方法を指定(後述)。
p.315 Selector:ピンニングする要素を指定。0=証明書、1=SPKIフィールド
p.315 Matching Type:完全一致かハッシュか。0=完全一致、1=SHA2-256、2=SHA2-512
p.316 Certificate Association Data:関連付けに使う生のデータを含める
p.316 Certificate Usageの詳細

  • 0=CA制約(PKIX-TA):CAに対するピンニング。PKIX[2]で検証。
  • 1=サービス証明書制約(PKIX-EE):エンドエンティティのピンニング。通常PKIXで検証。
  • 2=証明書利用者の表明(DANE-TA):CA証明書(ルートor中間)をトラストアンカーに指定し、ユーザーエージェントが検証に利用する。
  • 3=ドメイン発行証明書(DANE-EE):エンドエンティティのピンニング。PKIXによる検証はせず、ピンニングされた証明書が信頼される。
■デプロイ

p.315 DNSSECの設定が複雑だが、DANE自体はTLSAレコードを追加するだけで対応可能
p.316 ポート(443とか25とか)+プロトコル(UDPorTCPorSCTP[3]輻輳制御などはTCPと同様。TCPはバイト (byte) 指向、SCTPはフレーム・メッセージのやり取り(UDPに近い)。)+完全修飾ドメイン名

_443._tcp.www.example.com. IN TLSA (
             0 1 1 ....(16進数にエンコードされたSHA256ハッシュ)... )
// Certificate Usage=0
// Selector=1
// Matching Type=1

p.317 複数のTLSAレコードを1つのドメイン名に設定可能:バックアップとして利用し、ダウンタイムなしでの入れ替えも可能
p.317 DNSレコードのTTLを指定することで保持期間を指定できる
p.317 TLSAレコードが見つからない場合のユーザー側の対応がRFCでは規定されていない(HPKPではユーザーがピンニングを無効にできるように、と指示がある)
p.317 レポートの仕組みもないため、失敗時の調査が難しい

■アプリケーションにおける対応

p.317 主要なブラウザでサポートされていない(本書執筆時点[4]):OSでDNSSEC対応が必要なため
p.318 Chromeで実験的にサポートされたがあまり利用されず打ち切り
p.318 VeriSignのDANEデモサイトは2022年現在は使えない模様

10.3.9 TACK (Trust Assertions for Certificate Keys)

p.318 パブリックCAからもDNSからも独立した公開鍵ピンニングのための提案
p.318 サイト運用者がTSK(TACK署名鍵)を作る:プライベートCAに近い
p.318 TACK対応クライアント:ClientHelloに空のtack拡張を含める
p.318 サーバーは同じ拡張でtackを1つ以上返信:2回目の訪問時に利用される
p.318 tackの保持期間は30日が上限
p.318 HTTPでしか使えないHPKPと違い、どんなプロトコルとも利用可能(その分複雑

10.3.10 CAA

p.318 Certificate Authority Authorization
p.318 ドメイン所有者がCAに証明書発行権限を与えるための仕組み+CAが正当なドメイン所有者とやり取りしているのを確認するための仕組み
p.318 ポリシーの配布にDNSを利用(DNSSEC推奨:DNSスプーフィングの危険性があるため)
p.319 プロパティタグでCAに指示を伝える
p.319 issueプロパティタグ:CAのドメイン名で発行を許可する

// ca.example.comというCAに許可する
certs.example.com  CAA 0 issue "ca.example.com"

// 証明書の発行を禁止する(CAを指定しない)
nocerts.example.com CAA 0 issue ";"

p.319 issuewildプロパティタグ:ワイルドカード証明書用
p.319 iodefプロパティタグ:不正な証明書発行リクエスト時のサイト所有者への連絡方法を指定
p.319 2017年CAB Forumは加盟するCAにCAA必須化[5]
p.319 SSL MateによるCAA Record Generatorが便利:ドメイン名を入力してポリシーを選ぶだけ

脚注
  1. 権威ネームサーバが自身の秘密鍵で署名してレスポンスを返し、DNSキャッシュサーバが権威ネームサーバの公開鍵でレスポンスを検証する。公開鍵を伝達するためのDNSKEYレコード、署名情報を伝達するためのRRSIGレコードなどのDNSレコードが追加されている。参考:インターネット10分講座。DNSSECの署名は1024ビットのRSAを利用しており、2022年には安全ではないとされている(Adam Langley↩︎

  2. IETFのPKIワーキンググループ、またその成果物としての標準。参考 ↩︎

  3. Stream Control Transmission Protocol ↩︎

  4. 2022.07.17時点でも正式サポートはされていなそう。参考 ↩︎

  5. 参考記事 ↩︎

KIDANI AkitoKIDANI Akito

第11章 OpenSSL

p.321 OpenSSL:暗号ライブラリ+SSL/TLSのツールキットのOSS
p.321 1995年SSLeayとして開発、1998年プロジェクト開始
p.321 ブラウザではあまり使われなかったが、ChromeがBoringSSLとしてフォークして採用
p.321 ライセンスがBSD類似でGPL系と互換性がなく、GPLライセンスのプログラムではGnuTLSが使われる

11.1 ことはじめ

p.321 Unix系ならOpenSSLがインストールされている:Windowsはバイナリのダウンロードが必要

11.1.1 OpenSSLのバージョンと設定

p.322 openssl version コマンドでバージョン確認可能:完全なバージョンを表示するには-aオプション必要
p.322 v1.0.1でTLS1.1/1.2に初めて対応
p.323 -aオプション指定時のOPENSSLDIR:OpenSSLの設定と証明書の格納場所、Ubuntuであれば/etc/ssl

11.1.2 OpenSSLのビルド

p.324 OpenSSLにはルート証明書が含まれていない:トラストストアの維持はプロジェクトの対象外

11.1.3 利用可能なコマンドを調べる

p.324 ヘルプコマンドはないが、 openssl help と入力するとヘルプ(標準コマンドのリスト)が表示される

11.1.4 トラストストアの構築

p.325 候補1:OSに組み込まれているトラストストアを使う
p.325 候補2:Mozillaのトラストストアを利用する
p.326 Mozillaの配布形式は独自

  • CurlプロジェクトがPEM形式に変換して配布しているものを使う
  • Perl/Go製のツールで変換処理を行う

11.2 鍵と証明書の管理

11.2.1 鍵の生成

p.327 秘密鍵の生成の前に決めること:鍵アルゴリズム、鍵の長さ、パスフレーズ
p.327 Webサーバの鍵なら、RSAかECDSA。SSH鍵ならDSAかRSA(ECDSA非対応のクライアントもある)
p.327 RSA鍵長はデフォルト512で安全でない:RSAは2048、ECDSAは256が最低。
p.327 パスフレーズ

  • 安全に格納、転送、バックアップするためのもの
  • 本番システム上ではメモリ上にあり、パスフレーズで保護されない

p.328 genrsaコマンドでRSA鍵を生成

$ openssl genrsa -aes128 -out fd.key 2048
...

$ cat fd.key
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,01EC...(省略)

vERm...(省略)
-----END RSA PRIVATE KEY-----

p.329 rsaコマンドを使うと鍵の構造がわかる

$ openssl rsa -text -in fd.key 
(modulusとかprime1,2が出力される)

p.329 公開鍵だけを取り出すときは-puboutオプションを使う

$ openssl rsa -in fd.key -pubout -out fd-public.key
$ cat fd-public.key
-----BEGIN PUBLIC KEY-----
MIIBI...(省略)
-----END PUBLIC KEY-----

p.330 DSA鍵の出力例:DSAパラメータを含む一時ファイルをディスクに残さずに済む

$ openssl dsaparam -genkey 2048 | openssl dsa -out dsa.key -aes128

p.330 ECDSA鍵では名前付き曲線を選ぶ

$ openssl ecparam -genkey -name secp256r1 | openssl ec -out ec.key -aes128
KIDANI AkitoKIDANI Akito

11.2.2 CSRの生成

p.330 CSR:Certificate Signing Request(証明書への署名をCAに依頼)
p.330 証明書を必要とする組織の情報と、公開鍵を含め、対応する秘密鍵でCSRに署名
p.330 openssl req -new -key fd.key -out fd.csr 以降、対話的に生成
p.330 空欄にしたいフィールドの場合は.(ドット)を入力する:エンターキーのみだとデフォルト値に
p.331 challenge password:空欄推奨。証明書失効時に利用されるが、セキュリティ上ほとんど意味なし
p.331 CSR生成後:自分で証明書作成(オレオレ証明書)orCAに署名依頼
p.331 生成したCSRの確認: openssl req -text -in fd.csr -noout

11.2.3 既存の証明書からCSRを生成する

p.332 openssl x509 -x509toreq -in fd.crt -out fd.csr -signkey fd.key
p.332 ただし、公開鍵ピンニングを利用している場合を除き、新しい証明書の申請に新しい公開鍵を作るのがベスト

11.2.4 人手を介さずにCSRを生成する

p.332 opensslの設定ファイルを利用可能:openssl req -new -config fd.cnf -key fd.key -out fd.csr

[req]
prompt = no
distinguished_name = dn
req_extentions = ext
input_password = your_passphrase

[dn]
CN = www.feistyduck.com
emailAddress = webmaster@feistyduck.com
O = Feisty Duck Ltd
L = London
C = GB

[ext]
subjectAltName = DNS:www.feistyduck.com,DNS:feistyduck.com

11.2.5 自分自身の証明書に署名する

p.332 CSR生成後の自己署名証明書生成

$ openssl x509 -req -days 365 -in fd.csr -signkey fd.key -out fd.crt

p.333 CSR生成せずに自己署名証明書を生成可能

$ openssl req -new -x509 -days 365 -key fd.key -out fd.crt(対話式)
$ openssl req -new -x509 -days 365 -key fd.key -out fd.crt \
  -subj "/C=GB/L=London/O=Feisty Duck Ltd/CN=www.feistyduck.com"

11.2.6 複数のホスト名に対して有効な証明書を生成する

p.333 OpenSSLで作る証明書はデフォルトではSubjectが1つ(=有効なホストが1つ)
p.333 wwwありとなしのホストが必要になるケースなどがある
p.333 単一の証明書で複数のホスト名に対応する2つの方法

  • X.509拡張のSAN(SubjectAlternativeName)でホスト名列挙
  • ワイルドカード証明書を利用する

p.333 SAN拡張が含まれているとSubjectフィールドが無視されるので注意:利用したいホスト名は全てSAN拡張に載せる必要がある

$ cat fd.ext
subjectAltName = DNS:*.feistyduck.com, DNS:feistyduck.com

$ openssl x509 -req -days 365 -in fd.csr -signkey fd.key -out fd.crt \
   -extfile fd.ext

11.2.7 証明書を調べる

p.334 openssl x509コマンド:証明書の情報を出力する
p.334 -textオプション:中身を印字する
p.334 -nooutオプション:エンコードされた証明書を出力しないようにするオプション
p.334 パブリックCAが発行する証明書の方がフィールドが多い
p.334 Basic Constraints拡張:主体者(Subject)がCAかどうか(証明書に署名できるかどうか)
p.334 Key Usage拡張、Extended Key Usage拡張:証明書の用途制限(サーバ証明書はコード署名に使えない、など)
p.335 CRL Distribution Points拡張:失効情報の配布場所URL。CRLは発行元CAによって署名されるのでhttpでも完全性を検証可能。
p.335 Certificates Policies拡張:OID形式でEV証明書かどうか示す(発行元CAによって異なるOIDとなる)。CPS(Certificate Policy Statement/認証業務実施規程)へのリンクを含む
p.335 AIA拡張(Authority Information Access):OCSPレスポンダの情報、または発行元証明書(証明書チェーン上の次の証明書)へのリンク(IEなどが不完全な証明書チェーンの修正に利用)
p.335 Subject Key Identifier拡張、Authority Key Identifier拡張:AKIの値は発行者の証明書のSKIの値と一致し、証明書パス構築に利用
p.336 SAN拡張:証明書が有効となるホスト名一覧、これがある場合Common Nameフィールドは無視される

11.2.8 鍵と証明書の変換

p.336 バイナリ形式(DER)の証明書:X.509証明書そのまま(DER ASN.1エンコーディング)
p.336 ASCII形式(PEM)の証明書:DERをBase64でエンコード。----BEGIN CERTIFICATE-----で始まる
p.336 バイナリ形式(DER)の鍵:秘密鍵そのまま(DER ASN.1エンコーディング)。OpenSSLでは独自形式で秘密鍵が生成される
p.336 ASCII形式(PEM)の鍵:DERをBase64でエンコード。追加でメタデータ(パスワード保護時のアルゴリズムなど)を含む
p.336 PKCS#7形式の証明書:.p7bまたは.p7c。RFC2315で規定。証明書チェーン全体を含められる。
p.337 PKCS#12形式の鍵と証明書:.p12または.pfx。サーバの鍵をチェーン全体と一緒に格納できる。

■PEMとDERの変換

p.337 証明書ならx509コマンド、鍵ならrsaまたはdsaコマンドを使う

// PEM -> DER
$ openssl x509 -inform PEM -in fd.pem -outform DER -out fd.der
// DER -> PEM
$ openssl x509 -inform DER -in fd.der -outform PEM -out fd.pem
■PKCS#12(PFX)の変換

p.337 openssl pkcs12 -export ...で鍵・証明書・中間証明書を単一の.p12ファイルに変換
p.337 openssl pkcs12 -in fd.p12 -out fd.pem -nodes 逆方向の場合、手動で分割が必要
p.338 -nocertsオプションや-nokeys+-cacertsオプション、-nokeys+-clcertsオプションを組み合わせると要素を切り出せる

■PKCS#7への変換

p.338 crl2pkcs7コマンドを利用する

$ openssl crl2pkcs7 -nocrl -out fd.p7b -certfile fc.crt -certfile fd-chain.crt

p.338 PEMへの変換:pkcs7コマンドに-print_certsオプションを利用(手動で不要な要素の削除が必要)

KIDANI AkitoKIDANI Akito

11.3 設定

p.339 暗号スイートの設定:OpenSSLを利用するほぼすべてのプログラムで利用

11.3.1 暗号スイートの選択

p.339 暗号スイート:認証、鍵交換、暗号化、その他の操作が決まる重要な要素
p.339 OpenSSLに依存しているApache HTTPDの設定例[1]

// 優先順位づけを有効化
SSLHonorCipherOrder On
// 対応する暗号スイートを指定
// !aNULLは認証なしの暗号スイートを含まない、という意味
// @STRENGTHは暗号アルゴリズムの鍵長でソートする、という意味
SSLCipherSuite "HIGH:!aNULL:@STRENGTH"

p.339 OpenSSLのciphersコマンドを使って設定を考えよう[2]

■対応する暗号スイートの一覧を取得する

p.339 インストールしたOpenSSLのバージョンによって対応する暗号スイートが異なる
p.339 まずは openssl ciphers -v 'ALL:COMPLEMENTOFALL' で確認する[3]
p.339 OpenSSL 1.0.0以降であれば-Vオプションでさらに詳細表示可能

$ openssl version
LibreSSL 2.8.3
$ openssl ciphers -V 'ALL'
          0xCC,0xA9 - ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=ChaCha20-Poly1305 Mac=AEAD
          0xCC,0xA8 - ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=RSA  Enc=ChaCha20-Poly1305 Mac=AEAD
          0xCC,0xAA - DHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=DH       Au=RSA  Enc=ChaCha20-Poly1305 Mac=AEAD
          0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
(省略)

p.340 ここでの暗号スイートの表示順に従って、優先的に利用される

■キーワード

p.340 キーワード:暗号スイートの設定を構成する基本要素
p.341 DEFAULT, ALL, HIGH, MEDIUM, LOW, EXPORT, TLSv1, SSLv3などがある:LOWやEXPORTなど安全でないものも
p.341 ハッシュ関数のキーワード:MD5, SHA1, SHA256, SHA384
p.341 TLS1.2以降のAEAD(認証付暗号)ではプロトコルレベルの完全性検証が不要になったためハッシュ関数の指定が無効(AEADスイート末尾のSHA256やSHA384はPRFに使うハッシュ関数)
p.342 認証のキーワード:aDH, aDSS, aECDH, aECDSA, aNULL(匿名DHを指す), aRSA, PSK, SRPなど
p.342 鍵交換アルゴリズムのキーワード:ADH(匿名DH),AECDH(匿名ECDH) ,DH,ECDH,DHE,ECDHE,RSA,kEDH(匿名DHを含む一時的DH鍵交換)など
p.343 暗号化に関するキーワード:3DES, AES, AESGCM, CAMELLIA, NULL, RC4など
p.343 その他のキーワード:@STRENGTH, aGOST(認証にGOST R 34.10を利用する[4]

■キーワードの組み合わせ方

p.343 ほとんどの場合には単体で使う
p.343 +で結合:複数の用件に適合するものを選択できる

$ openssl ciphers -v 'RC4+SHA
(Enc=RC4 Mac=SHA1のもののみ出力される)
■暗号スイートのリストを構築する

p.344 リストにキーワードを追加する:コロン、スペース、コンマで連結する

全て同じ結果
$ openssl ciphers -v 'RC4:AES'
$ openssl ciphers -v 'RC4 AES'
$ openssl ciphers -v 'RC4,AES'
■キーワード修飾子

p.344 削除(-):合致するスイートをリストから全て取り除く、後から再追加可能
p.344 永遠に削除(!):後から他のキーワードで追加されないようになる
p.344 末尾に移動(+):リスト中のスイートの中で、指定されたものを末尾に移動する(追加はしない)
p.344 並べ替え(@STRENGTH):自動的に並べ替えてくれるが十分ではない
p.345 3DESなどの脆弱性が反映されていないバージョンがあるため

■エラー対応

p.345 2種類=タイポや存在しないキーワードor空の暗号スイートリスト

// 存在しないキーワードを指定した場合:invalid commandと出る
$ openssl ciphers -v '@HIGH'
4338255404:error:14FFF118:SSL routines:(UNKNOWN)SSL_internal:invalid command:/(省略)/libressl-2.8/ssl/ssl_ciph.c:1247:

// 暗号スイートが空:no cipher matchと出る
$ openssl ciphers -v 'SHA512'
4376364588:error:14FFF0B9:SSL routines:(UNKNOWN)SSL_internal:no cipher match:/(省略)/libressl-2.8/ssl/ssl_lib.c:1355:
■暗号スイート設定の要点

p.345 考慮すべき側面がたくさんあり、唯一絶対の設定はありえない[5]

  1. 事実上128ビット安全性をもつ(3DESは除外)
  2. 強力な認証が利用可能(匿名のスイート、輸出用暗号のスイートは除外)
  3. 脆弱性が指摘されているアルゴリズムを利用しない(MD5など)
  4. 堅牢なPFSに対応する(RSA鍵交換は除外)
  5. 認証でRSAよりECDSAを優先する(2通りの鍵を使い分ける運用が必須)
  6. TLS1.2のクライアントにはAESGCMを優先
  7. RC4はリストの末尾にする

p.346 !を使って以下を恒久的に除去するとよい

  • aNULL:認証なし
  • eNULL:暗号化なし
  • LOW:強度が低いスイート
  • 3DES:事実上の強度が108ビット
  • MD5
  • EXP:輸出用暗号スイート(40ビット)

p.346 たまにしか使わないDSA, PSK, SRP, ECDH、廃止されているSEED,IDEA、遅くサポートが少ないCAMELLIAなども除去する
p.346 必要な暗号スイートとして、PFS重視でkEECDH,kEDHを追加
p.347 この時点では鍵交換ECDH+認証RSAが最上位に来てしまう:kEECDH+ECDSAを先頭に追加
p.347 SSLv3など古い暗号スイートが混在している:+SHAでSHA1を末尾に移動
p.347 残り:HIGHを追加、+RC4を追加

■推奨設定

p.348 前項の設定が必ずしも最良ではない:好みやリスク評価による
p.348 パフォーマンスを追求:128ビットを優先、256ビットは処理が遅い(HMAC-SHA > HMAC-SHA256, HMAC-SHA384)

推奨設定
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-SHA
ECDHE-ECDSA-AES256-SHA
ECDHE-ECDSA-AES128-SHA256
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-SHA
ECDHE-RSA-AES256-SHA
ECDHE-RSA-AES128-SHA256
ECDHE-RSA-AES256-SHA384
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES128-SHA
DHE-RSA-AES256-SHA
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA384
// 古いクライアント向け:Windows XPなど
EDH-RSA-DES-CBC3-SHA
AES128-SHA
AES256-SHA
DES-CBC3-SHA
ECDHE-RSA-RC4-SHA
RC4-SHA

p.349 OpenSSL 1.1.x以降ではChaCha20/Poly1305に対応:AES以上のおセキュリティ、モバイルでもパフォーマンスが高い。ただしデスクトップではハードウェアアクセラレーションによりAES GCMの方が高速
p.349 BoringSSLでは同等レベルの暗号スイートを指定する機能がある:クライアントに暗号スイートの選択を任せられる

脚注
  1. OpenSSLのドキュメントによると,HIGHは128ビット以上の鍵長を持つ。 ↩︎

  2. ALL, HIGH, MEDIUM, LOWなどの他に、RSA, DH, DHE, ECDH, ECDHE, ECDSA, AES, AESGCM, CHACHA20などの要素技術での指定、およびAES128-SHAやECDHE-ECDSA-AES128-SHA256といった具体的な暗号スイート名を指定することもできる。 ↩︎

  3. ALLと書かれているが、全てでなくeNULL(暗号化なし)のスイートを含まない。同じく、DEFAULTにもeNULLは含まれない。参考 ↩︎

  4. RFC 5832でGOST R 34.10-2001が定められている。ロシア連邦の電子署名の標準。RFC 7091で2012年版が定められている。それぞれのRFCはGOSTの英訳になっている。 ↩︎

  5. TLS1.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。 RFC 8446 Appendix B.4 ↩︎

KIDANI AkitoKIDANI Akito

11.3.2 パフォーマンス

p.349 OpenSSLには組み込みのベンチマーク用ツールがある:speedコマンド
p.350 関係あるアルゴリズムのみテスト:$ openssl speed rc4 aes rsa ecdh sha
p.350 結果には、以下のように、バージョン情報・コンパイル時オプション、共通鍵暗号・ハッシュ関数のベンチマーク、公開鍵暗号のベンチマークが含まれる

$ openssl speed rc4 aes rsa ecdh sha 
Doing sha1 for 3s on 16 size blocks: 21593140 sha1's in 3.00s
Doing sha1 for 3s on 64 size blocks: 13130024 sha1's in 3.00s
(省略)
Doing 571 bit  ecdh's for 10s: 565 571-bit ECDH ops in 10.00s
LibreSSL 2.8.3
built on: date not available
options:bn(64,64) rc4(ptr,int) des(idx,cisc,16,int) aes(partial) blowfish(idx) 
compiler: information not available
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes
sha1            115201.74k   279994.15k   505222.42k   632033.29k   681473.75k
rc4            1053044.63k  1267069.24k  1333968.07k  1349330.03k  1359364.91k
aes-128 cbc     304227.46k   315529.38k   318755.65k   317318.79k   319863.63k
aes-192 cbc     262009.27k   271417.39k   273183.11k   272352.47k   273932.99k
aes-256 cbc     231834.36k   237524.94k   233694.55k   238283.92k   239939.19k
sha256          132463.27k   377677.42k  1110639.76k  1890959.23k  2372561.11k
sha512           65588.01k   260266.99k   365220.72k   488160.81k   546823.17k
                  sign    verify    sign/s verify/s
rsa  512 bits 0.000060s 0.000007s  16742.3 139161.9
rsa 1024 bits 0.000276s 0.000023s   3627.3  44013.3
rsa 2048 bits 0.001704s 0.000077s    586.8  13025.3
rsa 4096 bits 0.011061s 0.000273s     90.4   3658.9
                              op      op/s
 160 bit ecdh (secp160r1)   0.0003s   3232.4
 192 bit ecdh (nistp192)   0.0003s   3101.1
 224 bit ecdh (nistp224)   0.0004s   2353.6
 256 bit ecdh (nistp256)   0.0005s   2153.4
 384 bit ecdh (nistp384)   0.0010s   1007.1
 521 bit ecdh (nistp521)   0.0019s    524.4
 163 bit ecdh (nistk163)   0.0012s    866.1
 233 bit ecdh (nistk233)   0.0022s    453.7
 283 bit ecdh (nistk283)   0.0034s    290.9
 409 bit ecdh (nistk409)   0.0080s    124.7
 571 bit ecdh (nistk571)   0.0178s     56.3
 163 bit ecdh (nistb163)   0.0012s    865.0
 233 bit ecdh (nistb233)   0.0022s    451.6
 283 bit ecdh (nistb283)   0.0034s    292.0
 409 bit ecdh (nistb409)   0.0080s    124.8
 571 bit ecdh (nistb571)   0.0177s     56.5

p.451 コンパイル時オプションの比較、OpenSSLバージョンの比較などに利用できる
p.451 0.9.8k -> 1.0.1hでは2048ビットRSA鍵の処理が2倍近く高速に
p.451 注意:現実にはHTTPのkeepaliveやキャッシュなど、他の速度要因が大きいのであくまで参考程度に
p.451 speedコマンドはデフォルトでは1プロセス:-multiオプションでコア数指定可能
p.452 例)4コアで2048ビットRSA署名が秒間約1000回:1000件/秒のTLS接続を捌ける
p.452 AESなどのハードウェアアクセラレーションもデフォルトでは無効:-evpオプション[1]

11.4 プライベートCAを作る

p.452 最大の挑戦:インフラを安全に保つこと
p.452 ルート証明書の鍵はオフラインで安全に管理:一方、CRL/OCSPレスポンダの証明書は一定期間でルート証明書の鍵で署名して更新が必要

11.4.1 機能と制限

p.453 プライベートCAの構造はパブリックCAと同じ:ルートCAと複数の下位CA、CRLとOCSPレスポンダ

11.4.2 ルートCAを作る

■ルートCAの設定

p.353 設定ファイル(root-ca.conf)を用意する

[default]
name = root-ca
domain_suffix = example.com
aia_url = http://$name.$domain_suffix/$name.crt
crl_url = http://$name.$domain_suffix/$name.crl
ocsp_url = http://ocsp.$name.$domain_suffix:9080
default_ca = ca_default
name_opt = utf8, esc_ctrl, multiline, lname, align

[ca_dn]
countryName = "GB"
organizationName = "Example"
commonName = "Root CA"

p.354 電子署名のアルゴリズムはSHA256がデフォルト
p.354 デフォルトのポリシー(policy_c_o_match):発行される全ての証明書にcountryName, organizationNameが同じになる(プライベートCA向けの設定)

[ca_default]
home = .
database = $home/db/index
serial = $home/db/serial
crlnumber = $home/db/crlnumber
certificate = $home/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/random
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = none
default_days = 3650
default_crl_days = 365
default_md = sha256
policy = policy_c_o_match

[policy_c_o_match]
countryName = match
stateOrProviceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

p.354 reqコマンド向け拡張設定

[req]
default_bits = 4096
encrypt_key = yes
default_md = sha256
utf8 = yes
string_mask = utf8only
prompt = no
distinguished_name = ca_dn
req_extensions = ca_ext

[ca_ext]
basicConstraints = critical,CA:true
keyUsage = critical,keyCertSign,crlSign
subjectKeyIdentifier = hash

p.355 下位CA向けの設定

[sub_ca_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:true,pathlen:0
crlDistributionsPoints = @crl_info
extendedKeyUsage = clientAuth,serverAuth
keyUsage = critical,keyCertSign,cRLSign
nameConstraints = @name_constraints
subjectKeyIdentifier = hash

[crl_info]
URI.0 = $crl_url

[issuer_info]
caIssuers;URI.0 = $aia_url
OCSP;URI.0 = $ocsp_url

[name_constraints]
permitted:DNS.0=example.com
permitted:DNS.1=example.com
excluded;IP.0=0.0.0.0/0.0.0.0
excluded;IP.1=0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0

[ocsp_ext]
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
extendedKeyUsage =OCSPSigning
keyUsage = critical,digitalSignature
subjectKeyIdentifier = hash
■ルートCAのディレクトリ構成

p.356 privateディレクトリは秘密鍵が格納される:最小限のユーザーアカウントだけ作るように
p.356 シリアル番号は無作為な値にしておくこと(CAを作り直した際の衝突防止)

■ルートCAの生成
// 秘密鍵とCSRを生成する
$ openssl req -new -config root-ca.conf -out root-ca.csr -keyout private/root-ca.key
// 自己署名証明書を生成する
$ openssl ca -selfsign -config root-ca.conf -in root-ca.csr -out root-ca.crt -extensions ca_ext
■データベースファイルの構造

p.357 db/index:証明書の情報が行ごとにタブ区切りで格納
p.357 ステータスフラグ(V:有効,R:失効,E:期限切れ)

■ルートCAにおける操作
// CRLを作成
$ openssl ca -gencrl -config root-ca.conf -out root-ca.crl
// 証明書を発行
$ openssl ca -config root-ca.conf -in sub-ca.csr  -out sub-ca.crt -extensions sub_ca_ext

p.357 失効するときはcaコマンドの-revokeオプションを利用
p.357 -crl_reasonで理由指定:unspecified, keyCompromise, CACompromise, affiliationChanged, superseded(破棄), cessationOfOperation, certificateHold(保留),removeFromCRL

$ openssl ca -config root-ca.conf -revoke certs/1002.pem -crl_reason keyComprimise
■OCSP署名のための証明書を生成する
// OCSPレスポンダの鍵とCSRを作成
$ openssl req -new -newkey rsa:2048 \
 -subj "/C=GB/O=Example/CN=OCSP Root Responder" \
 -keyout private/root-ocsp.key -out root-ocsp.csr
// ルートCAで証明書発行
$ openssl ca -config root-ca.conf -in root-ocsp.csr -out root-ocsp.crt \
  -extensions ocsp_ext -days 30
// レスポンダを開始
$ openssl ocsp -port 9080 -index db/index -rsigner root-ocsp.crt \
  -rkey private/root-ocsp.key -CA root-ca.crt -text
// OCSPレスポンダのテスト(verify OKと出たら成功)
$ openssl ocsp -issuer root-ca.crt -CAfile root-ca.crt -cert root-ocsp.crt \
  -url http://127.0.0.1:9080

p.358 本番環境ではOCSPレスポンダの鍵と証明書はOCSPレスポンダとは別の場所にすべし

11.4.3 下位CAを生成する

p.359 ルートCAとほとんど同じ手順の繰り返し

■下位CAの設定

p.359 root-ca.confに、以下のように変更を加えてsub-ca.confを作成(変更のない部分は省略)
p.360 ディレクトリ名をsub-caなどにして、中身の階層は同様。

[default]
name = sub-ca
ocsp_url = http://ocsp.$name.$domain_suffix:9081

[ca_dn]
countryName = "GB"
organizationName = "Example"
commonName = "Sub CA"

[ca_default]
default_days = 365
default_crl_days = 30
// CSRに含まれる拡張を証明書にコピーする:危険なので注意
copy_extensions = copy

[server_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth,serverAuth
keyUsage = critical,digitalSignature,keyEncipherment
subjectKeyIdentifier = hash

[client_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth
keyUsage = critical,digitalSignature
subjectKeyIdentifier = hash
■下位CAの生成
$ openssl req -new -config sub-ca.conf -out sub-ca.csr -keyout private/sub-ca.key
$ openssl ca -config root-ca.conf -in sub-ca.csr -out sub-ca.crt -extensions sub_ca_ext
■下位CAにおける操作
// サーバー証明書の発行
$ openssl ca -config sub-ca.conf -in server.crt -out server.crt -extensions server_ext
// クライアント証明書の発行
$ openssl ca -config sub-ca.conf -in client.crt -out client.crt -extensions client_ext

p.361 証明書が発行されたら、検証用に表示される情報に注意:特に識別名、basicConstraints拡張、subjectAlternativeName拡張
p.361 CRLの生成や証明書の失効はルートCAの場合と同様

脚注
  1. 手元のMacBook Proでは特に結果が変わらなかった... ↩︎

KIDANI AkitoKIDANI Akito

第12章 OpenSSLによるテスト

p.363 SSL/TLSのテスト時に本当に頼りになるツール:OpenSSLとWireshark

12.1 SSL/TLSのサービスに接続する

p.363 サーバに接続すると接続情報が表示され、HTTPなど上のレイヤの入力が可能になる

2022年9月3日時点
$ openssl s_client -connect www.feistyduck.com:443
CONNECTED(00000005)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = feistyduck.com
verify return:1
---
Certificate chain
 0 s:/CN=feistyduck.com
   i:/C=US/O=Let's Encrypt/CN=R3
 1 s:/C=US/O=Let's Encrypt/CN=R3
   i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
 2 s:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFNzC(省略)rvNSqzA==
-----END CERTIFICATE-----
subject=/CN=feistyduck.com
issuer=/C=US/O=Let's Encrypt/CN=R3
---
No client certificate CA names sent
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 4717 bytes and written 322 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 6B7A0(省略)B30A
    Session-ID-ctx: 
    Master-Key: C5EA(省略)3D8
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - b5 da da 50 d8 8c 7c 8a-e6 91 d8 77 29 78 08 fe   ...P..|....w)x..
 (省略)
    00b0 - 9b 2e 06 9b 9c ea 8c 61-ae 59 97 0d e6 f7 68 70   .......a.Y....hp

    Start Time: 1662207983
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
(ここにHTTPのメッセージを入力する、GET /index.html HTTP/1.1 ...とか)

p.364 証明書チェーンのエラーが出る場合もある[1]

depth=3 CN = (省略)
verify error:num=19:self signed certificate in certificate chain
verify return:0

p.364 -CAfileオプションで信頼する証明書ストアを指定すると上記エラーを解消できる
p.364 AppleのOS Xに同梱されているOpenSSLは0.9.Xベースの改造版で、証明書検証失敗時のフォールバックとしてキーリングが利用されるようになっている:参考[2]
p.365 Certificate chain欄:サーバが提示した証明書チェーンが表示される、ブラウザで表示できるブラウザによって再構成された証明書チェーンとは異なる可能性がある
p.365 Server certificate欄:サーバ証明書
p.366 証明書のOIDが判定できなかった場合、所有者名のところに数字の列が表示される:例)1.3.6.1.4.1.311.60.2.1.3
p.366 SSL Handshake has read ...に続いてTLS接続の情報が表示される:プロトコルバージョン、暗号スイート、セッションID、セッションチケット、安全な再ネゴシエーションの対応有無、など
p.367 OS distributionに同梱されるOpenSSLそのものと異なる場合がある:Ubuntu 12.04 LTSではTLS1.2が無効化されていた

12.2 SSL/TLS利用へと昇格するプロトコルを試す

p.367 HTTP:平文の通信チャネル全体をTLSが包み込むことでHTTPSになる
p.367 平文で通信を開始、その後暗号化通信に昇格するプロトコル:SMTP、POP3、IMAP、FTP、XMPP[3]

$ openssl s_client -connect gmail-smpt-in.l.google.com:25 -starttls smtp

12.3 異なる形式のハンドシェイクを使う

p.367 OpenSSL:対応しているすべてのプロトコルでネゴシエーションを試みる(p.368の説明では最良のプロトコルを使う、とある...どっち?)
p.367 (古いバージョンでは)SSL2.0に対応しているため、サーバがSSL2.0未対応だと通信に失敗することも
p.368 -no_ssl2オプションでSSL2.0は無効化できる[4]

12.4 リモートの証明書を取得する

p.368 openssl s_client -connectの出力結果にPEMエンコードされたサーバ証明書が出力される
p.368 証明書だけを出力する場合のコマンド

// echoコマンドはs_clientへの入力(タイムアウト回避)
$ echo | openssl s_client -connect www.feistyduck.com:443 2>&1 | sed --quiet \
    '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > www.feistyduck.com.crt

p.368 証明書チェーン全体を出力する場合は-showcertsオプションを指定する

12.5 対応しているプロトコルを調べる

p.368 s_clientはデフォルトの最良のプロトコルを使う
p.368 -ssl2/-ssl3/-tls1/-tls1_1/-tls1_2を明示的に指定[5]
p.368 調べたくないプロトコルの場合は-no_ssl2などのように指定する

12.6 対応している暗号スイートを調べる

p.369 -cipherオプションで暗号スイートに対応しているかどうか調べられる:例)-cipher RC4-SHA
p.369 注意)OpenSSLが対応している暗号スイートしか調べられない
p.369 OpenSSL 0.9.8kでは32種類しかなかった:1.0.1系列では100種類以上
p.370 網羅的なテストを行うため、SSL Labsではネゴシエーションを開始するだけで、実際には暗号スイートを利用した接続はしない

12.7 SNIを要求するサーバを調べる

p.370 当初のSSLの設計:エンドポイント(アドレス+ポート)ごとに1つのWebサイト
p.370 SNI:1つのエンドポイントで複数のWebサイト(証明書)を利用可能にするTLS拡張
p.370 SNI(-servernameオプション)を指定しない場合の接続結果

  • 本来取得したかった証明書が手に入る
  • 本来取得したかった証明書と異なる証明書が手に入る
  • サーバがハンドシェイクを中断して接続を拒否する(極めて稀)

p.370 -servernameオプションの有無で証明書が変わる場合、SNIが要求されている
p.370 OpenSSL 1.0.0以前のバージョンだと、要求したサーバ名の証明書が利用できない場合、TLSのアラートを受け取ってクライアントは接続を中断する

12.8 セッションの再利用の有無を調べる

p.370 s_clientでセッション再利用のテスト:-reconnectオプションを付けると6回接続する(後半5回が再利用)
p.371 新しいセッション:New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
p.371 再利用されたセッション:Reused, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
p.371 セッションチケットを使いたくない場合:-no_ticketオプションで無効化できる

12.9 OCSPによる失効を確認する

p.371 手順は以下の通り

  • 失効を確認したい証明書を取得する
  • 発行元の証明書を取得する
  • OCSPレスポンダのURLを特定する
  • OCSPリクエストを発行してレスポンスを確認する

p.372 証明書の取得にはs_clientの-showcertsオプションを指定
p.372 証明書チェーンの1通目の発行元と2通目の所有者が一致することを確認
p.372 一致しない場合は、末端の証明書のAIA(Authority Information Access)拡張を調べるとOCSPレスポンダのURLと発行元証明書のURLが得られる
p.372 証明書からOCSPレスポンダのアドレスを調べる:openssl x509コマンドの-ocsp_uriオプション

$ openssl x509 -in fd.crt -noout -ocsp_uri
http://ocsp.starfieldtech.com/
$ openssl ocsp -issuer issuer.crt -cert fd.crt -url http://ocsp.starfieldtech.com/ -CAfile issuer.crt
WARNING: no nonce in response
Response verify OK
       fd.crt: good          // 失効している場合revokedとなる
          This Update: Feb 18 ...
          Next Update: Feb 18 ...

p.373 ナンスの警告:OpenSSLがリプレイ攻撃への防御としてナンスを利用しようとしているがサーバが省略している(パフォーマンスのため、OCSPレスポンスをキャッシュして再利用している)
p.373 サーバによっては、ナンスを要求しない(-no_nonceオプション)、Hostヘッダを使う(-header Host ocsp.example.com:OpenSSL 1.0.0以降のみ)などの対応が必要

12.10 OCSPステープリングを調べる

p.373 TLS接続の中でOCSPレスポンスを取得できるので、12.9のような手間が不要
p.374 status_request拡張でOCSPステープリングを要求する:-statusオプション
p.374 非対応のサーバの出力:OCSP response: no response sent
p.374 対応しているサーバ:出力にOCSPレスポンス全体が表示される

$ echo | openssl s_client -connect www.feistyduck.com:443 -status
CONNECTED(00000005)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
(省略)
OCSP response: 
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = US, O = Let's Encrypt, CN = R3
    Produced At: Sep  2 12:16:00 2022 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: 48DAC9A0FB2BD32D4FF0DE68D2F567B735F9B3C4
      Issuer Key Hash: 142EB317B75856CBAE500940E61FAF9D8B14C2C6
      Serial Number: 04BA3EAA47B97CA942EBA009467B39B88934
    Cert Status: good // 失効していない
    This Update: Sep  2 12:00:00 2022 GMT
    Next Update: Sep  9 11:59:58 2022 GMT

    Signature Algorithm: sha256WithRSAEncryption
         18:25:e3:b1:c3:aa:2d:bb:f9:af:ad:5f:7b:36:40:71:87:5d:
   (省略)
         fc:05:2e:fb
======================================
---
Certificate chain
 0 s:/CN=feistyduck.com
   i:/C=US/O=Let's Encrypt/CN=R3
(省略)
DONE

12.11 CRLの失効を調べる

p.374 OCSP以上に込み入った手順が必要

  • 失効を確認したい証明書を取得する
  • 発行元の証明書を取得する // ここまではOCSPの場合と同じ
  • CRLをダウンロードして検証する
  • 証明書のシリアル番号をCRLで探す
// CRLのURLを取得する
$ openssl x509 -in server.crt -noout -text | grep crl
 URI:http://crl.sca1b.amazontrust.com/sca1b-1.crl
// CRLを検証する
$ openssl crl -in sca1b-1.crl -inform DER -CAfile issuer.crt -noout
verify OK
// シリアル番号を確認する
$ openssl x509 -in server.crt -noout -serial 
serial=06C360C1059100A5ACE7B499E9DE5401
// CRLを確認する:シリアル番号が記載されていれば失効している
$ openssl crl -in sca1b-1.crl -inform DER -text -noout
Certificate Revocation List (CRL):
          Version 2 (0x1)
     Signature Algorithm: sha1WithRSAEncryption
           Issuer: ...
           (省略)
Revoked Certificates:
     Serial Number: 000000000
            Revocation Date: Nov 26 ...
        (省略)
脚注
  1. 本書執筆時とはwww.feistyduck.comの証明書チェーンが変更になっているため、2022年9月3日時点ではエラーが出なかった。 ↩︎

  2. ただし、最近のHigh Sierra以降のMacではLibreSSLが同梱されている(参考)。AppleのリポジトリではLibreSSLは公開されていないので、改造されていないバージョンだろうか? ↩︎

  3. eXtensible Messaging and Presence Protocol:インスタントメッセンジャーのプロトコル。 ↩︎

  4. Stackoverflowの回答によると、1.0.1s/1.0.2g以降(2016年3月)はSSLv2をデフォルトで無効化(設定してビルドすると利用可能)、 1.1.0以降(2016年8月)はSSLv2のコードが削除されており、SSLv3をデフォルトで無効化している。ということは、最近の流れでTLS1.0/1.1が無効されたサーバ相手の通信だと失敗する? ↩︎

  5. 2018年の1.1.1でTLS1.3もサポートされている。リリースノート ↩︎

KIDANI AkitoKIDANI Akito

12.12 再ネゴシエーションを調べる

※安全でない再ネゴシエーションに対するMITM攻撃ついては7.1節を参照

p.376 s_clientで接続時にTLS拡張で判定

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported // 再ネゴシエーションがサポートされていない場合はIS NOT〜
Compression: NONE

p.376 クライアント起源の再ネゴシエーションの利用:s_clientの入力にRをタイプ
p.377 対応しているサーバは証明書を再送
p.377 非対応のサーバは再ハンドシェイクを拒否

GET / HTTP/1.1
R
RENEGOTIATING
4376823340:error:140040E5:SSL routines:CONNECT_CR_SRVR_HELLO:ssl handshake failure:/AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/libressl/libressl-2.8/ssl/ssl_pkt.c:585:

p.377 安全な再ネゴシエーションと安全でない再ネゴシエーションの両方に対応したサーバは少ない
p.377 モダンなOpenSSLでは安全な再ネゴシエーションが優先されるため、0.9.8kなど古いバージョンで安全でない再ネゴシエーションをテストするのが良い

12.13 BEAST脆弱性を調べる

※脆弱性の詳細は7.2節を参照

p.377 TLS1.1未満のCBC暗号スイートの脆弱性
p.378 最新のクライアントはTLS1.2以降を使えば問題ない
p.378 TLS1.0を許可する場合はRC4のみサポート(PCIDSSはこのように要求)
p.378 BEASTの有無を調べるには-no_ssl2 -no_tls1_1 -no_tls1_2オプションを使う
p.378 RC4非対応クライアント向けにRC4を優先しCBCもサポートするサーバの調べ方は以下の通り

$ echo | openssl s_client -connect api.example.com:443 -cipher 'ALL:+RC4' \
    -no_ssl2 -no_tls1_1 -no_tls1_2

12.14 Heartbleedを調べる

※脆弱性の詳細は6.3節を参照

p.378 ツールでも検出できるが、脆弱なサーバを検出できないツールもある
p.379 Heartbeatプロトコルに対応した1.0.1以降のOpenSSL:-tlsextdebugオプションでサーバ拡張を表示

$ echo | openssl s_client -connect www.feistyduck.com:443 -tlsextdebug
CONNECTED(00000005)
TLS server extension "renegotiation info" (id=65281), len=1
0001 - <SPACES/NULS>
TLS server extension "EC point formats" (id=11), len=4
0000 - 03 00 01 02                                       ....
TLS server extension "session ticket" (id=35), len=0 // ここにheartbeat id=15があれば対応サーバ
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
(省略)

p.379 -msgオプション:プロトコルメッセージを表示する
p.379 Bとタイプするとheartbeatリクエストを送信できる

   enum {
      heartbeat_request(1),
      heartbeat_response(2),
      (255)
   } HeartbeatMessageType;

   struct {
      HeartbeatMessageType type;
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
      opaque padding[padding_length]; // 最低16バイトの乱数
   } HeartbeatMessage; // TLSレコードのcontent typeは24

p.379 脆弱性のテストには不正なペイロード長を送信できる改造版OpenSSLが必要
p.379 不正なOpenSSLサーバ:実際のペイロード長ではなく指定されたペイロード長でデータ返す(最大で64KB=2^16(uint16))
p.380 GnuTLS:Heartbeat対応で不正なペイロード長に応答するがメモリはリークしない(診断を難しくする要因)

12.15 DHパラメータの強度を見極める

p.381 OpenSSL1.0.2以降DHE鍵が利用されると強度が表示される

% openssl s_client -connect www.feistyduck.com:443 -cipher kEDH
CONNECTED(00000005)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
(略)
---
No client certificate CA names sent
Server Temp Key: DH, 2048 bits
---
(略)

p.381 輸出用暗号に対応しているか調べる場合は kEDH+EXPORT を指定

KIDANI AkitoKIDANI Akito

第13章 Apacheの設定

p.383 Apache httpd:2.4.x系ではTLSにもよく対応[1]
p.383 2013年11月の2.4.7でTLSの劇的な改善[2]
p.383 2.4.x系でのみ対応しているTLS機能

  • OCSPステープリング
  • 分散TLSセッションキャッシュ
  • 設定可能なセッションチケット鍵
  • セッション鍵の無効化

p.384 OSディストリビューション(UbuntuとかRHELとか)のApache:開発者による修正(機能追加)があるものも。ドキュメントとソースコードをよく読むこと
p.384 2.2.X系の問題:楕円曲線暗号に未対応(2.2.26未満、2.2.26は2013年11月リリース)
p.384 2.4.X:OpenSSLのメモリ節約モード(SSL_MODE_RELEASE_BUFFERSオプション)を利用
p.384 本書の説明はリファレンスではないので注意

13.1 静的なOpenSSLを使ってApacheをインストールする

p.384 OSベンダーが提供するバイナリ:TLSの最良の機能が使えないことも
p.384 昔ながらの方法でソースコードからインストール必要
p.385 その際、新しいバージョンのOpenSSLを別の場所にインストールする

$ ./config --prefix=/opt/openssl --openssldir=/opt/openssl enable-ec_nistp_64_gcc_128

p.385 続いて、Apache、APR、APR-Utilのソースを入手し、インストール
p.385 mod_sslモジュールは静的にコンパイルされる:他のモジュールは動的

$ ./configure (省略) --enable-ssl --with-ssl=/opt/openssl \
  --enable-ssl-staticlib-deps --enable-mods-static=ssl

13.2 TLSを有効にする

p.385 ポート443でListen:自動的にTLSが有効になる
p.385 非標準ポートを使う場合、以下のように明示的な設定が必要

Listen 192.168.0.1:8443 https

p.386 SSLEngineディレクティブでも設定可能(2.0.X時代からのやり方)

<VirtualHost 192.168.0.1:443>
    ServerName: site1.example.com
    SSLEngine on
</VirutalHost>

p.386 Webサーバでもありプロキシサーバでもある:それぞれの役割用のTLS設定ディレクティブがあるので注意

脚注
  1. 2022年9月現在の最新は2.4.54。https://httpd.apache.org/ ↩︎

  2. Changelogによると、Exportグレード暗号の排除や、OpenSSLの最低サポートバージョンが上がったり、任意のDHEパラメータが利用できるようになったりしたらしい。 ↩︎

KIDANI AkitoKIDANI Akito

13.3 TLSプロトコルの設定

p.386 Apacheで必要な3つのディレクティブ:SSLProtocol + SSLHonorCipherOrder + SSLCipherSuite

SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite "ECDHE-ECDSA-AES128-GCM-SHA256 \
ECDHE-RSA-AES128-GCM-SHA256 \
...
DHE-RSA-AES256-SHA256"
# サーバが暗号スイートを選ぶための設定
SSLHonorCipherOrder on

13.4 鍵と証明書の設定

p.387 秘密鍵と証明書チェーンのための設定3つ:SSLCertificateFile+SSLCertificateKeyFile+SSLCertificateChainFile

LoadModule ssl_module modules/mod_ssl.so

Listen 443
<VirtualHost *:443>
    ServerName www.example.com
    SSLEngine on
    # 証明書
    SSLCertificateFile "/path/to/www.example.com.cert"
    # 秘密鍵
    SSLCertificateKeyFile "/path/to/www.example.com.key"
</VirtualHost>

p.387 SSLCertificateChainFileは2.4.8で無効化されSSLCertificateFileに中間証明書も格納するように変更となった(参考):RSA鍵とECDSA鍵のサイトを同時に運用する場合など、異なる証明書チェーンが必要となったため
p.388 上記はパスフレーズで保護されていない鍵を使う設定:保護された鍵を使う場合はSSLPassPhraseDialogを使う[1]

13.5 複数の鍵を設定する

p.388 元々はRSA鍵とDSA鍵を同時に使えるようにするためのもの
p.388 近年はECDSA鍵でパフォーマンス向上を狙いつつ、SHA2署名への移行のために、鍵と証明書の組み合わせを複数用意して運用

SSLCertificateFile conf/rsa.crt
SSLCertificateKeyFile conf/rsa.key

SSLCertificateFile conf/dsa.crt
SSLCertificateKeyFile conf/dsa.key

SSLCertificateFile conf/ecdsa.crt
SSLCertificateKeyFile conf/ecdsa.key

# 中間証明書(上記すべての組み合わせで動作するもの)
SSLCertificateChainFile conf/chain.pem

p.388 SSLCertificateChainFileはサーバごとに1回しか利用できない点に注意
p.388 SSLCACertificateFileディレクティブですべての鍵に対する中間証明書を連結して設置することもできる:接続のたびにチェーン構築が必要なのでパフォーマンスが心配
p.389 Apache 2.4.8以降ではSSLCertificateFileで証明書チェーンを含んだファイルを指定できるので問題ない
p.389 設定した鍵が使われるように、暗号スイートにECDSA(ECDSA鍵のスイート)やDSS(DSA鍵のスイート)のものを設定する必要

13.6 ワイルドカード証明書とマルチサイト証明書

p.389 Apacheでは、同一IPで2つ以上のサイトを同じ証明書でデプロイできる
p.389 TLS終端とHTTPのホスト選択は別々のステップ

  • SNI情報がない場合のApache-TLS終端:デフォルトサイト(最初に設定されているサイト)の証明書を提示する
  • HTTPリクエストのHostヘッダを見て正しいサイトを提示

p.389 Apache 2.4.9以下の場合、同一エンドポイントに対して複数のセキュアなサイトがあると警告が表示される

13.7 仮想セキュアホスティング

p.389 異なる証明書のサイトを同じIPアドレスで配信するSNI(モダンブラウザ以外は未対応のクライアントもあるので注意)
p.390 未対応のクライアントの場合、Apacheはデフォルトサイトの証明書を提示:クライアントの要求すいたホスト名には一致しないため警告が表示される
p.390 SSLStrictSNIVHostCheckディレクティブ:IPアドレス全体または一部に厳格な仮想セキュアホスティングを強制し、非対応クライアントにはコンテンツを提示しないようにできる
p.391 Apache 2.4.9以下のStrictSNIVHostCheckのエラー=HTTPステータス403を返す:HostヘッダがマッチすればErrorDocumentディレクティブのメッセージが返る(ファイルは返らない)
p.391 独自のエラーメッセージを返す場合、以下のように実装する

RewriteEngine on
# SSL_TLS_SNI変数(クライアントが提示したSNI情報)が空かどうかチェック
RewriteCond %{SSL:SSL_TLS_SNI} = "" 
RewriteRule ^ /errors/no-sni.html

p.391 Apache 2.4.10以降では403ページに拒否理由が含まれ、ErrorDocumentのスクリプトを起動できる

脚注
  1. SSLPassPhraseDialogは、ターミナルでApache起動時にパスフレーズを入力するbuiltinのほか、外部プログラムを実行するように設定できる。 ↩︎

KIDANI AkitoKIDANI Akito

13.8 エラーメッセージ用にデフォルトサイトを予約する

p.391 検索エンジンに任意のホスト名に対するインデックスを作られると困る
p.391 不正なリクエストには実際のコンテンツを返さず、エラーメッセージ用のサイトを予約しよう

<VirtualHost 192.168.0.1:443>
  ServerName does-not-exist.example.com
  DocumentRoot /var/www/does-not-exist
  
  SSLStrictSNIVHostCheck on
  RewriteEngine on
  ReriteRule ^ - [L,R=404] # 全リクエスト404に書き換え
  ErrorDocument 404 "<h1>No such site</h1>"
  # TLSなど他の設定が続く(省略)
</VirtualHost>

13.9 PFS

p.392 バージョン2.4系でソースコードからコンパイルしていれば楕円曲線暗号スイート利用可能
p.392 古い2.2系では楕円曲線暗号に未対応:OpenSSLのバージョンによらない
p.392 OpenSSLのバージョンが古い:静的に最新版をインストールすれば対応可
p.392 RedHatのOS(Fedora, RHEL)は楕円曲線暗号を含まず出荷されていた(2013年以前):特許に抵触しないようにするため
p.393 TLS Interposer:2.2にパッチを当てずにECDHEスイートを利用するツール(2022年時点では2.2系はサポートされていないのでやめておいた方が良さそう)

13.10 OCSPステープリング

p.393 通常のOCSP:証明書情報からクライアントがCAと直接やりとり
p.393 ステープリング:WebサーバがCAからのOCSPレスポンスをキャッシュするので、パフォーマンス、プライバシー面で優位(Apache 2.4系以降)

13.10.1 OCSPステープリングの設定

p.393 有効にするためのディレクティブは2つ

# キャッシュサイズ128K
SSLStaplingCache shmcb:/opt/httpd/logs/stapling_cache(128000)
SSLUseStapling on

p.394 デフォルトでは3600秒間キャッシュ:SSLStaplingStandardCacheTimeoutディレクティブで変更可能
p.394 ApacheでOCSPレスポンスを検証するためにCA証明書が必要:SSLCertificateChainFileまたはSSLCACertificateFile

13.10.2 エラー処理

p.394 エラーもキャッシュされる:SSLStapingErrorCacheTimeout、デフォルト60秒
p.394 レスポンダがエラーを返さないようにする:SSLStaplingReturnResponderErrors off
p.395 CAのレスポンダが応答しない場合に、ApacheはデフォルトでtryLaterというOCSPレスポンスを生成する(がoffにしておいた方が良い):SSLStaplingFakeTryLater off

13.10.3 独自のOCSPレスポンダを使う

p.395 証明書にOCSP情報がない場合は、Apacheがハードコードする必要がある
p.395 Webサーバから外向きのトラフィックが許可されない場合も同様

SSLStaplingForceURL http://ocsp.example.com

13.11 DHE鍵交換の設定

p.395 デフォルトのDHEの強度は1024ビットだった:2.4.7/2.2.31以降、2048ビットになる

13.12 TLSセッションの管理

p.396 Apache:サーバーサイドでのキャッシングも、クライアントサイドでのチケットによるセッション管理にも対応
p.396 Apache2.4系:分散環境でのキャッシングにも対応

13.12 1 スタンドアロンのサーバでのセッションキャッシュ

p.396 ファイルへのセッションキャッシュも可能だが高負荷時に信頼性がない
p.396 今日メモリを利用するのが実用的:mod_socache_shmcbモジュールを有効化

# 1MBのキャッシュ(デフォルトは512KB):Apache2.4系では1MBで約4000セッション
SSLSessionCache shmcb:/path/to/logs/ssl_scache(1024000)
# 持続時間1日(デフォルトは5分)
SSLSessionCacheTimeout 86400

p.396 Apache再起動時にセッションキャッシュもクリアされる:gracefulオプションでmasterプロセスを維持してもクリア。
p.396 クリア時にはサーバCPU負荷+ユーザに若干遅延
p.397 ApacheはTLSセッションキャッシュを全サーバで共有:無関係なアプリケーションの場合危険(詳細は6.8.2)

13.12.2 スタンドアロンのサーバでのセッションチケット

p.397 セッションチケットの実装はOpenSSLによる
p.397 チケットは128ビットAES暗号で保護:サーバ初回起動時に使い捨ての鍵生成。
p.397 鍵長は固定だが十分に強力
p.397 サーバ再起動で新しい鍵生成=新しいTLSセッションのネゴシエーション必須
p.397 PFSを考慮し、1日1回はサーバ再起動が望ましい

13.12.3 分散環境におけるセッションキャッシュ

p.397 分散TLSセッションキャッシュのバックエンド:memcachedを利用
p.397 mod_socache_memcacheモジュールのインストールが必要

LoadModule socache_memcache_module modules/mod_socache_memcacheso
SSLSessionCache memcache:memcache.example.com:11211
SSLSessionCacheTimeout 3600

p.398 memcachedの設定の要点

  • セッションの全期間に十分なRAM(-mオプション)
  • スワップ防止のためのキャッシュメモリ固定(-kオプション)
  • クラスタ全体をサポート可能な接続数(-cオプション)
-d # デーモン実行
-u memcache
-p 11211
logfile /var/log/memcached.log
-m 10 # 10MB
-c 10240
-k

p.398 可用性:TLSセッション情報がmemcachedのみにあるので単一障害点となることに注意
p.398 パフォーマンス:memcachedまでのネットワークが十分高速なら低コスト
p.399 セキュリティ:memcachedまでの通信は通常暗号化されない。暗号化されたネットワークセグメントを使うなどで解決可
p.399 無関係なアプリケーション間で共有しないこと:memcachedセクションをアプリごとに分離するのがベスト

13.12.4 分散セッションチケット

p.399 クラスタの全ノードで同じ鍵を共有する必要がある:OpenSSL任せではだめ
p.399 Apache 2.4系のみ:SSLSessionTicketKeyFileディレクティブで設定
p.399 チケット鍵:48バイトのランダムなデータ。16バイト(128ビット)ずつ、鍵の名前、HMACシークレット、AES鍵。

$ openssl rand -out ticket.key 48

p.399 アプリごとの共有を避けるため、異なるチケット鍵を使うこと
p.400 PFSを守るため、セッション鍵を1日おきにローテートする必要あり

13.12.5 セッションチケットを無効にする

p.400 セッションチケットは運用面の負荷が増える
p.400 Apache 2.4.11以降ではSSLSessionTicketsディレクティブで無効化可能(off指定)
p.400 下位バージョンではパッチを当ててビルドし直すことで無効化可能

KIDANI AkitoKIDANI Akito

13.13 クライアント認証

SSLVerifyClient require # クライアント認証を要求
SSLVerifyDepth 2.         # クライアント証明書からルート証明書までの深さ
SSLCACertificateFile xxxx.pem # クライアント証明書の発行元CA
SSLCARevocationCheck chain  # クライアント証明書の失効確認
SSLCARevocationFile xxx.crl     # 失効した証明書一覧

# Apache 2.4系からOCSP利用可能
SSLOCSPEnable on

p.401 クライアント証明書が提供されない場合、fatalアラートでハンドシェイク拒否:SSLVerifyClientの値で改善可能
p.401 optionalを設定:SSL_CLIENT_VERIFY変数で検証結果(NONE, FAILED:〜, SUCCESS)を確認して、エラーメッセージを返す
p.401 optional_no_ca:要求するが検証は外部サービス任せ(SSL_CLIENT_始まりの変数で検証結果取得可能)
p.402 optionalだと不適切なクライアント証明書が選択されることもあるので注意
p.402 環境変数の利用にはSSLOptionsディレクティブが必要:+StdEnvVars +ExportCertData

13.14 プロトコルの問題を低減する

p.402 Apache自体は迅速に対処される:パッケージを提供するOSベンダー次第

13.14.1 安全でない再ネゴシエーション

p.402 2009年発見:2010年3月リリースの2.2.15でクライアント起源の再ネゴシエーション無効化
p.402 Apache 2.4系は2012年リリースで影響なし
p.402 サーバー起源の再ネゴシエーションを悪用することもできるので、クライアント起源のものを無効化するだけでなく、SSL_SECURE_RENEG変数をチェックして安全な再ネゴシエーションが可能なクライアントか見極めが必要

13.14.2 BEAST

p.402 TLS1.0以前のブラウザが脆弱:サーバ側のパッチでは対応不可(TLSのバージョンを上げるくらいか)

13.14.3 CRIME

p.403 2012年発見:圧縮の問題は解決されておらず、無効化が対策となる
p.403 2013年のApache 2.2.26/2.4.4以降、デフォルトで圧縮無効
p.403 ベンダー提供のパッケージは別途セキュリティパッチあり
p.403 圧縮無効化はOpenSSL1.0.0以降のSSL_OP_NO_COMPRESSIONオプションに依存:古いバージョンでは無効化できない可能性あり

13.15 HSTSの導入

p.403 Headerディレクティブで設定

# 保守的な設定:詳細は第10章参照
Header always set Strict-Transport-Security "max-age=300; includeSubDomains" 

p.404 80->443のリダイレクト設定もホストごとに必要

<VirtualHost *:80>
  ServerName example.com
  RedirectPermanent / https://example.com/
  ....省略..
</VirtualHost>

<VirtualHost *:80>
  ServerName www.example.com
  RedirectPermanent / https://www.example.com/
  ....省略..
</VirtualHost>

13.16 セッションキャッシュの状態をモニターする

p.404 mod_statusモジュールで外部から観察可能

# Apache 2.2系で必要な設定:2.4系ではmod_status読み込みで自動的に有効化
ExtendedStatus On

<Location /status>
  SetHandler server-status
  # 機密性が高いのでアクセス元のIPを制限する:Basic認証をかけてもよい
  Recquire ip 192.168.0.1
</Location>

p.405 キャッシュの利用率、エントリー数、期限切れ数などが確認できる

13.17 ネゴシエーションしたTLSパラメータをログに残す

p.405 パフォーマンスやプロトコル利用状況のために監視が必要
p.405 パフォーマンス:セッションリザンプションによる悪化がないか?(2.4系:SSL_SESSION_RESUMED変数=Initial or Resumed)
p.405 利用状況:弱いバージョンの無効化のための調査

SSLOptions +StdEnvVars
CustomLog /path/to/ssl.log "%t %h %k %X %{SSL_PROTOCOL}e \
   %{SSL_CIPHER}e %{SSL_SESSION_ID}e %{SSL_SESSION_RESUMED}e"

p.405 セッションIDはセッション再開時のみ記録
p.405 %k変数:同じ接続でのリクエスト数の記録
p.405 %X変数:リクエストの最後の接続状態。-:閉じる、+:開いたまま
p.406 TLSハンドシェイクの失敗を記録する方法はない

13.18 mod_sslhafによる高度なログ

p.406 クライアントが提示したプロトコル最新バージョンや暗号スイートを知りたい:mod_sslhaf(著者が開発したモジュール)

  • 対応している最新プロトコルバージョン
  • 提示された暗号スイート一覧
  • TLS拡張の一覧:SNI利用可否、セッションチケット対応状況、OCSPステープリング対応状況など
  • ClientHelloをログに残せる
KIDANI AkitoKIDANI Akito

第14章 JavaおよびTomcatの設定

p.409 主にJava7/8について

14.1 Javaにおける暗号の構成要素

p.409 JCA:Java Cryptography Architecture(Java暗号アーキテクチャ)。暗号用APIを定義。実態はプロバイダとして提供される。
p.409 Java Certification Path API:CertPathとも。証明書および証明書パスを処理するAPI。
p.409 JSSE:Java Secure Socket Extension。JCAを実装したSSL/TLSコンポーネント。
p.409 keytool:鍵と証明書をひとまとめにしたキーストアを操作するためのツール。
p.410 ルート証明書ストア:JVMベンダーが管理している

14.1.1 強力で制限のない暗号化

p.410 Javaの暗号化強度2つ:strong mode(かつてのデフォルト) / unlimited strength
※2022年現在では、無制限モードがデフォルト[1]
p.410 strong=米国の暗号輸出規制の制限あり:AESは128ビットのみ
p.410 無制限モード:古いJavaの場合、Oracleのサイトからポリシーファイルをダウンロードする必要がある

14.1.2 プロバイダの設定

p.411 汎用プロバイダ(例:SunJSSE)とプラットフォーム限定プロバイダ(例:SunMSCAPI(Windows用))
p.411 パフォーマンスやFIPS(Federal Information Processing Standards、連邦情報処理標準)対応は設定が必要
p.411 例)OpenSSLやNSS(MozillaのNetwork Security Services)

14.1.3 機能の概要

p.411 JavaのTLS実装は保守的で主要機能がなかなか実装されなかった
p.411 例)TLS1.2(2008年)の実装はJava7(2011年)で、デフォルトになったのはJava8(2014年)
p.412 Java7:クライアントサイドSNI、SHA256/384暗号スイートなどに対応
p.412 Java8:サーバーサイドSNI、GCM暗号スイート、1024ビットDH、サーバ暗号スイート選択、クライアント起源再ネゴシエーション無効化、AESハードウェアアクセラレートなどに対応

14.1.4 プロトコルの脆弱性

p.412 Javaの頻繁なセキュリティフィックスはクライアントに影響するものが大半
p.412 サーバーサイドの暗号ライブラリ関連バグもある:2014年のJSSEの深刻な問題[2]
p.413 安全でない再ネゴシエーション:2010年のJava 5u26/6u22で対応
p.413 BEAST攻撃:2011年のJava 6u29/7u1で対応
p.413 CRIME攻撃:JavaはTLS圧縮非対応だったので影響なし(WebアプリはCRIME亜種のHTTPレスポンスボディ圧縮攻撃TIME/BREACHに脆弱な可能性あり)

14.1.5 相互運用性の問題

p.413 さまざまなプロトコル、暗号スイートに対応しているのでサーバとしてのJavaはまず問題ない
p.413 クライアントとしてのJavaは潜在的な問題あり
p.413 ルート証明書の欠如:JREの定期更新orルート証明書の手動管理
p.414 256ビット暗号スイート限定サーバ:古いJREでは無制限モードが必要
p.414 1024ビット以上のDHパラメータ:Java8未満は1024ビットまでしか使えない
p.414 1024ビットに満たないRSA鍵:Java 7u40からデフォルトでは接続できなくなる
p.414 RC4:Java8u51からRC4が削除
p.414 MD2:Java7u40以降、MD2署名の証明書も拒否される
p.414 jdk.certpath.disableAlgorithmsプロパティ:安全でないアルゴリズムを無効にできる

14.1.6 プロパティによる調整

p.415 一覧はJSSEのドキュメントを参照
p.415 デフォルトのクライアントプロトコル:https.protocols, jdk.tls.client.protocols(Java8〜)
p.415 デフォルトの暗号スイート:https.cipherSuites
p.415 無効にする暗号スイート:jdk.tls.disabledAlgorithms
p.415 失効確認を有効化:com.sun.net.ssl.checkRevocation(デフォルト無効、別途OCSP/CRLを有効化必要)
p.416 jdk〜はセキュリティプロパティ(が多い):${java.home}/lib/security/java.securityファイルで定義される
p.416 他はシステムプロパティ:java -D~形式で指定 / System.setProperty("key", "value")
p.416 セキュリティプロパティをコマンドラインでオーバライド可能

# 設定ファイルでsecurity.overridePropertiesFile=trueとなっている
# 部分的にオーバーライドする場合
$ java -Djava.security.properties=/path/to/my/java.security-overrides
# 完全に置き換える場合(=が2個)
$ java -Djava.security.properties==/path/to/my/java.security

p.417 ランタイムでも変更可能(Security.setProperty("key", "value")):ただし、起動時のみ設定を読むクラスなどは変更できない

脚注
  1. 2016年くらいに変更になった。OpenJDKのケースIBM Javaのケース↩︎

  2. 記事によると、PKCS #1 v1.5 メッセージの処理においてパディングエラーが発生した際の動作を観察することにより、暗号文の解読を行うBleichenbacher攻撃が可能だったとのこと。5時間で2048ビットのRSA鍵を攻撃し、プリマスターシークレットを取得できたと書かれている。RFC 5246(TLS1.2)でも注意してねって書いてあるのに...Oracleでもやらかすんだな。 ↩︎

KIDANI AkitoKIDANI Akito

14.1.7 主なエラーメッセージ

p.417 JSSEのエラーは技術的で解決が難しい

■証明書チェーンの問題

p.417 検証できない証明書の時の例外

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
 PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
 unable to find valid certification path to requested target

p.417 未知のCAが原因:独自のCA、またはクライアントのトラストストアが古い
p.417 任意のルート証明書を追加することは推奨できない:なりすましの危険性
p.417 不完全な証明書チェーンが原因:JavaはデフォルトではAIA拡張(Authority Information Access)のURLを利用しない(com.sun.security.enableAIAcaIssuers=trueで利用可能)
p.418 自己署名証明書が原因:例外的に信頼して対処可(検証を無効化するのはNG)

■サーバのホスト名が合致しない

p.418 URLホスト名が証明書のホスト名と一致しない場合は下記の例外[1]

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
  No name matching xxx.example.com found
■クライアントにおけるDHの制限

p.418 Java8未満は1024ビットまでのDHパラメータのみ、Java8は2048ビットまで:対応不可の時の例外[2]

javax.net.ssl.SSLException: java.lang.RuntimeException:
  Could not generate DH keypair
...
Caused.by: java.secyrity.InvalidAlgorithmParameterException:
  Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)

p.418 対処法1:ECDHEを有効にし優先度を上げる(Java7以降利用可能)
p.418 対処法2:DHパラメータの強度を1024に下げる、かつ既知のDHパラメータは避ける(6.5節Logjam参照。事前計算攻撃。スノーデン事件。)
p.418 対処法3:DHE暗号スイートを無効化
p.419 対処法4:Bouncy CastleなどJCE以外のプロバイダーを利用(別の例外が出ることも)

■SNIの不寛容

p.419 SNI対応サーバ:一致する仮想ホストがない場合TLSアラートを送信
p.419 fatalでないので無視可能だがJava製クライアントは接続中止

javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
■安全な再ネゴシエーションの厳格モードの失敗

p.419 JVMには厳格モードがある:クライアント/サーバ双方が安全な再ネゴシエーション実装必要

javax.net.ssl.SSLHandshakeException: Failed to negotiate the use of secure renegotiation
■プロトコルのネゴシエーション失敗

p.419 SSL3.0のみのサーバと非対応クライアントの接続時のエラー

javax.net.ssl.SSLHandshakeException: Server chose SSLv3, but that protocol version
  is not enabled or supprted by the client.

p.420 サーバのバージョンが新しいもののみ

javax.net.ssl.SSLHandshakeException: Received fatal alert: protocol_version
■ハンドシェイクの形式に互換性がない

p.420 Java6未満のバージョン:SSL2.0のハンドシェイクがデフォルト

javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
脚注
  1. RFC9110HTTPではCommonNameでの検証が禁止になったが、JavaではまだCommonNameでも検証している(HostnameChecker.java)。HTTPでだけCommonName検証をしないってどうやって実装するんだろう... ↩︎

  2. 2016年のJava(jdk-9+115)以降だと8192ビットまで利用可能。 ↩︎

KIDANI AkitoKIDANI Akito

14.1.8 Java製のWebアプリケーションを安全にする

p.420 暗号処理のみ説明:クッキーやセッション管理は割愛

■暗号化を強制する

p.420 設定ミスなどによりHTTPでアプリを利用できてしまう
p.420 HttpServletRequest#isSecure() を使い、アプリで常に確認するのが吉
p.420 ソースコードを利用できないアプリの場合はサーブレットのフィルタに実装する

■Webアプリケーションのクッキーを安全にする

p.420 httpOnly属性とsecure属性の設定

Cookie cookie = new Cookie(name, value);
cookie.setMaxAge(days * 24 * 3600);
cookie.setHttpOnly(true);
cookie.setSecure(true);
// HttpServletResponse#addCookie()
response.addCookie(cookie);

p.421 サーブレットのフィルタでも実装可

■HTTPセッション用のクッキーを安全にする

p.421 Javaアプリはほぼ全てがセッション管理をサーブレットコンテナに依存=セッションクッキーを守る設定変更が必要
p.421 Servlet3.0以降の仕様ではhttpOnly+secure属性が導入されているので変更不要:それ以外は下記が必要

<session-config>
  <cookie-config>
    <secure>true</secure>
    <http-only>true</http-only>
  </cookie-config>
</session-config>
■HSTSのデプロイ

p.421 レスポンスヘッダの設定

response.setHeader("Strict-Transport-Security",
   "max-age=31536000; includeSubdomains; preload"); // 365日有効

p.421 通常セキュリティポリシーは自分でコードを書くよりもアプリレベルで実施すべき:サーブレットフィルタなど。

■クライアントサイドで強力なプロトコルを使う

p.422 Java製のクライアントアプリケーション:セキュリティを犠牲にして相互運用性を重視
p.422 例)JavaはSSL2.0に対応していないがJava 6ではSSL2.0形式のハンドシェイクを利用
p.422 HttpsURLConnectionクラスで利用するプロトコル:https.protocolプロパティで変更可能
p.422 ※Java8でSunJSSEを利用している場合はjdk.tls.client.protocolsプロパティでも変更可能
p.422 プログラムで実装する場合はSSLSocketFactoryを独自に生成し利用する必要がある

// 独自のSSLSocketFactoryで以下を呼び出して設定変更
SSLSocket#setEnabledProtocols()
SSLSocket#setEnabledCipherSuites()

// HttpsURLConnectionに設定
URL url = new URL("https://www.example.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(new MySSLSocketFactory());
■失効確認

p.424 Javaはデフォルトでは失効確認をしない
p.424 CRLとOCSP両方を有効にすべき

com.sun.net.ssl.checkRevocation=true
ocsp.enabled=true
com.sun.security.enableCRLDP=true

// 追加で不完全な証明書パスの再構築
com.sun.security.enableAIAcaIssuers=true

14.1.9 キーストアの主な操作

p.424 keytoolとOpenSSLで主に操作:GUIがよければKeyStore Explorer

■キーストアのレイアウト

p.425 Javaのプログラムは複数のキーストアを利用可能
p.425 ルート証明書の更新などのためJREは定期的にアップデートすべき
p.425 サーバではWebアプリごとにキーストアを利用するとよい:移行も楽

■鍵と自己署名証明書の生成

p.425 genkeypairコマンドで生成(Java7以前はgenkey)

$ keytool -genkeypair -keystore mystore.jks \
    -alias server \  // 証明書チェーンの別名
    -keualg RSA -keysize 3072 -validity 365
    -ext "DNS:www.example.com,DNS:example.com"
    // extオプションはJava7以降で利用可能
// キーストアのパスワードの入力に続いて、証明書情報各種を入力

p.425 keytoolの-storepassオプションでコマンドラインでパスワードを受け付けられるが履歴に残るためよくないので注意
p.426 キーストアの中身を確認

$ keytool -keystore mystore.jks -list -v
// キーストアのパスワードを入力すると証明書のエイリアス、生成日、拡張などが出力される
■CSRの生成

p.426 CSR生成コマンドはcertreq

$ keytool -certreq -keystore mystore.jks -alias server -file my.csr
// キーストアのパスワード入力

p.427 csrファイルをCAに送ると証明書が手に入る

■証明書のインポート

p.427 CAから証明書を受け取ったら証明書チェーンの構築に必要な証明書と一緒にインポート

// ルート証明書のインポート
$ keytool -import -keystore mystore.jks -trustcacerts -alias root -file root.crt
// 中間証明書のインポート
$ keytool -import -keystore mystore.jks -trustcacerts -alias intermediate1 \
  -file intermediate1.crt
// サーバー証明書のインポート
$ keytool -import -keystore mystore.jks -trustcacerts -alias server -file my.crt

p.427 keytoolはインポートした証明書が鍵と合致するか、チェーンが有効化どうか確認してくれる

■既存の証明書を変換する

p.427 Apacheからの移行:鍵と証明書ファイルそれぞれを1つのキーストアにマージが必要

// OpenSSLでpkcs12形式のキーストアに変換
$ openssl pkcs12 -export -out my.p12 -inkey my.key -in my.crt \
    -certfile my-intermediates.crt -name server

p.428 Javaのデフォルトの形式(JKS)ではないのでTomcatのパラメータ指定(keystoreType=pkcs12)or keytoolでJKSへ変換が必要[1]

■クライアントのルート証明書をインポートする

p.428 Javaで対応していないパブリックCAの発行した証明書のサイトもある
p.428 ブラウザでサイトに接続し、証明書ビューアで証明書を取得しキーストアにインポートする

$ keytool -import -keystore keystore.jks -trustcacerts -file newroot.crt -alias newroot
脚注
  1. Java 9以降はデフォルトがPKCS12になっている。JEP229 ↩︎

KIDANI AkitoKIDANI Akito

14.2 Tomcat

●TomcatのレベルではTLSを使わない方法

p.429 HTTPの処理はリバースプロキシとしてApacheに任せる:TomcatはJavaの処理に専念
p.429 ApacheはTomcat用にmod_proxy_ajpという専用モジュールがある

●JSSEを使う方法

p.429 TomcatでSSL終端したい場合の有力な選択肢:JSSE
p.429 Javaをインストールすれば使える反面、Java7以下では色々と制限がある

●APRとOpenSSLを使う方法

p.429 Tomcat Native = APR(Apache Portable Runtime) + OpenSSL(要はこの2つのラッパー)
p.429 パフォーマンスが改善する(らしい):反面、デプロイが複雑になる

p.430 TomcatとJSSEを組み合わせるためのコネクタ3種:BIO(ブロッキング)、NIO(非ブロッキング)、NIO2
p.430 Tomcat7系まではBIOがデフォルト:8系からはNIOがデフォルト
p.431 パフォーマンス重視:リバプロTLS終端 > Tomcat Native > JSSE
p.431 TLS機能:リバプロTLS終端 > Tomcat(Java8) / Tomcat Native > Tomcat(Java7)
p.431 Java8ではDHE暗号スイートで1024ビットセキュリティがデフォルト:適切でないのでjdk.tls.ephemeralDHKeySizeを2048にすべき
p.432 Java7以前では768ビットまでのDHEスイートのみ利用可能、安全でない
p.432 Java7以前ではサーバーが暗号スイート優先順位を決定できない:Java8でもTomcat7.0.61/8.0.21以降が必要
p.432 Java8からクライアント起源の再ネゴシエーションを無効化できる:jdk.tls.rejectClientInitiatedRenegotiationプロパティ
p.432 以上のようにJava8未満には問題があるのでリバプロorTomcat Nativeがオススメ

14.2.1 TLS処理の設定

p.432 コネクタのデフォルト設定はHTTP/1.1:APRコネクタ > BIOコネクタ(Tomcat7) or NIOコネクタ(Tomcat8〜)の順で選択される
p.432 本番環境では明示的に設定すべき

server.xml
// BIO
<Connector protocol="org.apache.coyote.http11.Http11Protocol" port="443 ... >
// NIO
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" port="443 ... >
// Tomcat Native (OpenSSL)
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol" port="443 ... >

p.433 TomcatはデフォルトではTomcat Nativeを使おうとする

server.xml
// Tomcat Nativeを無効にする
<Listener className="org.apache.catalina.core.AprLifecycleListener" 
  SSLEngine="off" ... >
■外部でのTLSの終端

p.433 mod_jk/mod_proxy_ajpでAJPプロトコル(Apache Jserv Protocol)でApache-Tomcatを繋ぐと、TLSの情報をApacheからTomcatへ透過的に伝達
p.433 それ以外は外部で終端する場合でも、TomcatでTLS設定必要:例)セッションクッキーのSecure属性

server.xml
<Connector scheme="https" secure="true" ... >

p.434 SSL Valve:TLSプロキシによって挿入された情報をリクエストヘッダから抽出し、SSLクライアントに関する情報をJavaで扱えるようにしてくれる

14.2.2 JSSEの設定

参考

server.xml
<!-- Define an SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           port="8443" maxThreads="200"
           scheme="https" secure="true" SSLEnabled="true"
           keystoreFile="${user.home}/.keystore" keystorePass="changeit"
           clientAuth="false" sslProtocol="TLS"/>

p.434 他に、sslEnabledProtocols、ciphers、sessionTimeoutなどが設定できる
p.435 TomcatはデフォルトではキャッシュされるTLSセッション数上限がない=DoS攻撃を招く恐れあり:sessionCacheSizeでメモリを設定すべき
p.435 暗号スイートについてはPFSのあるもの、強力な暗号のみを利用すべき:鍵交換はECDHEを使う、暗号化にRC4は含めない、など

■PFS

p.436 全てのPFS暗号スイートを利用して良いわけではない

  1. JSSEでは暗号スイートの明示的な選択が不可:IEのようなECDHEよりRSAを優先するクライアントもある
  2. DHEはPFSがあるがJava7では768ビットに制限されており安全でない
■Java8における設定

p.436 jdk.tls.ephemeralDHKeySizeで2048ビットを使うようにすべき(Java7で使えなかった強力なDHEスイートが利用できる)
p.436 jdk.tls.rejectClientInitiatedRenegotiationプロパティをtrueに
p.436 デフォルトの暗号スイートを使う:GCMが利用可能
p.436 サーバーサイドでの暗号スイート選択、仮想セキュアホスティングにも対応

14.2.3 APRとOpenSSLの設定

<!-- Define an SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
           protocol="org.apache.coyote.http11.Http11AprProtocol"
           port="8443" maxThreads="200"
           scheme="https" secure="true" SSLEnabled="true"
           SSLCertificateFile="/usr/local/ssl/server.crt"
           SSLCertificateKeyFile="/usr/local/ssl/server.pem"
           SSLVerifyClient="optional" SSLProtocol="TLSv1.2"
           SSLDisableCompression="true"
/>

p.437 TLSのセッションキャッシュを抑制する方法がなく懸念事項

■OpenSSLのグローバルな設定

p.438 server.xmlでAprLifecycleListenerの設定を変更する

<Listener className="org.apache.catalina.core.AprLifecycleListener"
  SSLEngine="on" SSLRandomSeed="builtin"
  FIPSMode="off" />

p.438 ハードウェアアクセラレーション対応のOpenSSLではエンジン名を指定可能:参考

<Listener className="org.apache.catalina.core.AprLifecycleListener"
          SSLEngine="someengine" SSLRandomSeed="somedevice" />
KIDANI AkitoKIDANI Akito

第15章 Microsoft WindowsおよびIISの設定

p.439 MSのOSの入った端末(デスクトップ、モバイル)は至るところに:SSL/TLS、PKIのエコシステムの中心的存在
p.439 歴史が長い:複雑で、ドキュメントも不十分(存在しない、見つけづらい、不正確)

15.1 Schannel

p.439 Secure Channel:MSのSSL/TLS実装、Windows用プログラムが多数これに依存

15.1.1 機能の概要

p.439 Schannelはプロトコルの機能によく対応している:例)TLS1.2対応は2009年Windows 7(OpenSSLは2012年)
p.439 ただ、MSはTLS1.2をデフォルトで無効にしていた:2013年11月にデフォルトで有効(IE11)
p.440 最大の問題:Windows XP(2001年〜)がSNI(2003年〜)非対応(SPでもサポートされず)
p.440 Schannel以外にも複数の層が暗号処理に関与:暗号処理の制限の原因特定が困難
p.440 例)Windows 8は3072ビットDSA鍵に対応しているが、IEでは1024ビットを超える鍵のサーバに接続拒否(おそらくSchannel起因)
p.440 Windows Vista(2006)〜:楕円曲線暗号、クライアントサイドSNI、TLS1.0、AES暗号スイート
p.440 Windows 7(2009)〜:TLS1.1、TLS1.2、GCMスイート(限定的)、1024bit以上のDHパラメータ、OCSPステープリング
p.440 Windows 8(2012)〜:サーバーサイドSNI、セッションチケット(クライアント)
p.440 Windows 8.1(2013)〜:セッションチケット(サーバ)、ALPN(クライアント)

15.1.2 プロトコルの脆弱性

p.441 安全でない再ネゴシエーション:2010年2月無効化、8月安全な再ネゴシエーション(RFC5746)対応
p.441 BEAST:2012年1月に修正(TLS1.0では1/n-1分割を行う)
p.441 CRIME:SchannelではTLS圧縮に非対応なので影響なし
p.441 Logjam:2015年5月にIEで最低でも1024ビットのDHパラメータ利用

15.1.3 相互運用性の問題

p.441 Schannelの相互運用性問題は多くない:弱くて既に廃止された暗号技術の問題が大半
p.442 DSA:1024ビットより強力な鍵に未対応だが、インターネット上にDSA鍵のサーバが事実上存在しないので問題ない
p.442 1024ビットより強力なDHパラメータ:インターネットではあまり使われず問題なし
p.442 1024ビット未満のRSA鍵:2012年10月の更新で非推奨に(プライベートCA含む)
p.442 MD5:2013年8月の更新で非推奨(WindowsVistaなど:Windows 8.1などは最初からMD5拒否)(プライベートCAは影響なし)
p.442 RC4:他に先んじて非推奨に。Windows8.1(2013)以降デフォルトでは無効。2013年11月の更新でWindows8以前でも無効化できるように。
p.443 RC4のみのサイト:MSの調査では2013年11月に3.9%、SSL Pulseでは2014年7月に1.8%
p.443 IE11でRC4のみのサイトに接続:SSL3.0へのダウングレードorSSL3.0非対応サーバの場合接続不可
p.443 SHA1:2013年11月に2016年末までに非推奨とする計画発表[1]

15.2 Microsoftルート証明書プログラム

p.443 Vista以降はOSで必要な少数のみが同梱:それ以外のルート証明書は初めて遭遇した際にMSのサイトから随時取得
p.444 Windows XPには同様の仕組みなし:Microsfoft Updateから手動ダウンロードでシステム更新が必要

15.2.1 システムのトラストストアの管理

p.444 最新のWindows:信頼された証明書の更新は1週間に1回、必要なルート証明書は都度ダウンロード、MSのブラックリスト証明書は毎日更新(参考
p.444 Windowsは複数トラストストア利用可能:サービスやユーザーごとに分離可能
p.444 変更する場合はMMC(Microsoft Management Console)を利用して管理

15.2.2 信頼された証明書のインポート

p.444 .cerファイルから証明書のインポートウィザードを起動して証明書インストール可能
p.445 CAを信頼する前にセキュリティに影響がないか十分な検討が必要

15.2.3 信頼された証明書のブラックリスト

p.445 信頼された証明書の自動更新があるので、手動でのストアからの削除をしても無駄
p.445 ブラックリスト:「信頼されていない証明書」というストアに追加が必要

15.2.4 ルート証明書の自動更新を無効にする

p.445 gpedit.mscを実行:ローカルグループポリシーエディター > コンピュータの構成 > 管理用テンプレート > インターネット通信の管理 > インターネット通信の設定 > ルート証明書の自動更新をオフにする

脚注
  1. 2019年以降はコード署名もSHA1を廃止。参考 ↩︎

KIDANI AkitoKIDANI Akito

15.3 設定

p.445 WindowsはGUI指向だがSchannelのSSL、暗号スイート関連の設定はレジストリ直接編集
p.445 例外:IISには鍵と証明書を操作するインタフェースあり
p.445 Schannelの設定を変更してもFirefoxやChrome、Javaなどは影響を受けない[1]

15.3.1 Schannelの設定

p.446 プロトコルをクライアントアプリとサーバーアプリ用に設定可能[2]

■プロトコルの設定
レジストリのキー
// regeditまたはPowerShellで変更
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols
// サブキーとして以下がある(他のプロトコルバージョンも同様)
TLS1.2
TLS1.2\Client
TLS1.2\Server
TLS1.2を無効化する(Enabled=0)
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'DisabledByDefault' -value 1 -PropertyType 'DWord' -Force | Out-Null

p.446 DisabledByDefault=0にするとデフォルトで有効にできる(1は無効)
p.447 レジストリ変更後は影響するプログラムの再起動が必要

■アルゴリズムの選択による暗号スイートの設定

p.447 弱いアルゴリズムを個別に無効化できる(暗号スイートとしても利用できなくなる)

RC4 の無効化
([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$env:COMPUTERNAME)).CreateSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128')
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null

([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$env:COMPUTERNAME)).CreateSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128')
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null

([Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$env:COMPUTERNAME)).CreateSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128')
New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null

p.447 Schannel\KeyExchangeAlgorithms\PKCS:鍵交換のRSAの有効/無効を設定できる(認証のRSAは利用可能)
p.447 Schannel\Hashes\MD5, SHA, SHA256:暗号スイートのハッシュの制約。証明書の署名については後述。

15.3.2 具体的な暗号スイートの設定

p.447 Windows Vista以降:クライアント、サーバの両方で暗号スイート優先順位を設定可能
p.448 Schannelで唯一GUI(gpedit.msc)で設定が可能[3]:コンピューターの構成->管理用テンプレート->ネットワーク->SSL構成設定->SSL暗号の順位(参考
p.448 ポリシーエディタは1023文字まで編集可能:以降は切り捨てられるので注意
p.448 Schannelの暗号スイート名一覧
p.449 標準的な命名に加えて、P256=secp256r1、P384=secp384r1など楕円曲線を明示できるようになっている

p.449 ECDHE_RSA_AES_128_GCM:2015年7月時点ではWindows 10で対応、サーバーは非対応(Schannelの暗号スイート名一覧によると、Windows Server 2016 以降は対応している模様)
p.449 レジストリによる暗号スイートの設定キーは以下の通り(暗号スイートをカンマ区切りで指定)

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002

15.3.3 鍵と署名の制限

p.450 Windows 8.1/Windows Server 2012 R2〜:証明書チェーン検証時に弱いアルゴリズムを制限

  • 弱い暗号アルゴリズムの無効化
  • 鍵アルゴリズムの最小の鍵長を強制
  • 証明書の種類ごとのポリシー適用(サーバー認証用とコード署名用、など)
  • パブリックCAのみに適用するポリシー設定可能
  • ポリシーを適用する証明書発行日を指定可能
  • ポリシー違反のログ(ロギングのみでエラーにしないことも可能)
  • 証明書ごとの例外指定

p.450 グループポリシーオブジェクトを利用して他のユーザーにもポリシーを適用可能
p.450 レジストリキー(参考

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\OID\EncodingType0\
    CertDllCreateCertificateChainEngine\Config
// 設定書式
Weak<CryptAlg><ConfigType><ValueType>
// 設定例:MD5をパブリックCAについて特定の時間以降の署名について制限
WeakMd5ThirdPartyAfterTime

p.451 CryptAlg:Md5、Sha1、Dsa、Ecdsa、Rsa
p.451 ConfigType:ThirdParty、All(Allで設定したものはThirdPartyにも影響
p.451 ValueType:Flags(後述)、MinBitLength(鍵アルゴリズムのビット長)、AfterTime、Sha256Allow(明示的許可する証明書のSHA256署名)
p.451 ルールを有効化するフラグと、ルールの適用対象証明書を制御するフラグがある
p.451 有効化フラグ

  • CERT_CHAIN_ENABLE_WEAK_SETTINGS_FLAG(0x80000000):ポリシーを有効化
  • CERT_CHAIN_ENABLE_WEAK_LOGGING_FLAG(0x00000004):違反エラー時のログ
  • CERT_CHAIN_ENABLE_ONLY_WEAK_LOGGING_FLAG(0x00000008):違反をエラーにしないがログ記録

p.452 証明書制御フラグ

  • CERT_CHAIN_DISABLE_ALL_EKU_WEAK_FLAG(0x00010000):全証明書に適用
  • CERT_CHAIN_DISABLE_SERVER_AUTH_WEAK_FLAG(0x00100000):サーバ証明書
  • CERT_CHAIN_DISABLE_CODE_SIGNING_WEAK_FLAG(0x00400000):コード署名
  • CERT_CHAIN_DISABLE_MOTW_CODE_SIGNING_WEAK_FLAG(0x00800000):コード署名をWeb証明書と同様に扱う
  • CERT_CHAIN_DISABLE_TIMESTAMP_WEAK_FLAG(0x04000000):タイムスタンプ証明書
  • CERT_CHAIN_DISABLE_MOTW_TIMESTAMP_WEAK_FLAG(0x08000000):TS証明書をWeb扱い
■暗号関連のポリシーをCertUtilで操作する

p.452 CertUtilコマンドでレジストリを生成、変更、削除可能

CertUtilでMD5をパブリックCAについて2010年以降無効化
$ Certutil -setreg chain\WeakMD5ThirdPartyAfterTime  @1/1/2010

p.453 レジストリへの変更時にはバックアップをとっておくべし

■弱い証明書チェーンを記録する

p.453 WeakSignatureLogDirキーを設定してログを残せる

$ Certutil -setreg chain\WeakSignatureLogDir C:¥Log¥WeakCertificateChains
■ポリシーの実例
// PowerShellでMD5制限を有効化、対象は全証明書、ログをのこすがエラーにしない
$ Certutil -setreg chain\WeakMd5AllFlags 0x80010008

// レジストリファイルの場合
"WeakMd5AllFlags"=dword:80010008

p.454 設定が確認できたら、ポリシーを強制する:80010004に変更

脚注
  1. 本文中にChromeのルート証明書がWindowsのものを利用するとあるが、Chrome 105(2022年8月)からChrome Root Storeの利用が始まっている。参考。システムストアを見る設定もv111までは残されている予定↩︎

  2. 参考:クライアントの SSL/TLS プロトコルと暗号スイートのAD FSを管理する ↩︎

  3. Windows 7 Home Premiumにはgpedit.mscは含まれないらしい ↩︎

KIDANI AkitoKIDANI Akito

15.4.3 再ネゴシエーションを設定する

p.454 セキュリティ更新プログラムMS10-049:Windows8以前で安全な再ネゴシエーションに必要なパッチ
p.454 上記パッチは相互接続性のため互換性のある再ネゴシエーション(安全でない)が可能
p.454 サーバーで再ネゴシエーションを無効にしていれば問題ない(KB 977377)

// 0以外を設定すると無効化できる
HKEY_LOCAL_MACHINE¥System¥CurrentControlSet¥Control¥SecurityProviders¥
    SCHANNEL¥DisableRenegoOnServer

p.455 IIS 6以降ではクライアント起源の再ネゴシエーションを許可しない
p.455 サーバーで安全な再ネゴシエーションが必要な場合

// 0を設定すると安全な再ネゴシエーションのみが有効に(再起動不要)
HKEY_LOCAL_MACHINE¥System¥CurrentControlSet¥Control¥SecurityProviders¥
    SCHANNEL¥AllowInsecureRenegoClients

p.455 安全な再ネゴシエーションに対応していないサーバ:2014年7月時点で約11.6%
p.455 クライアントの再ネゴシエーションを無効にすることも可能:ただし、安全でない再ネゴシエーションはサーバーを騙す攻撃なのであまり意味はない

15.3.5 セッションキャッシュを設定する

p.456 Schannelのレジストリキーでセッションキャッシュの以下のエントリーで設定可能

  • ServerCacheTime:セッションの保持期間(ミリ秒)
  • ClientCacheTime:クライアントのセッション保持期間(基本変更不要)
  • MaximumCacheSize:セッションの最大数(0でキャッシュ無効)

p.456 1セッションで2K〜4Kバイト
p.456 ServerCacheTimeに応じて周期的にセッションが削除される:指定したメモリ量を超えても削除はされない
p.456 DoS攻撃の材料になるので、セッション保持期間を1時間程度にすべき(通常他のライブラリでは24時間を推奨)
p.456 Windows 8.1以降サーバとしてセッションチケットに対応しているがドキュメントがない[1]

15.3.6 セッションキャッシュの監視

p.457 セッションキャッシュやセッションリザンプション成功率の監視に使えるパフォーマンスカウンタがある
p.457 Windowsのパフォーマンスモニターで閲覧可能(perfmonコマンド)
p.457 Active Schannel Session Cache Entries:現在利用されているセッションキャッシュ数
p.457 Schannel Session Cache Entries:セッションキャッシュ数
p.457 SSL Client-Side Full Handshakes:クライアントで1秒に処理されるフルハンドシェイク数
p.457 SSL Client-Side Reconnect Handshakes:クライアントで1秒に処理される再接続ハンドシェイク数
p.457 SSL Server-Side Full Handshakes:サーバで1秒に処理されるフルハンドシェイク数
p.457 SSL Server-Side Reconnect Handshakes:サーバで1秒に処理される再接続ハンドシェイク数

15.3.7 FIPS 140-2

p.457 FIPS:Federal Information Processing Standards(NISTによる政府の非軍事システム向け標準
p.457 FIPS 140-2:暗号に関するガイドライン。以降これを指して単にFIPSと表記。
p.458 Windows上で5つの階層でFIPSが実装される

  • 下位ライブラリ:CAPI[2]とCNG[3]がFIPS対応。ただし承認されていないアルゴリズムもあるので注意
  • FIPSレジストリインジケーター:レジストリキー。あるアプリがFIPSに準拠するかどうかの設定。
  • 上位のライブラリ:SchannelやMicrosoft .NET Frameworkは上記レジストリキーを読み動作。
  • OSの構成要素:Remote Desktop Protocol、ファイルシステム暗号化(EFS、BitLocker)、IPSecなど
  • アプリケーション:実際にFIPS準拠する責任を負うのはアプリ。下位ライブラリを利用する場合はFIPS準拠の保証が必要
■FIPSを設定する

p.458 ローカルセキュリティポリシーの画面で設定可能

  • secpol.mscを起動:ローカルポリシー > セキュリティオプション
  • 「システム暗号化:暗号化、ハッシュ、署名のためのFIPS準拠アルゴリズムを使う」を有効にする
  • システムを再起動して反映する

p.459 レジストリを直接編集して設定も可能

// Windows Vista以降:1が有効、0が無効
HKEY_LOCAL_MACHINE¥System¥CurrentControlSet¥Control¥Lsa¥FIPSAlgorithmPolicy¥Enabled

15.3.8 サードパーティのユーティリティ

p.460 Nartac Software社のIIS Crypto:IIS設定のためのユーティリティ

  • GUIでプロトコルや暗号スイートの要素技術の有効化や順序変更が可能
  • 設定のテンプレートが利用可能
  • SSL Labsのスキャンも実行可能
脚注
  1. 軽く検索してみたが2022年11月時点でもドキュメントはなさそう。 ↩︎

  2. Cryptographic API ↩︎

  3. Cryptographic API: Next Generation ↩︎

KIDANI AkitoKIDANI Akito

15.4 ASP.NETのWebアプリケーションを安全にする

15.4.1 SSLの利用を強制する

p.461 設定ミスを防止するためにアプリ側でリクエストをチェックすべき

if (Request.Url.Scheme.Equals("https") == false) {
  // Error, access without SSL
}

p.461 実行するプログラムごとに実装するのではなく、認証フィルタを利用すべき

15.4.2 クッキーを安全にする

p.461 アプリケーションで利用するクッキーは個別に設定必要

HttpCookie cookie = new HttpCookie();
cookie.Name = "CookieName";
cookie.Value = "CookieValue";
cookie.Expires = DateTime.Now.AddMinutes(10d);
cookie.HttpOnly = true;
cookie.Secure = true;
Response.Cookies.Add(cookie);

15.4.3 セッション用のクッキーとフォーム認証を安全にする

p.461 ASP.NETの設定ファイルの<httpCookies>要素でセッションクッキーを設定

<configuration>
  <system.web>
    <httpCookies domain="www.example.com"
      httpOnlyCookies="true"
      requireSSL="true"
      lockItem="true" // 設定ファイルの他でオーバーライドされないための設定
    />
  </system.web>
</configuration>

p.462 フォーム認証利用時は<forms>要素のrequireSSLもtrueに設定する必要あり

<forms requireSSL="true"
  cookieless="UseCookies"
  // セッションIDの管理にクッキーを利用する
  // URIベースの方式もあるが他のサイトに取られるのでNG
  ...
/>

15.4.4 HSTSのデプロイ

p.462 HSTS:アプリケーションのミスでSecureでないクッキーがあっても防げる

// アプリ側で設定
Response.AppendHeader("Strict-Transport-Security",
  "max-age=31536000; includeSubDomains; preload");

// 設定ファイル
<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Strict-Transport-Security"
                  value="max-age=31536000; includeSubDomains; preload" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

15.5 IIS

p.463 IIS:Internet Information ServicesはWindows用Webサーバ、SSL/TLSはSchannelに依存
p.463 最大の問題:IISにSSL設定のUIがない、Schannelに完全に依存

●PFS

p.463 ECDHE鍵交換を利用可能
p.463 楕円曲線に対応していないクライアント向けに2014年DHE_RSAスイートを追加(Win8.1/WinServer2012)したが、GCMを利用しており古いクライアントでは結局利用できない...
p.463 そもそもDHパラメータが1024ビットと弱いのも問題

●GCM暗号スイート

p.463 本書執筆時点ではGCMのみが安全なスイート
p.464 SchannelではGCMをECDSA鍵と主に組み合わせて使う
p.464 ただこの鍵に対応していないクライアントもあるので問題(例:Wikipediaへの2015年6月のアクセスで6%が非対応)

●OCSPステープリング

p.464 Windows Server2008以降IISでOCSPステープリングがデフォルト有効
p.464 ただしIISがCAと通信できるファイアウォールの設定が必要

●サイトごとの設定

p.464 IISではサイトごとにSSL/TLSの設定はほとんどできない
p.464 サイト全体に適用できるようにするしかない:FIPS準拠などでは制約になる

15.5.1 鍵と証明書の運用

p.464 IISはGUIで鍵と証明書を操作可能:UI上「証明書」とされているのは「鍵」と「証明書」がほとんど

■IISの管理コンソールをカスタマイズする

p.464 Microsoft管理コンソール(MMC):ファイル名を指定して実行 -> mmc

■IIS証明書の管理

p.464 IIS管理コンソールで設定可能(本書ではWindows Server 20012をベースに説明)
p.466 自己署名入り証明書も設定可能:証明書ストアは個人、Webホスティングが選べる(差は不明)
p.466 CA発行の証明書もインポート可能:PKCS#12(PFX)形式のみ対応(他の形式の場合はOpenSSLなどで変換が必要)
p.466 インポート時に「証明書のエクスポートを許可」にチェックがついていると鍵も一緒に取り出されるので注意
p.466 CA証明書の取得のためにCSRの作成も可能:鍵としては本書執筆時点ではRSAまたはDHのみ[1]
p.466 CSR送付後、ドメインの検証が終わると証明書が発行される
p.467 CAからサーバー証明書と中間証明書と場合によってルート証明書が送られてくる
p.467 中間証明書・ルート証明書をそれぞれのストアにインポートするとサーバー証明書をインポートできる

■SSL/TLSサイトの設定

p.467 ユニークなIPアドレスごとにサイトを提供する:グローバルIPがたくさん必要
p.468 ワイルドカード証明書などで複数サイトを1つのIPで提供する:SNIは無効にしておく
p.468 SNIを利用して仮想セキュアホスティングも可能:IIS 8以降

■高度なオプション

p.468 IIS 8以降:鍵と証明書を共有ファイルにしてWebサーバを容易にクラスタ化可能

脚注
  1. GlobalSignの説明によれば、Windows Server 2008でECDSA鍵の証明書も作成可能とのこと。 ↩︎

KIDANI AkitoKIDANI Akito

第16章 Nginxの設定[1]

p.469 比較的最近のプロジェクトなので新しいTLS機能にも対応

  • 楕円曲線暗号のサポート
  • OCSPステープリング
  • バックエンドでの証明書検証
  • チケット鍵の設定

※本書では1.4系、1.6系、1.8系が比較されているが、2022年12月現在の最新の安定版は1.22.1

16.1 OpenSSLソースコード利用のNginxのインストール

p.469 Nginx:インストール時にシステムのOpenSSLと動的リンク
p.470 システムOpenSSLが古い場合、--with-opensslオプションを指定して静的ビルド可能

./configure --prefix=/opt/nginx --with-openssl=../openssl-1.0.1h \
    --with-openssl-opt="enable-ec_nistp_64_gcc_128" --with-http_ssl_module

16.2 TLSを有効にする

p.470 listenディレクティブにsslパラメータを設定する[2]

server {
  listen 443 ssl;
  server_name www.example.com;
}

16.3 TLSプロトコルの設定

p.471 ssl_protocols:TLSv1.2、TLSv1.3などの値を設定する
p.471 ssl_prefer_server_ciphers:利用する暗号スイートをサーバーが決める
p.471 ssl_ciphers:暗号スイートと優先順位を制御(OpenSSLと同じ文字列で設定)

16.4 鍵と証明書の設定

p.471 ssl_certificate_key:サーバの秘密鍵の設定
p.471 ssl_certificate:サーバ証明書と中間証明書を含める
p.472 ssl_password_file:v1.7.3以降。秘密鍵を設定ファイルのパスワードで保護できる

16.5 複数の鍵を設定する

※2018年には複数鍵の設定方法を解説したブログが出ている

脚注
  1. Nginxの設定についてはマスタリングNginxも詳しい ↩︎

  2. 参考:https://github.com/kdnakt/aws-alb-ecs-ssl-badcert/blob/main/docker/ssl.conf ↩︎

KIDANI AkitoKIDANI Akito

16.6 ワイルドカード証明書とマルチサイト証明書

p.472 2つ以上のサイトで証明書を共有して同一IPでデプロイは容易
p.472 特別な設定は不要:TLS終端とHTTPのホスト選択は別処理なので

nginx.confで同じ証明書を使う
ssl_certificate server.crt;
ssl_certificate_key server.key;

server {
    listen 443 ssl;
    server_name site1.example.com
    ...(他の設定)...
}

server {
    listen 443 ssl;
    server_name site2.example.com
    ...(他の設定)...
}

16.7 仮想セキュアホスティング

p.473 仮想セキュアホスティング:複数の無関係なサイトが異なる証明書で1つのIPアドレスを共有
p.473 古いクライアント(主に2010年以前、Windows XPやJava 6など)はSNI非対応:デフォルトの証明書が表示される
p.473 HTTPのホストヘッダに該当するホスト名がない場合はデフォルトのサイトが表示される

16.8 エラーメッセージ用にデフォルトのサイトを使う

p.473 不正なホスト名で検索エンジンにインデックスされたり、別ホストにセキュリティの問題が波及したりするのを避けるため、デフォルトサイトは常にエラーメッセージのみとすべき

server {
    listen 443 ssl default_server:
    # server_nameの設定は不要
    root /path/to/site/root;
    location / {
        return 404;
    }
    location /404.html {
       internal;
    }
    error_page 404 ./404.html;
}

p.474 本書執筆時点(2016年ごろ)ではNginxは厳格なSNIに非対応
p.474 1.7.0で導入された$ssl_server_name変数でSNIホスト名を取得可能:空の場合非対応クライアントとしてエラーを提示可能

※2020年10月27日にリリースされたNginx 1.19.4でssl_reject_handshakeディレクティブが導入され、厳格なSNIを実現できるようになった[1]

server {
    listen               443 ssl default_server;
    ssl_reject_handshake on;
}

server {
    listen              443 ssl;
    server_name         example.com;
    ssl_certificate     example.com.crt;
    ssl_certificate_key example.com.key;
}

16.9 PFS

p.474 2011年8月のバージョン1.1.0からDHE/ECDHEに完全対応
p.474 ただし利用するOpenSSLのバージョン依存(OpenSSL 1.0.1が必要)
p.474 OSのバージョンによっては楕円曲線の特許回避のため楕円曲線非対応(2013年以前のFedora,RHEL)

16.10 OCSPステープリング

p.475 2013年リリースのNginx 1.4系でOCSPステープリングに対応
p.475 起動時にはOCSPレスポンスを取得せず、初回アクセス時にOCSPリクエストを実行、かつワーカープロセスごとに取得が必要(後述の方法で手動設定可能)

16.10.1 OCSPステープリングの設定

ssl_stapling on;
# ドメイン名からIPアドレスを取得するためのDNSリゾルバの設定
resolver 127.0.0.1;
# レスポンスの検証はデフォルトでは無効
ssl_stapling_verify on;
# OCSPレスポンス検証に必要な証明書チェーン:中間+ルート
ssl_trusted_certificate trusted-for-ocsp.pem;

p.476 ステープリングのキャッシュやタイムアウトの設定はない
p.476 キャッシュはワーカーごとに必要に応じてメモリが拡張される
p.476 タイムアウトはハードコード:有効レスポンスは1時間、エラーは5分、ネットワークタイムアウトは60秒

16.10.2 独自のOCSPレスポンダを使う

p.476 通常は証明書にハードコードされたレスポンダを利用する
p.476 直接外に出るトラフィックが許可されない場合or証明書にレスポンダURLがない場合に、以下の設定を行う

ssl_stapling_responder http://ocsp.example.com;

16.10.3 手動でOCSPレスポンスを設定する

p.476 DER形式のOCSPレスポンスをファイルに格納して設定可能

ssl_stapling_file ocsp-response_www.example.com.der;

p.477 レスポンスの取得はOpenSSLなどで実行

$ openssl x509 -in server.crt -noout -ocsp_uri
(OCSPレスポンダのURLが出力される)

$ openssl ocsp -issuer issuer.crt -cert server.crt -url (上で出力されたURL) \
  -noverify -respout ocsp-res.der
server.crt: good
    This Update: Jan 10 ~
    Next Update: Jan 17 ~

p.477 本番利用の際はOCSPレスポンスを最新に維持する仕組みが必要(Nginxの再起動含む)

脚注
  1. チケットのオープンは2012年なので8年越しに機能が実装された。 ↩︎

KIDANI AkitoKIDANI Akito

16.11 DHE鍵交換の設定

p.477 デフォルトの鍵サイズは1024ビットで強力ではない(これはOpenSSL由来)

# opensslでパラメータを生成
$ openssl dhparam -out dh-2048.pem 2048

# nginx.confで強力なDHパラメータを指定可能
ssl_dhparam dh-2048.pem

p.478 2048ビットにするとJava6やJava7のクライアントでは問題が発生しうる(スイート順序で回避可能)

16.12 ECDHE鍵交換の設定

p.478 デフォルトの鍵サイズは256ビット(128ビット安全性)のsecp256r1曲線(3072ビットRSA鍵相当)

# 192ビット安全性の楕円曲線も指定可能(7680ビットのRSA鍵相当)
ssl_ecdh_curve scep384r1;

p.478 OpenSSLは多様な曲線をサポート:ブラウザは上記2種類をメインにサポート[1]
p.478 OpenSSLはsecp256r1を最適化しているがsecp384r1は高速でない

16.13 TLSセッションの管理

p.478 Nginxは分散環境のセッションキャッシュには非対応[2]

16.13.1 スタンドアロンのサーバにおけるセッションキャッシュ

p.478 共有メモリにキャッシュを設定し、TLSセッションを全ワーカプロセス間で共有(デフォルトでは無効)

nginx.conf
# およそ4000セッション分
# sharedの代わりにbuiltinを指定してワーカプロセスごとに固有のキャッシュを持たせることもできる
ssl_session_cache shared:ssl_session_cache:1M;
# 有効期間24時間(デフォルトは5分)
ssl_session_timeout 1440m;

p.479 セッション共有は同一のアプリケーションであり、証明書を共有している場合だけ(参考:6.8節

16.13.2 スタンドアロンのサーバにおけるセッションチケット

p.479 OpenSSLが処理するのでNginxの設定は不要
p.479 起動時に128ビット(固定)の使い捨てのAES鍵で保護(複数鍵も可能、後述)
p.479 固定のため再起動時にTLSのネゴシエートが必要:チケットの保護のため定期的な再起動推奨
p.479 サイトごとに異なるセッション鍵を割り当てるべき

16.13.3 分散環境におけるセッションキャッシュ

p.480 2017年時点で未対応:2022年12月時点でもおそらく同様(ドキュメントには未記載)
p.480 クラスタを組む際は同一ノードにクライアントが転送されるようスティッキーな設計が必要

16.13.4 分散環境におけるセッションチケット

p.480 ssl_session_ticket_keyディレクティブで鍵共有、ローテーションが実現可能

# OpenSSLで鍵ファイルを生成
$ openssl rand -out ticket.key 48

# nginx.conf
ssl_session_ticket_key ticket.key;
# 復号にのみ利用するローテーション前の鍵
ssl_session_ticket_key old-ticket.key;

16.13.5 セッションチケットを無効にする

ssl_session_tickets off;

16.14 クライアント認証

ssl_verify_client on;
# クライアント証明書から信頼されたルート証明書までのパスの最大深さ
ssl_verify_depth 2;
# クライアント証明書を発行する中間証明書
ssl_client_certificate sub-ca.crt;
# ルート証明書
ssl_trusted_certificate root-ca.crt;
# 失効した証明書リスト(変更には再起動)
ssl_crl revoked-certificates.crl;

p.482 ssl_verify_client optional:クライアント証明書を任意とする。証明書がない場合、$ssl_client_verify変数がNONEとなる(ブラウザによってはWebサイトが動作しなくなるので注意が必要)

参考:[Nginx]特定の画面だけでクライアント認証する

利用例
        location / {
            if ($ssl_client_verify != SUCCESS) {
                # クライアント認証に失敗していたら403エラーページを返却する
                return 403;
            }
            try_files $uri $uri/ /index.php?$query_string /index.html;
        }

p.482 ssl_verify_client optional_no_ca:証明書の検証をスキップし、外部サービスに$ssl_client_certを経由して依頼[3]

脚注
  1. 2018年の記事があった。 ↩︎

  2. Apacheはmemcachedを用いた分散キャッシュにも対応していた ↩︎

  3. StackExchangeを見ていると、HTTPヘッダに渡すためにこの変数を利用していた。ただしそのままでは改行の問題などがあるため、 proxy_set_header X-SSL-CERT $ssl_client_escaped_cert; のように $ssl_client_escaped_cert変数を利用するとPEM形式での証明書が取得できるとのこと。 ↩︎

KIDANI AkitoKIDANI Akito

16.15 プロトコルの問題への対策

p.482 場合によっては公式発表前に、迅速に対応されている

16.15.1 安全でない再ネゴシエーション

p.482 Nginxは2009年11月の発券から1週間以内にv0.8.23で対応:クライアント起源の再ネゴシエーションは不可
p.483 サーバ起源の再ネゴシエーションも利用しない(OpenSSLが対応していても拒否)

16.15.2 BEAST

p.483 厳密に言えばサーバとクライアント両方に影響があるが、暗号化データを攻撃者が制御する必要があるため実際にはブラウザのみ:Nginx側で対処できることはない

16.15.3 CRIME

p.483 2012年に発見された、TLS1.2までのプロトコルでは対処されていない問題[1]
p.483 Nginxはパフォーマンス上の理由で2011年から圧縮を無効化(OpenSSL1.0.0以上のみ:Nginx 1.0.9,1.1.6)
p.483 全てのバージョンのOpenSSLで圧縮が無効化されたのは2012年Nginx1.2.2、1.3.2

16.16 HSTSのデプロイ

server {
  listen 192.168.0.1:80;
  server_name www.example.com;
  # 80ポートへのアクセスを443へリダイレクト
  return 301 https://www.example.com$request_url;
  ...
}
server {
  listen 192.168.0.1:442;
  server_name www.example.com;
  # 有効期間1年のHSTSをセット(2XX〜3XXのレスポンスのみヘッダ追加)
  add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
  ...
  # 子のブロックには継承されないので別途add_headerが必要
  location /foo {
      add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload";
      ...
  }
}

16.17 TLSバッファの調整

p.484 バージョン1.5.9以降:ssl_buffer_sizeディレクティブ(デフォルト16Kバイト)
p.484 バッファを1400バイトにすると初期遅延が改善するが、スループットは悪化

16.18 ロギング

p.484 以下の理由でTLS運用状況を監視すべき

  • パフォーマンス:セッションリザンプションの成功率($ssl_session_reused変数で追跡可能)
  • プロトコル・暗号スイート利用状況:設定が正しいか、古い機能が使われていないか
nginx.confでTLS接続用ログを出力
# $ssl_session_idはNginx1.5.9以前ではバグにより利用できない
log_format ssl "$time_local $server_name $remote_addr $connection \
  $connection_requests $ssl_protocol $ssl_cipher $ssl_session_id \
  $ssl_session_reused";

access_log /path/to/ssl.log ssl;

p.486 ハンドシェイクの失敗はロギングできない

脚注
  1. TLS1.3ではClientHelloのlegacy_compression_methods に0以外の値が入っていた場合はillegal_parameterアラートでハンドシェイクを終了すること、とされている。 ↩︎

このスクラップは2023/01/14にクローズされました