コンテンツにスキップ

HTTPS について

HTTPSは単に「有効」か「無効」かで決まるものだと思いがちです。

しかし、それよりもはるかに複雑です。

Tip

もし急いでいたり、HTTPSの仕組みについて気にしないのであれば、次のセクションに進み、さまざまなテクニックを使ってすべてをセットアップするステップ・バイ・ステップの手順をご覧ください。

利用者の視点から HTTPS の基本を学ぶに当たっては、次のリソースをオススメします: https://howhttps.works/.

さて、開発者の視点から、HTTPSについて考える際に念頭に置くべきことをいくつかみていきましょう:

  • HTTPSの場合、サーバ第三者によって生成された「証明書」を持つ必要があります。
    • これらの証明書は「生成」されたものではなく、実際には第三者から取得されたものです。
  • 証明書には有効期限があります。
    • つまりいずれ失効します。
    • そのため更新をし、第三者から再度取得する必要があります。
  • 接続の暗号化はTCPレベルで行われます。
    • それはHTTPの1つ下のレイヤーです。
    • つまり、証明書と暗号化の処理は、HTTPの前に行われます。
  • TCPは "ドメイン "について知りません。IPアドレスについてのみ知っています。
    • 要求された特定のドメインに関する情報は、HTTPデータに入ります。
  • HTTPS証明書は、特定のドメインを「証明」しますが、プロトコルと暗号化はTCPレベルで行われ、どのドメインが扱われているかを知る前に行われます。
  • デフォルトではIPアドレスごとに1つのHTTPS証明書しか持てないことになります。
    • これは、サーバーの規模やアプリケーションの規模に寄りません。
    • しかし、これには解決策があります。
  • TLSプロトコル(HTTPの前に、TCPレベルで暗号化を処理するもの)には、SNIと呼ばれる拡張があります。

    • このSNI拡張機能により、1つのサーバー(単一のIPアドレスを持つ)が複数のHTTPS証明書を持ち、複数のHTTPSドメイン/アプリケーションにサービスを提供できるようになります。
    • これが機能するためには、パブリックIPアドレスでリッスンしている、サーバー上で動作している単一のコンポーネント(プログラム)が、サーバー内のすべてのHTTPS証明書を持っている必要があります。
  • セキュアな接続を取得したでも、通信プロトコルはHTTPのままです。

    • コンテンツはHTTPプロトコルで送信されているにもかかわらず、暗号化されています。

サーバー(マシン、ホストなど)上で1つのプログラム/HTTPサーバーを実行させ、HTTPSに関する全てのことを管理するのが一般的です。

暗号化された HTTPS リクエスト を受信し、復号化された HTTP リクエスト を同じサーバーで実行されている実際の HTTP アプリケーション(この場合は FastAPI アプリケーション)に送信し、アプリケーションから HTTP レスポンス を受け取り、適切な HTTPS 証明書 を使用して 暗号化 し、そしてHTTPS を使用してクライアントに送り返します。

このサーバーはしばしば TLS Termination Proxyと呼ばれます。

TLS Termination Proxyとして使えるオプションには、以下のようなものがあります:

  • Traefik(証明書の更新も対応)
  • Caddy (証明書の更新も対応)
  • Nginx
  • HAProxy

Let's Encrypt

Let's Encrypt以前は、これらのHTTPS証明書は信頼できる第三者によって販売されていました。

これらの証明書を取得するための手続きは面倒で、かなりの書類を必要とし、証明書はかなり高価なものでした。

しかしその後、Let's Encrypt が作られました。

これはLinux Foundationのプロジェクトから生まれたものです。 自動化された方法で、HTTPS証明書を無料で提供します。これらの証明書は、すべての標準的な暗号化セキュリティを使用し、また短命(約3ヶ月)ですが、こういった寿命の短さによって、セキュリティは実際に優れています

ドメインは安全に検証され、証明書は自動的に生成されます。また、証明書の更新も自動化されます。

