> For the complete documentation index, see [llms.txt](https://kunishi.gitbook.io/web-application-textbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kunishi.gitbook.io/web-application-textbook/security.md).

# セキュリティ、TLS

* 安全確保、機密保持
* バグに起因
* 通信が絡むとデータを守るべき箇所が増える
* ユーザとしてのセキュリティ（使っているパソコン + α）→開発者としてのセキュリティ（サーバ、クライアント、通信路）

## 通信における危険性

一般に、通信におけるセキュリティ上の危険には、次の3つが考えられる。

* 盗聴：通信路を流れるデータを第三者が盗み見る
* 改ざん：通信路を流れるデータを第三者が不正に書き換える
* なりすまし：第三者が正規の通信相手に成り代わり通信を行い、不正な行為を行う

インターネットにおける通信では、これらのセキュリティ上の危険に対し、**公開鍵基盤**（Public Key Infrastructure, PKI）という公開鍵暗号方式を基礎とする解決策が広く用いられている。特にWebでは、PKIを利用したセキュアな通信方式である**TLS**(Transport Layer Security)が広く用いられる。TLSはトランスポート層(TCP, UDP)とアプリケーション層の間に置かれるプロトコルであり、Web(HTTP)だけでなく、FTP, LDAP, TELNETなどのアプリケーション層プロトコルでも利用できる。

特に、TLS上で実現されるHTTP通信プロトコルを**HTTPS**と呼ぶ。HTTPSはデフォルトでは443番のポートを用いる。

## 暗号化技術

TLSの説明をする前に、技術基盤となる暗号化について簡単に解説しておこう。

### エンコードと暗号化

データに対し、後で元に戻せるような変換を施すことを **エンコード** (符号化, encode)という。エンコードされたデータを元に戻すことを **デコード** (decode)という。ここまでに登場したMIMEエンコーディング、BASE64エンコーディング、URLエンコーディングなどはエンコードの例である。ただし、セキュリティの観点では、一般にエンコードされたデータは誰でもデコードすることができるため、セキュリティ上の危険への対策にはなっていない。

エンコードの特殊な場合として、通信の当事者間でのみ共有される秘密の情報を用いなければデコードできない、というものを考えることができる。このようなエンコードを **暗号化** (encrypt)、暗号化されたデータを復元することを **復号** (decrypt)という。また、暗号化および復号において用いられる、当事者間でのみ共有される秘密の情報を **鍵** (key)という。

### 共通鍵暗号方式

暗号化の方式で最も簡単なのは、暗号化と復号とで同じ鍵を用いる方式である。これを **共通鍵暗号方式** (symmetric key cryptography)という。共通鍵暗号方式では、通信の当事者間で同じ鍵（ **共通鍵** , symmetric key)を共有する。共通鍵暗号によるデータの暗号化・復号の概略は次のようになる。

1. あらかじめ生成された共通鍵$$K$$を、何らかの方法で送り手と受け手とで共有しておく。
2. 送り手は$$K$$を用いて送信したいデータ$$D$$を暗号化し（$$D' = {encrypt}(D, K)$$）、暗号化後のデータ$$D'$$を受け手に送信する。
3. 受け手は$$K$$を用いて$$D'$$を復号し（$$D = decrypt(D', K)$$）、元のデータ$$D$$を得る。

### 公開鍵暗号方式

PKIにおいて重要となるもう一つの暗号化方式が **公開鍵暗号方式** (public key cryptography)である。公開鍵暗号方式は、共通鍵暗号方式に比べ計算コストが高いが、後述するように、共通鍵暗号方式での問題点であった「鍵の共有」について解決策を与えるとともに、デジタルデータへの署名（デジタル署名）という別の用途にも解決策を与えている。

公開鍵暗号方式では **秘密鍵** (private key)と **公開鍵** (public key)という2本の鍵を用いる。これらは常に対として用いられ、かつ、次に示す重要な性質がある。

* 秘密鍵$$SK$$ で暗号化されたデータは公開鍵$$PK$$でしか復号できない。($$D' = encrypt(D, SK)$$ の時 $$D = decrypt(D', PK)$$)
* 公開鍵$$PK$$で暗号化されたデータは秘密鍵$$SK$$でしか復号できない。($$D' = encrypt(D, PK)$$ の時 $$D = decrypt(D', SK)$$)

