created: 2021-03-03T09:57:50.000Z

SNIについて調べたことをメモ

AnyEvent::HTTP を使っている処理でエラーが出て、ホストと接続できなくなった。

596 - errors during TLS negotiation, request sending and header processing.

原因

AnyEvent をアップグレードしたら接続できるようになったので、それをヒントに調査。

TLS ネゴシエーション周りの変更を探すと、AnyEvent は 7.12 から Net::SSLeay::set_tlsext_host_name を呼ぶようになっているのが見つかった。

set_tlsext_host_nameとは「リクエストしたいドメイン名をセットする」処理であり、ここでドメイン名をセットできていなかったのでエラーになっていた。

SNIについてメモ

SNI(Server_Name_Indication)とは、TLSハンドシェイク時の手続き。

リクエスト時にクライアントから接続したいホスト名を伝えることで、サーバ側がグローバルIPの証明書ではなく、クライアントが接続したいホスト名の証明書を使ってくれるという機能。

https://ja.wikipedia.org/wiki/Server_Name_Indication

Server Name Indication (SNI, サーバー ネーム インディケーション, サーバ名表示)は、SSL/TLSの拡張仕様の一つである。SSLハンドシェイク時にクライアントがアクセスしたいホスト名を伝えることで、サーバ側がグローバルIPごとではなくホスト名によって異なる証明書を使い分けることを可能にする

これがなぜ嬉しいかというと、この機能があると1つのIPに複数のホスト名が設定できるようになるため。

SSL/TLS接続のはじめに、クライアントはSSL/TLSのサーバから(サーバとCAの)証明書を受け取り、証明書の改ざんされていないことなどを確認する。サーバ証明書にはホスト名が書かれており、それが今接続しようとしているホスト名と一致することをクライアントは確認する。そうでない場合、なりすましや中間者攻撃の恐れがあるため、クライアントはユーザに警告をする。ユーザの責任で証明書を信用し、警告を迂回することができるアプリケーションも存在する。 HTTPの場合、名前ベースバーチャルホストを使うと、複数のホスト名(同一のドメインであれ、異なるドメインであれ)を単一のサーバと単一のグローバルIPで運用できる。これは、ブラウザ側がHTTPヘッダによって希望のホスト名を指定することで働く。しかし、HTTPSの場合、HTTP要求をやりとりする前にSSL/TLSハンドシェイクを行う必要がある。このため、ブラウザがどのホストを訪問するかを、ハンドシェイク時点でサーバ側には予測できず、HTTPヘッダのホスト名によって複数枚のサーバ証明書を使い分けることができない。したがって、単一のサーバで複数枚のサーバ証明書を使い分けるには名前ベースバーチャルホストは利用できず、ホスト名ごとに異なるグローバルIPを使う必要があった(IPベースバーチャルホスト)。

ただし、SNIをつかうとTLS通信なのにホスト名の通信は平文で行われるため誰がどのサイトにアクセスしているかがバレてしまう

Server Name Indication (SNI) では、TLSに拡張を加えることでこの問題に対処する。TLSのハンドシェイク手続きで、HTTPSクライアントが希望するドメイン名を伝える(この部分は平文となる)[3]。それによってサーバが対応するドメイン名を記した証明書を使うことが可能になる。サーバとクライアントが共にSNIに対応していれば、一つのIPで複数のドメインにHTTPSサーバを提供することが可能になる。

この問題に対処するのに、ESNI という方式がある