このアイデアは、これらの証明書の取得と更新を自動化することで、安全なHTTPSを、無料で、永遠に利用できるようにすることです。

開発者のための HTTPS

ここでは、HTTPS APIがどのように見えるかの例を、主に開発者にとって重要なアイデアに注意を払いながら、ステップ・バイ・ステップで説明します。

ドメイン名

ステップの初めは、ドメイン名取得することから始まるでしょう。その後、DNSサーバー(おそらく同じクラウドプロバイダー)に設定します。

おそらくクラウドサーバー(仮想マシン)かそれに類するものを手に入れ、固定の パブリックIPアドレスを持つことになるでしょう。

DNSサーバーでは、取得したドメインをあなたのサーバーのパプリックIPアドレスに向けるレコード(「Aレコード」)を設定します。

これはおそらく、最初の1回だけあり、すべてをセットアップするときに行うでしょう。

Tip

ドメイン名の話はHTTPSに関する話のはるか前にありますが、すべてがドメインとIPアドレスに依存するため、ここで言及する価値があります。

DNS

では、実際のHTTPSの部分に注目してみよう。

まず、ブラウザはDNSサーバードメインに対するIPが何であるかを確認します。今回は、someapp.example.comとします。

DNSサーバーは、ブラウザに特定のIPアドレスを使用するように指示します。このIPアドレスは、DNSサーバーで設定した、あなたのサーバーが使用するパブリックIPアドレスになります。

TLS Handshake の開始

ブラウザはIPアドレスとポート443(HTTPSポート)で通信します。

通信の最初の部分は、クライアントとサーバー間の接続を確立し、使用する暗号鍵などを決めるだけです。

TLS接続を確立するためのクライアントとサーバー間のこのやりとりは、TLSハンドシェイクと呼ばれます。

SNI拡張機能付きのTLS

サーバー内の1つのプロセスだけが、特定 のIPアドレスの特定のポート で待ち受けることができます。

同じIPアドレスの他のポートで他のプロセスがリッスンしている可能性もありますが、IPアドレスとポートの組み合わせごとに1つだけです。

TLS(HTTPS)はデフォルトで443という特定のポートを使用する。つまり、これが必要なポートです。

このポートをリッスンできるのは1つのプロセスだけなので、これを実行するプロセスはTLS Termination Proxyとなります。

TLS Termination Proxyは、1つ以上のTLS証明書(HTTPS証明書)にアクセスできます。

前述したSNI拡張機能を使用して、TLS Termination Proxy は、利用可能なTLS (HTTPS)証明書のどれを接続先として使用すべきかをチェックし、クライアントが期待するドメインに一致するものを使用します。

今回は、someapp.example.comの証明書を使うことになります。