後述するように、TLSにおいては、秘密鍵は鍵を生成した当事者が誰にも（もちろん他の当事者にも）知られないよう厳重に保管し、一方公開鍵は誰でも（通信の当事者以外の人でも）利用できるように公開する、という使い方をする。

### 暗号学的ハッシュ関数

暗号とは異なるが、PKIにおいて重要な役割を果たす**暗号学的ハッシュ関数** (cryptographic hash function)というものを紹介しておこう。これは、任意のデータを与えると固定長のビット列を出力する関数($$H = hash(D)$$)で、かつ次のような性質を持つ。

* 出力結果$$H$$から元のデータ$$D$$を求めることは極めて困難（事実上不可能）である。
* 同じ出力を得るような2つの異なる入力を求めることは極めて困難（事実上不可能）である。（$$hash(D\_1) = hash(D\_2), D\_1 \neq D\_2$$ となる$$D1, D2$$は事実上求められない） つまり、$$D\_1 \neq D\_2$$ と $$hash(D\_1) \neq hash(D\_2)$$ は事実上同値であると考えてよい。

$$H$$は、データ$$D$$のある種の要約であると考えることができる。この意味で、$$H$$を**ダイジェスト** (digest)と呼ぶ。

## TLS

では、盗聴・改ざん・なりすましへの対策の観点から、TLSの概要を説明することにしよう。なお、以下の説明では、理解を助けるために、各段階ではやや技術的に不正確な記述をあえて行っている場合がある。

### 盗聴への対策

まず盗聴への対策である。これは、共通鍵暗号方式によってデータを暗号化すれば良い。

ただし、共通鍵暗号方式には、共通鍵の共有を安全に行わなければならないという大きな問題が残っている。TLSでは、公開鍵暗号方式やDiffie-Helman鍵共有を利用している。公開鍵暗号方式による鍵共有は、大まかには次のようになる。

1. Webサーバ `server` 上であらかじめ公開鍵$$PK\_{serv}$$と秘密鍵$$SK\_{serv}$$を生成し、$$PK\_{serv}$$を公開しておく。一方、$$SK\_{serv}$$はサーバ外部からアクセスできないようにしておく。
2. Webクライアント`client`は共通鍵$$K$$を生成し、次に$$PK\_{serv}$$をサーバから取得し（`ServerCertificate`メッセージ）、$$PK\_{serv}$$で$$K$$を暗号化する（$$D = encrypt(K, PK\_{serv})$$）。暗号化した結果$$D$$を`server`に送信する。
3. `server` は$$D$$を受け取り、自身の秘密鍵$$SK\_{serv}$$ で$$D$$を復号して$$K$$をとりだす。($$K = decrypt(D, SK\_{serv})$$)

公開鍵暗号方式の性質により、$$D = encrypt(K, PK\_{serv})$$の復号は$$SK\_{serv}$$が必要であり、（Webサーバで$$SK\_{serv}$$が厳格に保管されている限り）盗聴される心配はない。

### 改ざんへの対策

改ざんへの対策には暗号学的ハッシュ関数が用いられる。ただし、通常の暗号学的ハッシュ関数ではなく、送信したいデータと共通鍵を用いて計算を行うため、**メッセージ認証符号**（Message Authentication Code, MAC）と呼ばれている。

1. 送信側で、送信したいデータ$$D$$と共通鍵$$K$$からMAC値$$H=hash(D, K)$$を計算し、$$D$$と$$H$$をまとめて共通鍵$$K$$で暗号化（$$D' = encrypt({D, H}, K)$$）、$$D'$$を送信する。
2. 受信側では$$D'$$から$$D$$と$$H$$を取り出し（$${D, H} = decrypt(D', K)$$）、$$D$$と$$H$$からMAC値を計算する（$$H' = hash(D, K)$$）。暗号学的ハッシュ関数の性質から、$$H = H'$$なら通信路上での改ざんはないと保証することができる。

