「Real World HTTP」を読んだ話

Taiga Ishii
11 min readJul 29, 2018

前回の読んだ話に続く第二弾です。

今回の本は、オライリー・ジャパンが発刊している。「Real World HTTP」という本です。前回読んだ「Webを支える技術」よりもHTTPの歴史や仕様に関して深く掘り下げられています。

今回はこの中でも、特に自分が難しいと感じた、TLSについて自分の整理のためにも書こうと思います。

最初に感想からすると、先月に読んだ本に比べだいぶ細かく、深い内容が書かれていたため、とても難しく感じました。正直わからないところもたくさんあったという印象でした。この本は定期的に読み返して少しづつ理解していけたらなと思ってます。

TLS(トランスポート・レイヤー・セキュリティ)

TLSとは、通信経路を暗号化する規格で、HTTP/1.1と並行して規格化がれました。httpsが「安全な通信」といわれる一つの要因でもあり、HTTPだけに限らず、既存のプロトコルに通信経路の安全性をプラスした新たなプロトコルを作り出すことができる汎用的な仕組みになっています。HTTPのウェルノウンポートは80番ですが、HTTPSは443番と別のサービスとして扱われています。 TLSを使うと改ざんなどが行えない安定した通信路ができるため、HTML5で新しく導入されたWebsocketのような通信プロトコルやHTTP/2など、数多くの新しい仕組みをスムーズに導入するインフラになりました。

TLSは現在1.3が策定中というステータスです。

curlでhttps://から始まるURLを叩くことでTLSの動作を確認することができます。また下記のオプションを付与することでもう少し詳細にTLSの設定ができます。

  • -1, --tlsv1

TLSで接続する

  • --tlsv1.1, --tlsv1.1. --tlsv1.2, --tlsv1.3

TLSネゴシエーション時に指定のバージョンでつなぐように強制します

  • --cert-status

証明書の確認をします

  • -k, --insecure

自分で作成した証明書でもエラーが発生しなくなります。

curl --cert-status -v https://example.com*  SSL certificate verify ok.

ハッシュ関数

入力データから、ルールに従ってデータを集約することで、ハッシュ値と呼ばれる短いデータを作り出します 。ハッシュ関数には暗号化通信を行う上で便利な数学的特性があります。

  • ハッシュ関数を h()
  • 入力データを A,B...
  • 算出されたハッシュ値を X, Y...
  • 長さは len()
  • 同じアルゴリズムと同じ入データであれば、結果として作られるハッシュ値は同じになります。 h(A) = X が常に成り立ちます
  • ハッシュ値はアルゴリズムが同じであれば、同じ長さとなります。 len(X) < len(A) に基本的にはなります
  • ハッシュ値から元のデータを類推することは難しいです。 h(A) = X の値から、Aを探すのは困難です。(弱衝突耐性)
  • 同一のハッシュ値を持つ任意のデータの組を見つけるのが難しいです。 h(A) = h(B) となる任意のデータの組AとBを見つけるのは困難です(強衝突耐性)

ハッシュ関数はコンピュータの中で様々な用途に使用されています。例えばハッシュ値は入力データ1バイトでも違えば変わるため、ダウンロードしたファイルが壊れてないかを確認する方法としても使用できます。

またGitはファイルを管理するときにファイル名ではなく、ファイルの内容を元にしたハッシュ値を用いています。

共通鍵暗号と公開鍵暗号とデジタル証明

暗号のアルゴリズムで大事にされていることは、アルゴリズムそのものを秘密にすることではなく、アルゴリズムが公開されていても安全に通信をすることができるということです。

現在一般的に使用されている暗号化方式はオープンですが、その暗号化に使うデータ(鍵)を別に用意する方式です。

TLSで使われているのは共通鍵方式と公開鍵方式の2種類があります。

共通鍵方式

鍵をかけるのと鍵を開けるのに同じ鍵を使う方式です。
対称暗号とも呼ばれます。通信する人同士が鍵を共有する必要があります。暗号化とは「鍵のデータに従ってデータを壊す」ことです。鍵があればその壊れたデータを正しく戻すことができます。
TLSでは通常の通信の暗号化に使います。