クライアントは、そのTLS証明書を生成したエンティティ(この場合はLet's Encryptですが、これについては後述します)をすでに信頼しているため、その証明書が有効であることを検証することができます。

次に証明書を使用して、クライアントとTLS Termination Proxy は、 TCP通信の残りをどのように暗号化するかを決定します。これでTLSハンドシェイクの部分が完了します。

この後、クライアントとサーバーは暗号化されたTCP接続を持ちます。そして、その接続を使って実際のHTTP通信を開始することができます。

これがHTTPSであり、純粋な(暗号化されていない)TCP接続ではなく、セキュアなTLS接続の中にHTTPがあるだけです。

Tip

通信の暗号化は、HTTPレベルではなく、TCPレベルで行われることに注意してください。

HTTPS リクエスト

これでクライアントとサーバー(具体的にはブラウザとTLS Termination Proxy)は暗号化されたTCP接続を持つことになり、HTTP通信を開始することができます。

そこで、クライアントはHTTPSリクエストを送信します。これは、暗号化されたTLSコネクションを介した単なるHTTPリクエストです。

リクエストの復号化

TLS Termination Proxy は、合意が取れている暗号化を使用して、リクエストを復号化し、プレーン (復号化された) HTTP リクエスト をアプリケーションを実行しているプロセス (例えば、FastAPI アプリケーションを実行している Uvicorn を持つプロセス) に送信します。

HTTP レスポンス

アプリケーションはリクエストを処理し、プレーン(暗号化されていない)HTTPレスポンス をTLS Termination Proxyに送信します。

HTTPS レスポンス

TLS Termination Proxyは次に、事前に合意が取れている暗号(someapp.example.comの証明書から始まる)を使ってレスポンスを暗号化し、ブラウザに送り返す。

その後ブラウザでは、レスポンスが有効で正しい暗号キーで暗号化されていることなどを検証します。そして、ブラウザはレスポンスを復号化して処理します。

クライアント(ブラウザ)は、レスポンスが正しいサーバーから来たことを知ることができます。 なぜなら、そのサーバーは、以前にHTTPS証明書を使って合意した暗号を使っているからです。

複数のアプリケーション

同じサーバー(または複数のサーバー)に、例えば他のAPIプログラムやデータベースなど、複数のアプリケーションが存在する可能性があります。

特定のIPとポート(この例ではTLS Termination Proxy)を扱うことができるのは1つのプロセスだけですが、他のアプリケーション/プロセスも、同じパブリックIPとポートの組み合わせを使用しようとしない限り、サーバー上で実行することができます。

そうすれば、TLS Termination Proxy は、複数のドメインや複数のアプリケーションのHTTPSと証明書を処理し、それぞれのケースで適切なアプリケーションにリクエストを送信することができます。

証明書の更新

将来のある時点で、各証明書は(取得後約3ヶ月で)失効します。

その後、Let's Encryptと通信する別のプログラム(別のプログラムである場合もあれば、同じTLS Termination Proxyである場合もある)によって、証明書を更新します。

TLS証明書は、IPアドレスではなく、ドメイン名に関連付けられています。

したがって、証明書を更新するために、更新プログラムは、認証局(Let's Encrypt)に対して、そのドメインが本当に「所有」し、管理していることを証明する必要があります。

そのために、またさまざまなアプリケーションのニーズに対応するために、いくつかの方法があります。よく使われる方法としては:

  • いくつかのDNSレコードを修正します。
    • これをするためには、更新プログラムはDNSプロバイダーのAPIをサポートする必要があります。したがって、使用しているDNSプロバイダーによっては、このオプションが使える場合もあれば、使えない場合もあります。
  • ドメインに関連付けられたパブリックIPアドレス上で、(少なくとも証明書取得プロセス中は)サーバーとして実行します。
    • 上で述べたように、特定のIPとポートでリッスンできるプロセスは1つだけです。
    • これは、同じTLS Termination Proxyが証明書の更新処理も行う場合に非常に便利な理由の1つです。
    • そうでなければ、TLS Termination Proxyを一時的に停止し、証明書を取得するために更新プログラムを起動し、TLS Termination Proxyで証明書を設定し、TLS Termination Proxyを再起動しなければならないかもしれません。TLS Termination Proxyが停止している間はアプリが利用できなくなるため、これは理想的ではありません。

アプリを提供しながらこのような更新処理を行うことは、アプリケーション・サーバー(Uvicornなど)でTLS証明書を直接使用するのではなく、TLS Termination Proxyを使用してHTTPSを処理する別のシステムを用意したくなる主な理由の1つです。

まとめ

HTTPSを持つことは非常に重要であり、ほとんどの場合、かなりクリティカルです。開発者として HTTPS に関わる労力のほとんどは、これらの概念とその仕組みを理解することです。

しかし、ひとたび開発者向けHTTPSの基本的な情報を知れば、簡単な方法ですべてを管理するために、さまざまなツールを組み合わせて設定することができます。

次の章では、FastAPI アプリケーションのために HTTPS をセットアップする方法について、いくつかの具体例を紹介します。🔒