古いTLSバージョンを利用しているユーザーへの対応

# この記事の内容について

TLS1.0/1.1などの古いバージョンのTLSや3DESの暗号化アルゴリズムには脆弱性があることが知られており、TLSで通信を行う場合はTLS1.2以上のバージョンで行うことが推奨されています。
クレジットカード情報を利用した取引を行う際に推奨されるセキュリティ要件を定義している[PCI DSS v3.2](https://blog.pcisecuritystandards.org/are-you-ready-for-30-june-2018-sayin-goodbye-to-ssl-early-tls)においても2018年の6月30日までにTLS1.2に移行することを求めています。

FastlyでもPCI DSS および HIPPA(Health Information Portability and Accountability Act)のサポートを希望されるお客様を中心に、TLS1.0/1.1の廃止を進めています。


この記事では、Fastlyを利用してエンドユーザーが利用しているTLSのバージョンを確認しログに出力する方法や、TLS1.2より前のバージョンを利用している場合はページ側でアラートを表示するなど、何らかのアクションを行うページのサンプルを作成してみたいと思います。


```
特に記載がない限り本記事の記載内容はデフォルト設定での挙動となります。
Fastlyの正式なサポート情報は https://docs.fastly.com/ を参照して下さい。
```

# はじめに

Fastlyではエンドユーザーが接続に利用したTLSのバージョンを取得してオリジンへのリクエストやレスポンスに付与することが出来ます。
最初に取得したTLSバージョンをログに出力する方法を説明し、続いて取得したTLSバージョンの内容に応じてメッセージを表示する手順を説明します。

# 使用されたTLSバージョンのログへの出力

以下の変数には使用されたTLS通信に関する情報が含まれています。

**req.is_ssl**: 通信がTLSで行われたかどうか
**tls.client.protocol**: 通信に利用されたTLSのバージョン
**tls.client.cipher**: 使用された暗号化アルゴリズム

これらの情報をログに含めることで、どの程度の割合のユーザーが古いTLSバージョンの通信を利用しているかを調べることが可能です。

ログにこれらの情報を含めるには、Fastly の設定画面の Logging から、**Log Format** を以下のように指定して下さい。

`%h %l %u %t "%r" %>s %b %{req.http.host}V %{if(req.is_ssl, "true", "false")}V %{cstr_escape(tls.client.protocol)}V %{cstr_escape(tls.client.cipher)}V`

ログを出力する条件(Condition)として、以下のような条件を設定することで、古いTLSのバージョンを利用している場合のみログを出力することも可能です。

`tls.client.protocol == "TLSv1" || tls.client.protocol == "TLSv1.1" || tls.client.cipher ~ "DES"`

設定が正しければ以下のような情報がログに出力されます

`x.x.x.X "-" "-" [10/May/2018:03:23:01 +0000] "GET / HTTP/1.1" 200 5421 www.example.com true TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256`



# 古いTLSバージョンを利用しているユーザーにメッセージを表示する
TLSバージョンをオリジンへのリクエストに付与して、オリジン側でアラートを表示するページと表示しないページの2種類のページを作成することも可能ですが、それでは同じURLに対して異なるバージョンのページを作成する必要がありますし、キャッシュの効率も悪くなってしまいます。

そこで今回はTLSバージョンの情報をレスポンスでクライアントに付与し、クライアント側でJavaScriptを用いて必要に応じてアラートメッセージを表示するサンプルを作ってみたいと思います。

**本設定は現在TLS1.0/1.1をサポートしているFastlyの配信環境が前提になります。**

## 設定の流れ

設定の手順は以下の通りです。

1. FastlyでTLSバージョンを取得し、Set-Cookieヘッダーに情報を付与してクライアントにレスポンスを返却。
TLSのバージョン情報を直接レスポンスヘッダーにいれたいところなのですが、JavaScriptがレスポンスヘッダーの値を直接取得できないので、一旦Cookieに入れます。


2. JavaScriptでCookieからTLSバージョン情報を確認し、TLS1.0/1.1の場合はエラーメッセージを表示


## 1. TLSバージョンをレスポンスに含める

Fastlyのコントロールパネル( https://manage.fastly.com/ )にログインし、**Configure**タブを選択し、**Snippet**をクリックして下さい。

以下の画面を参考にしてvcl_deliverにSnippetを作成します。


![Fastly: Configure (test.a24fkd.com) 2018-03-20 13-47-59.png](https://qiita-image-store.s3.amazonaws.com/0/128569/dfb998d9-56bf-c7bc-f791-26da553b1e5a.png)


最低限必要なSnippetのコードは以下の通りです。ここでは無条件でCookieを発行していますが、ドメインや拡張子、パスなどを指定して条件にマッチする場合にのみヘッダーを設定することも可能です。

```
add resp.http.Set-Cookie = "tlsinfo="req.is_ssl","tls.client.protocol","tls.client.cipher"; expires="time.add(now,2s)";";
```

コードを見て頂ければお分かりになると思いますが、ここでは2秒のみ有効なCookieを発行するための `set-cookie`ヘッダーをレスポンスに付与しています。

オリジンからの元々のレスポンスに`Set-Cookie`が含まれていた場合に上書きをしてしまわないように、`set`ではなく`add`を利用しています。

Cookieに含まれる情報は以下の通りです。

**req.is_ssl**: 通信がTLSで行われたかどうか
**tls.client.protocol**: 通信に利用されたTLSのバージョン
**tls.client.cipher**: 使用された暗号化アルゴリズム

これらの変数の詳細や、その他の変数については[Fastlyでよく使う変数とテクニック](https://qiita.com/AtsushiFukuda/items/5e0a24266a09038b7801#tls%E6%8E%A5%E7%B6%9A%E3%81%A8http2%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E6%83%85%E5%A0%B1---tls)を参照してみて下さい。

続いて上記のSnippetを追加した設定を Activate して、設定が正しく行われているかをテストしてみます。

`curl -svo /dev/null https://www.yourdomain.com`

上記のcURLコマンドを実行して、レスポンスに以下のようなset-cookieヘッダーが含まれていれば設定は正しく行われています。
`< set-cookie: tlsinfo=1,TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256; expires=Tue, 20 Mar 2018 04:32:07 GMT;`


## 2. クライアント側でエラーを表示する
それではTLSのバージョンに応じてクライアント側でメッセージを表示するロジックを追加したいと思います。

ここでどのような処理を追加したいかは運用するサービスにより様々だと思いますが、このサンプルではシンプルにTLSのバージョンとを確認してアラートメッセージを表示してみたいと思います。

メッセージはJavaScriptで表示しますので、Cookieの値を取得して内容に応じて処理を行う形になります。


```
    <script type="text/javascript">

      window.onload = function(){

        function getCookie(name) {
          var value = "; " + document.cookie;
          var parts = value.split("; " + name + "=");
          if (parts.length == 2) {
            return parts.pop().split(";").shift().split(",");
          } else {
            return ['undefined','undefined'];
          }
        }

        var tlsinfo_values = getCookie('tlsinfo');
        var ssl = tlsinfo_values[0];
        var tlsv = tlsinfo_values[1];
        var cipher = tlsinfo_values[2];

        if(tlsv == "TLSv1" || tlsv == "TLSv1.1") {
          alert("接続に利用された" + tlsv + "は近日中にサポートが停止されます。TLS1.2をサポートするクライアントへのアップデートをお願いします。");
        }else if(cipher.indexOf('DES') !== -1){
          alert("接続に利用された" + cipher + "は近日中にサポートが停止されます。クライアントへのアップデートをお願いします。");
        }else{
          console.log("OK, do nothing")
        }
  
    </script>
```


例えば上記のコードではページが読み込まれたタイミングでCookieから情報を取り出し、取得したTLSのバージョンや利用する暗号アルゴリズムを確認し、`TLSv1`または`TLSv1.1`にマッチする場合や、暗号化アルゴリズムに`3DES`を含む場合にアラートメッセージを表示しています。
上記の条件にマッチしない場合は、ブラウザのデベロッパーコンソールにログを出力するだけで特に処理は行いません。


## 3. 動作確認

それではサンプルのページにそれぞれのパターンでアクセスをしてみます。

・http://yourdomain.com/
アラートメッセージが表示されずに、ブラウザのデベロッパーコンソールに `OK, do nothing` と表示されていればOKです。

・https://yourdomain.com/
通信に利用したTLSのバージョンによって動作が異なります。
TLS1.2: アラートメッセージが表示されずに、ブラウザのデベロッパーコンソールに `OK, do nothing` と表示されていればOKです。

TLS1.1: `接続に利用されたTLSv1.1は近日中にサポートが停止されます。TLS1.2をサポートするクライアントへのアップデートをお願いします。`というアラートメッセージが表示されればOKです。

ちなみにChromeではTLS1.2を無効化する手順が見つけられませんでしたが、FireFox(58.0.2)では以下の手順でTLS1.2を無効化して1.1で通信を行うことが出来ました。

![about:config 2018-03-19 17-35-04.png](https://qiita-image-store.s3.amazonaws.com/0/128569/cb72d36a-75ed-3ee0-b0ff-7b6cea3ae949.png)

FireFoxのアドレスバーに`about:config`を入力。
Searchからtlsを検索
security.tls.version.max を `3` から `2` に変更


## まとめ

通常はレスポンスヘッダーの内容を使ってクライアント側で処理を行うことは難しいと思うのですが、FastlyのVCLを使ってCookie経由で情報を渡すことで、クライアント側でヘッダーの内容に応じた処理が可能になります。

この記事ではTLSのバージョンに応じてJavaScriptで動作を変更する処理を行いましたが、TLSのバージョンではなく例えば国情報、Fastlyのキャッシュに関する情報、URLなどのMD5のハッシュの結果など様々な情報をクライアントに渡すことが出来ます。