DTLSにおけるハンドシェイクの特徴
暗号化通信というとTLSが使われることが多いと思いますが、これはTCP通信を暗号化するために使われます。一方、UDP上の通信を暗号化したい場合は、DTLSというプロトコルが使われます。DTLSではUDP固有の問題に対応するために、TLSにはない機能がいくつか追加されています。今回はハンドシェイクについてみてみます。
DTLSにおいても通信の開始時にはTLSと同じハンドシェイクが行われます。DTLSのフルハンドシェイクは以下のようになります。

DTLSのハンドシェイクも基本的な流れはTLSと同じになります。ただ、TLSのハンドシェイクを知っている人は、以下が気になったのではないでしょうか。
- HelloVerifyRequestというTLSではみないメッセージがある
- ClientHelloメッセージがなぜか二回送られている
これはIPを詐称したクライアントからのDoS攻撃を防ぐためにDTLSに追加されている機能です。
IP詐称したホストからのDoS攻撃
UDPではコネクションレスな特性から、プロトコルによっては、送信元IPを詐称してパケットを送信するような攻撃がしやすくなる場合があります(図2)(*1)。

何も対策がされていない場合、DTLSのハンドシェイクでも同様の攻撃が可能になります(図3)。

RFCではIP詐称による以下の攻撃を懸念しています(RFC 9147 5.1. Denial-of-Service Countermeasures)。
- サーバーにコストのかかる暗号処理を実行させることができる
- Certificateメッセージ(*2)を大量に送りつけることが可能になる
ここで、HelloVerifyRequestメッセージの出番になります。
最初のClientHelloを受信すると、サーバー側ではクライアントを検証するためのCookieを生成して、HelloVerifyRequestに載せて送信します。そして、HelloVerifyRequestを受信したクライアントは受け取ったCookieを2回目のClientHelloに設定して送信します(図1参照)。
サーバーはこのCookieを検証することで、ClientHelloが送信元IPが詐称されていない正しいクライアントから送信されたものだと判断できます。詐称されたClientHelloの場合、攻撃者はHelloVerifyRequestを受け取れないので、正しいCookieをサーバーに送り返すことができずに、ハンドシェイクはこれ以降の処理に進みません。
これにより懸念だった、
- IPを詐称したHostからのClientHelloで、サーバーがコストのかかる暗号化処理を実行する
- 詐称"された"Hostに、サイズが大きくなりがちなCertificateメッセージが送られる
ことを防ぐことができるようになります。
なお、RFCにも書いてありますが、この手法は有効なIPからのDoS攻撃には意味がありません。ただそれは、DTLS固有の問題ではなくTLSでも同じ話になります。CookieはDTLS固有の問題を解決する手段です。
Cookieの作り方
ところで、ClientHelloを受信して作成したCookieをサーバー側で保持していたら、それなりにサーバーに負担がかかってしまいます。
このため、サーバー上でクライアントごとの状態を保持せずに(Stateless)、Cookieを検証できるように実装を工夫する必要があります。RFCでは例として以下のような作成方法が提案がされています。
Cookie = HMAC(Secret, Client-IP, Client-Parameters)
Secretはサーバーだけが持つ乱数列でHMAC計算時の秘密鍵、Client-ParametersはPort番号等のClientを識別するIPアドレス以外のパラメータになります。
ClientIPとClient-Parameters(Port番号等)のHMACを計算して、結果をCookieの値としています。クライアントからCookieが送られてきた際には、Secret, Client-IP, Client-Parametersを使ってHMACを再計算して送られてきたCookie値と同じになることを検証します。
Client-IP,Client-Parametersは送信元の情報から取得できるので、サーバー側はStatelessにできるわけですね。攻撃者はSecretが分からないので、正しいCookieを作成することはできません。
ちなみにOpenSSLでDTLS通信する際は、このCookie作成処理は自分で実装して、SSL_CTX_set_cookie_generate_cb()でコールバック登録しておく必要があるので、若干面倒です。ただ、apps/s_cb.c の generate_cookie_callback() にCookie作成処理があるので、これを流用すればよいでしょう。
TLSのハンドシェイクとの他の違い
他の違いについては、Handshakeメッセージのフォーマットにフラグメントに関するフィールドがあることです。
DTLSではUDPを使うので、HandShakeメッセージが大きくなるとMTU長を越えて、IPフラグメントが発生する可能性があります。このため、DTLSではIPフラグメントが発生しないように、DTLSレベルでHandshakeメッセージをフラグメント(複数のUDPデータグラムに分割)するようになっています(図4)。
ところで、図4はOpenSSLを使ってDTLS通信をした時のデータをキャプチャしたものですが、かなり小さく(L2ヘッダー込みで270 Byte)フラグメントされているのが分かります。RFCにも以下のような記述があるので、Path MTUが小さい環境でもIPフラグメントがされないように保守的な設定値になっているようです。
However, DTLS handshakes occur infrequently and involve only a few round trips; therefore, the handshake protocol PMTU handling places a premium on rapid completion over accurate PMTU discovery.
DTLSハンドシェイクはまれにしか発生せず、数回のラウンドトリップしか発生しない。このため、HandshakeプロトコルのPMTU処理は、正確なPMTUの発見よりも迅速な完了を重視する。
RFC 9147 - 4.4. PMTU Issues より
他の目立つ違いとしては、Handshakeメッセージの再送はDTLS層によって行われるという点でしょうか。DTLSはUDPを使うので、Handshakeメッセージが失われた時にどうなるかというと、DTLS層で再送が行われるようになっています。
まとめ
DTLSのハンドシェイクについて、TLSとの違いという観点からまとめてみました。
昨年、DTLSを使ったプロトコル実装のお手伝いしたこともあって、DTLSに触れる機会が増えました。DTLSまわりで何かお困りのことがあれば、お気軽にお問い合わせください。何か手助けできることがあるかもしれません。
ネットワークプロトコルに関しては、このようなテストツールなども開発しています。
(*1) TCPの場合、コネクションを張るために3way hanshakeを行う必要があるため、IPを詐称した場合そもそもTCPコネクションが張れず、サーバーへリクエストを送る段階までいかない。TCPはTCPでSYN flood攻撃のようなものがあったりしますが。
(*2) Certificateメッセージは、証明書を送るためサイズが大きくなる傾向があります。
投稿日:2023/01/21 20:58