要約
- TLS 1.3の
close_notifyは、送信者がその接続でこれ以上TLSメッセージを送らないという、認証された一方向の宣言である。反対方向を閉じず、要求、応答、ストリーム、永続的な副作用の受領確認にもならない。 - アラートなしにトランスポートEOFが来れば、切断による欠落の可能性が残る。上位プロトコルが独立した完全性規則を持ち、アプリケーションが実際に検査した場合に限り、互換動作として扱える。
- 再試行を認める証拠は、アプリケーションのメッセージ境界、送受それぞれのTLS終了、トランスポート終端、永続結果の四層に分ける必要がある。単一の「closed」では代替できない。
正常終了という誤答
事故記録の末尾にはshutdown=successとあった。運用担当者は応答の配信と取引の完了を意味すると読んだ。しかし、この値を出したのはTLSラッパーであり、ローカルの終了処理しか見ていなかった。
サーバーはコミット前に成功応答を組み立て、TLSレコードを書き、close_notifyを送った。その直後、重複制約によりデータベース更新が拒否された。クライアントが受け取った暗号学的な終端は本物だったが、コミットIDは存在しなかった。
ネットワーク上の順序は、業務上の意味を自動的には得ない。TLSは保護したバイトとアラートを順序づけられる。別プロセスの台帳やキューまで証明する機能はない。
終了は一つの瞬間ではない
TLS 1.3では送信方向ごとに終了する。close_notifyの送信者は、それ以降TLSメッセージを送らない。アラート後に届くデータは無視される。一方、読み取り方向は残り、相手からの最後のデータを受け取れる。
エラーアラートをすでに送った場合を除き、各端点は書き込み側を閉じる前に終了アラートを送る。TLS 1.3が即時応答と保留書き込みの破棄を要求しないのは、受信側がまだ送ろうとしているデータを切り捨てないためである。
TCPのFINにも片方向性があるが、証拠の種類は違う。FINは一方のバイト列の終わりを示すだけで、認証されたTLSアラートではない。完全なTLSレコードやアプリケーションメッセージの終端も特定しない。
EOFで失われる確実性
close_notifyより先に下位トランスポートが消えると、受信者は送信者の予定したデータがすべて届いたか判断できない。プロセス障害、プロキシのタイムアウト、非準拠実装、意図的な切断のいずれでも起こる。攻撃を断定する証拠ではなく、強い終端証拠が欠けた状態である。
致命的アラート、RST、タイムアウト、予期しないEOF、相手の終了アラート、ローカルの送信試行も別々に記録すべきだ。すべてを「disconnected」にすると、使えるメッセージと照合すべき処理を見分けられない。
IANAはアラート値0をclose_notifyに割り当てている。この登録はコードポイントを解釈する根拠であって、実運用の接続がそれを送受信した証拠でも、直前のメッセージが完全だった証拠でもない。
メッセージの終端は上位層が決める
保護されたレコードが終了アラートより前に届けば、その順序は証明できる。ただしTLSにとって内容は不透明であり、全文、最終チャンク、受領票、コミット結果のどれかは分からない。
宣言された長さ、区切り、ゼロ長チャンク、ストリームFIN、最終応答、要求ID、確認応答、永続レコードなど、アプリケーション固有の終端が必要になる。完全な応答が失敗を伝えることも、処理が成功して応答だけ失われることもあるため、境界と結果はさらに分ける。
バッファも中間状態を作る。書き込みAPIの成功は、バイトがTLSやBIOに入っただけかもしれない。ノンブロッキング処理では、アラートの送信試行を記録してから実際にトランスポートへ出るまで間がある。どちらもデータベースの耐久性を保証しない。
OpenSSLの0と1は合否ではない
SSL_shutdown()の戻り値0は通常、ローカルのclose_notifyを送ったが相手のアラートは未受信という状態を表す。エラーではない一方、双方向完了でもない。1になって初めて、両方のアラートを送受信したことになる。
最初の呼び出しで閉じるのはTLS書き込み側で、読み取りは続き、TCPも開いたままである。OpenSSLは最後のアプリケーションデータやハンドシェイク後メッセージを処理するため、相手の終了まで読むことを推奨する。保留データを消費せず再度終了を呼ぶと失敗し得る。
sentフラグは送信試行、receivedフラグは相手のアラートを示す。quiet shutdownはアラートを出さずローカル状態だけ閉じる非準拠動作である。OpenSSL 3.0以降、予期しないEOFには固有のエラー意味がある。SSL_OP_IGNORE_UNEXPECTED_EOFは、上位プロトコルが切断を確実に検出し、その検査を実行する場合にしか正当化できない。
GnuTLSもGNUTLS_SHUT_WRとGNUTLS_SHUT_RDWRで送信のみと相手待ちを分ける。BoringSSLも読み書きの終了状態を別管理する。ライブラリに区別があっても、運用指標が一語に潰せば証拠は残らない。
HTTPなら完全性を別に判定できる
HTTP/1.1ではContent-Lengthが指定されればそのバイト数が必要で、chunkedなら最後のゼロ長チャンクが必要である。欠けている場合、接続終了によって完全な応答へ昇格することはない。
接続終了だけで長さを決める応答は特に弱い。TLS終了が不完全なら、HTTPメッセージも不完全になる。RFC 9112が長さまたは符号化による境界を勧めるのは、ネットワーク障害が正常終了に見えるからである。
逆に、所定の長さや終端チャンクをすでに検証したメッセージは、その後に相手のアラートが欠けても境界証拠を保つ。EOF互換を許すなら、利用する各メッセージについてこの独立証拠を示さなければならない。
多重化では要求単位の境界が要る
HTTP/2は一つのTLS接続に複数ストリームを載せる。close_notifyだけでは、サーバーがどの要求の処理を始めたか分からない。GOAWAYの最終ストリームIDが、処理された可能性のある範囲を与える。
GOAWAYなしで接続が終わると、飛行中の非冪等POSTは曖昧なままである。HTTP/3もQUIC上でGOAWAYを使い、受理した要求範囲を示す。接続終了から要求単位の確認応答を推論することはできない。
一律に再試行すれば二重決済が起こり、一律に止めれば処理が失われる。メソッドの意味、冪等キー、結果照会、照合処理が再試行権限を与える。
QUICは別の終了語彙を持つ
QUICはTLSハンドシェイクを使うが、アプリケーションデータをTLSレコードで保護しない。TLSアラートはQUIC接続エラーに変換され、QUIC自身がCONNECTION_CLOSE、ストリームFIN、reset、closing、drainingを定義する。close_notifyのwarning終了を持ち込めない。
HTTP/3がさらにGOAWAYを必要とすることも、接続終端だけでは要求範囲が決まらない証拠である。指標にはTLSアラート、TCP FIN/RST、QUICストリームFIN、CONNECTION_CLOSE、アイドルタイムアウト、HTTP GOAWAY、アプリ確認のどれかを明記する。
終了の証拠台帳を作る
端点の役割、方向、TLS版、接続相関ID、最後の完全なメッセージまたはストリームを残す。フレーミング規則、予定と実受信のバイト数、要求ID、冪等キー、再試行区分も必要である。
TLSについては、ローカルアラートのキュー投入と送出、相手アラートの受信、ライブラリ戻り値列、読み書き状態、保留中の平文と暗号文、正確なエラー分類を分ける。quiet、EOF互換、ライブラリ版、kTLS、プロキシ境界も記録する。
トランスポートではFIN、RST、EOF、タイムアウト、半閉鎖を区別し、多重化プロトコルではGOAWAY範囲とストリーム終端を保つ。業務層では確認、コミットID、永続時刻、その発行主体を保存する。
負の試験には、アラート前のTCP切断、終端チャンク欠落、完全フレーム後かつコミット前の閉鎖、最初の戻り値0、未読データ、quiet shutdown、EOF方針切替、非冪等要求を抱えたHTTP/2・HTTP/3切断を含める。
出典
- https://www.rfc-editor.org/rfc/rfc9846.html
- https://www.rfc-editor.org/rfc/rfc9293.html
- https://www.rfc-editor.org/rfc/rfc9112.html
- https://www.rfc-editor.org/rfc/rfc9113.html
- https://www.rfc-editor.org/rfc/rfc9000.html
- https://www.rfc-editor.org/rfc/rfc9001.html
- https://www.rfc-editor.org/rfc/rfc9114.html
- https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
- https://docs.openssl.org/3.5/man3/SSL_shutdown/
- https://docs.openssl.org/master/man3/SSL_get_error/
- https://docs.openssl.org/master/man3/SSL_CTX_set_options/
- https://www.gnutls.org/manual/html_node/Core-TLS-API.html
- https://boringssl.googlesource.com/boringssl/+/refs/heads/main/ssl/ssl_lib.cc
- https://heng.lu/running-code-primary-the-patch-needed-to-preserve-the-internet-original-design/
会員向け解説
プロフィールの詳細
適切な会員レベルでログインすると、解説全文と出典メモをご覧いただけます。
Strategic Circle 限定
Strategic Circle
すべての読者に公開されています。参加してログインすると プロフィール解説 を閲覧できます。
Strategic Circle に参加Leadership Alliance 会員限定
Leadership Alliance
対象となる IP 資産の所有者・管理者向けです。ログインすると Leadership Alliance の解説を閲覧できます。
Leadership Alliance に参加