実際には$$D$$と$$H$$だけでなく、送信側と受信側しか知らないパラメータを含めて暗号化するなどの対策が行われるため、$$D$$と$$H$$がともに改ざんされたとしても、改ざんを検出することができる。

### なりすましへの対策

なりすましへの対策は、Webサーバの公開鍵$$PK\_{serv}$$が正当なものであることを検証することであり、公開鍵暗号方式による電子署名が用いられている。

ここで登場するのが**認証局**（Certificate Authority, CA）である。認証局はすべての通信の当事者が信頼することのできる第三者機関で、$$PK\_{serv}$$の正当性を示す**デジタル公開鍵証明書**を発行することが主な役割である。実際には専門業者や政府機関などが認証局となる。

1. 認証局`CA`はあらかじめ自身の公開鍵$$PK\_{CA}$$と秘密鍵$$SK\_{CA}$$を用意し、$$PK\_{CA}$$を誰でも取得できるように公開しておく。
2. Webサーバ`server`を運用する組織は、`CA`に$$PK\_{serv}$$のデジタル公開鍵証明書の発行を依頼する。
3. `CA`は$$PK\_{serv}$$を$$SK\_{CA}$$で暗号化（$$C = encrypt(PK\_{serv}, SK\_{CA})$$）し、`server`の運用組織に渡す。この$$C$$が$$PK\_{serv}$$のデジタル公開鍵証明書になる。
4. Webサーバ`server`では、$$PK\_{serv}$$と$$C$$をともに公開する。

Webクライアント`client`は$$PK\_{serv}$$の他に$$C$$、$$PK\_{CA}$$をすべて取得し、$$PK\_{CA}$$で$$C$$の復号を試みる（$$decrypt(C, PK\_{CA})$$）。復号に成功すれば、$$PK\_{serv}$$と$$C$$を取得した通信相手は`CA`によって認証を受けた正当なサーバであり、なりすましはされていないと保証される。

では`client`は$$PK\_{CA}$$の正当性をどうやって検証すればいいのだろうか。実は、$$PK\_{CA}$$自体もまた他の認証局によって発行されたデジタル公開鍵証明書により正当性が証明されるのである。このように、多くの認証局が互いに信用関係を持つことで、公開鍵暗号方式での鍵の正当性が保証されている。どのような信用関係を形成するかは**信用モデル**と呼ばれ、多数のモデルが使われているが、Webでは木状の階層構造に信用関係を形成し、かつWebブラウザに信用できるいくつかの認証局の公開鍵をあらかじめ内包しておく、という解決策を取っている。

### 暗号アルゴリズムの決定

このようにTLSでは鍵交換、共通鍵による暗号化、暗号学的ハッシュ関数、電子署名に暗号技術が用いられており、具体的なアルゴリズムは多数提案されている。そこでTLSにはこれらの暗号アルゴリズムの組み合わせ（**暗号スイート**, Cipher Suites）がいくつか定められており、TLSでのハンドシェイクの最初にどの暗号スイートを使うか、通信の当事者間で合意を取る。

1. Webクライアント`client`は、自身が利用できる暗号スイートの一覧をWebサーバ`server`に送信する。(`ClientHello`メッセージ)
2. `server`は、`client`から送られてきた暗号スイートの中から一つを選び、`client`に送信する。(`ServerHello`メッセージ)

このやり取りでは、暗号スイート以外にも、使用するTLSのバージョンや共通鍵を生成するのに必要な乱数なども交換され、`client`と`server`で共有される。

ところでHTTP/2では、WebクライアントとWebサーバがともにHTTP/2を理解できる場合にのみHTTP/2を用い、それ以外の場合にはHTTP/1.1を用いる。つまり、HTTP/1.1でハンドシェイクを開始し、双方がHTTP/2を理解できる場合にのみHTTP/2による通信に変更する（プロトコルアップグレード）。HTTP/2では、その実装の一つとして、`ClientHello`メッセージ及び`ServerHello`メッセージの拡張であるALPN（Application Layer Protocol Negotiation）を用いる方法を定めている。ALPNでは、TLSの`ClientHello`メッセージの中に、Webクライアントが使用できるHTTPプロトコルの一覧も含め、Webサーバがその中から一つを決定して、`ServerHello`メッセージによってクライアントに通知する。