公開鍵方式

非対称暗号とも呼ばれ、公開鍵と秘密鍵が必要になります。公開鍵はその名の通り、全世界に公開しても問題のない鍵です。秘密鍵は他の人に知られてはいけない鍵のことです。暗号化するほうが公開鍵で、復号化するのが秘密鍵です。
南京錠の例がわかりやすく、鍵をかけた南京錠を他の人に送り、それを開ける鍵を持っている人だけが南京錠を開けることができる仕組みです

デジタル署名

デジタル署名は公開鍵方式の応用です。逆に鍵の方を配って南京錠を秘密にしておくようなイメージです。手紙の本文に対して南京錠をかけたデータも一緒に添付して送付します。受け取った人は公開されている鍵を使って南京錠を解錠したときに本文と同じ内容が出てきたら本文が改ざんされていないことがわかります。

鍵交換

クライアントとサーバ間で鍵を交換します。簡単な方法としてはクライアントで共通鍵を生成し、前述のサーバ証明書の公開鍵を使って暗号化して送る方法もありますし、鍵交換専用のアルゴリズムもあります。

この本ではDH(ディフィー・ヘルマン)鍵交換アルゴリズムを紹介しています。

このアルゴリズムのポイントとしては、鍵そのものを交換せず、クライアントとサーバでそれぞれ鍵の材料を作り、お互いに交換しあって、それぞれの場所で計算して同じ鍵を得ます。

共通鍵方式と公開鍵方式を使い分ける理由

TLSでは通信ごとに一度だけ使われる共通鍵を作り出し、公開鍵方式を使って通信相手へ厳重に鍵を受け渡し、その後は、共通鍵で高速に暗号化を行うという2段階の方式を利用しています。公開鍵方式のほうが安全性は高いのですが、鍵を持ってい田としても暗号化と復号化に必要な計算量が共通鍵方式に比べて大きすぎるためです。

TLSの通信手順

TLSの通信は3つに分けることができます。

  • ハンドシェイクプロトコルと呼ばれる通信を確立するステップ
  • レコードプロトコルと呼ばれる通信時のステップ
  • Session Ticketという仕組みを使った、再接続時の高速なハンドシェイク

サーバの信頼性を確認

サーバの信頼性を保証する仕組みは、公開鍵の保証する仕組みでもあり、公開鍵基盤(Public Key Infrastructure)と呼ばれます。ブラウザはサーバから、そのサーバの持つSSLサーバ証明書と呼ばれるものを取得するところからスタートします。

証明書は、サイトの主体者(名前とドメイン名)、発行者、主体者のサーバの公開鍵、有効期限などの項目があり、発行者は認証局です。
また証明書に発行者のデジタル署名がついています。

外部に公開されているサービスであれば、証明書は誰でも取得できます。

openssl s_client -connect www.google.com:443 < /dev/null > google.crtopenssl x509 -in google.crt -noout -text

鍵交換と通信の開始

次は鍵交換です。クライアントはまず乱数を使って通信用の共通鍵を作成します。共通鍵を作るときの乱数生成の質も高く保つ必要があります。

公開鍵を使う方法はシンプルです。サーバの証明書に添付されている公開鍵を使って通信用の共通鍵を暗号化し、その鍵をサーバに送付します。サーバは証明書の公開鍵に対応した秘密鍵を持っているため、送付されたデータを復号して共通鍵を取り出すことができます。

また鍵交換アルゴリズムを使うときは、どちらか一方が完全な鍵を作って相手に渡すのではなく、鍵の種をクライアントとサーバの両方で一つずつ作ります。その種をお互いに交換して、計算した結果が共通鍵になります

Forward Securiry(前方秘匿性)という特性が優れているため、鍵交換で今後主流になるのは鍵交換専用アルゴリズムです。
TLS1.3では公開鍵暗号による鍵交換は廃止になります。

通信

通信時にも機密性と安全性のために暗号化を行います。ここでは共通鍵暗号方式のアルゴリズムを利用します。

通信の高速化

通常の接続では、まずHTTPでつなぐ前のTCP/IPの段階で1.5RTTかかります。その後TLSのハンドシェイクで2RTT、その後のHTTPリクエストで1RTTの通信時間がかかります。ただしTCP/IPの通信の最後の0.5RTTと、その後のTLSの最初の通信は一緒に行えるため、合計は4RTTです。TLSを使わなければ2RTTですみます。

通信の時間においてはとてつもなく大きな時間がかかります。インターネットの表示を早くするためには、往復の時間を減らすことが大切です。

HTTPで指定することができるKeep-Aliveを使えばセッションが持続されるため、最初のリクエスト以降の通信ではRTTが1になります。

TLS1.3では事前共有鍵を共有しておくことで0RTTで最初のリクエストから情報を送信できるようになります。
また鍵交換と秘密鍵暗号が分離されたため、暗号スイートで秘密鍵暗号をネゴシエーションした結果を待つことなく、クライアント側から鍵の交換が行えるようになります。そのため通信が一往復減り、認証が完了します。通信が一往復減り、1RTTで認証が完了します。

暗号スイート

TLSはHTTPとはまた違った方式で仕様に柔軟性をもたせています。TLSの骨格となる「サーバを認証し、鍵を交換して通信を行う」というフローは、TLS1.0から1.3まで大きく変わりません。その一方で、鍵交換の方法、メッセージの暗号化、メッセージの署名方式など、それぞれの場面で使うアルゴリズムの組み合わせをリスト化して管理し、サーバ/クライアント間で共通に使えるものを選択する仕組みにすることで、新しいアルゴリズムを少しづつ導入したり、古いアルゴリズムを非推奨にするといったことを、バージョン間で行いやすくなっています。このアルゴリズムのセットを暗号スイートと呼びます。

プロトコルの選択

TLSが提供する機能の中で、次世代通信に無くてはならない者としてアプリケーション層のプロトコルを選択する拡張機能です。ALPN(Application-Layer Protocol Negotiation)拡張という方式がRFC7301になっています。

ALPNでは、TLSの最初のハンドシェイク開始時に、クライアントからサーバに「クライアントが利用可能なプロトコル一覧」を送付して送信します。サーバはそのレスポンスで、鍵交換や証明書と一緒に選択したプロトコルを送ります。クライアントからレスポンスをおくり、受理可能なものをサーバが1つ選んで返すの手法は、コンテントネゴシエーションと同じです。

現在選択可能なプロトコルとして

  • HTTP/1.1
  • SPDY/1, SPDY/2, SPDY/3
  • Traversal Using Relays around NAT(TURN)
  • NAT discovery using Session Traversal Utilities for NAT(STUN)
  • HTTP2 over TLS
  • HTTP2 over TCP
  • WebRTCのメディアとデータ
  • Confidential WebRTCのメディアとデータ
  • FTP

TLSが守るもの

TLSは通信経路の安全を守るための仕組みです。クライアントとサーバ間の通信経路が全く信頼できない状態でも安全な通信が行えるように設計されています。TLS1.3では認証付き暗号モードのアルゴリズムが守っています。そこで大切になるのが、共通鍵の安全な交換です。経路上で観測しているだけでは鍵を探すことが難しいDHEやEXDHEといった鍵交換アルゴリズムを利用します。

TLS1.2では公開鍵暗号を使って鍵を交換する方法を提供しています。秘密鍵さえ守っていれば保護することができます。

証明書の安全性は、公開鍵基盤を使って他者(発行者)によるお墨付きによって得ます。

TLSはいくつもの方法を組み合わせてそれぞれの穴を防ぎ合っています。

一方TLSが守ってくれないものもあります。TLSは通信経路外の情報の秘匿は行いません。ブラウザ上のクッキーを盗み出すクラッキングはTLSで守られていてもブラウザを誤動作させて意図しないサーバ向けに送信されます。サーバがクラックされたときの保護も行いません。ユーザのパスワードを平文でDBに格納するではなく。ハッシュ化して保護するというようなことはTLSとは別で行わなければなりません。

最後に

この本を読んでみて、先月に読んだ本より圧倒的に内容が難しくなっていました。ですが、これらのことを自分が所属している会社のインフラと照らし合わせて考えてみると理解が進みやすかったところもありました。

--

--