HSTSを導入することになった時のメモ書き

# 初めに

恥ずかしながら少し前までHSTSの存在自体知らなかったのですが、
最近セキュリティ診断を実施した際に指摘を受けることが増えました。

社内の他の人が担当の環境で導入することになり
その方がうまくいかないとハマってたのでその時のアドバイスのメモ書きになります。
(存在すら知らなかった自分になぜ質問が来るのか...)

今後ハマってる人から別の質問が来たら追記するかもしれません。

なおQAは大概[RFC6797]("https://tools.ietf.org/html/rfc6797")を読むと解決しました。
今回は基本的に上記RFCの話を正として記載しますが実際の実装は各ブラウザによるためご注意ください。

# HSTSって何

サーバ側ではなくクライアント側でHTTPからHTTPSに転送する仕組み(かなり意訳)

## 導入すると何がいいの

中間者攻撃のを受ける余地が減ります。

よくあるサーバ側でのHTTPSリダイレクトはHTTPSに転送する前に、
暗号化されていないHTTPで通信するためその際に通信を改竄される危険性が高まります。

例えばApacheで以下のような転送設定が行われているサイトがあったとします。
(このサイトではHSTSの設定を行なっていない)

```
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
```

このサイトにHTTPをアクセスを行なった場合`Location`ヘッダーに対してHTTPSのURLが格納されて応答されますが、
暗号化されていないため中間者攻撃等で`Location`ヘッダーが改竄され意図しないサイトへ転送される危険性が高くなります。

```
$ curl -I https://example.com
HTTP/1.1 301 Moved Permanently
(略)
Location: https://example.com/
```

つまり以下の2~3のタイミングで改竄される可能性が高まります。

1. クライアントは`http://example.com`へのリンクを踏む
2. クライアントが`http://example.com/`に要求を行う(暗号化されていない通信が発生)
3. サーバが`https://example.com/`への転送を含む応答を返す(暗号化されていない通信が発生)
4. クライアントは`Locastion`ヘッダーの`https://example.com`にアクセスする(暗号化されている)

HSTSによる転送が有効な状態ではざっくり以下のようになります。
外部との通信に暗号化されていない通信が発生しないのでその分中間者に改竄される可能性が低くなります。

1. クライアントは`http://example.com`へのリンクを踏む
2. クライアントのブラウザ内で`https://example.com`へ転送する(具体的な処理未確認のため注意)
3. クライアントは`Locastion`ヘッダーの`https://example.com`にアクセスする(暗号化されている)

Chromeで確認してもらった限り2.の処理として307リダイレクトが発生しています。

# 設定

[Strict-Transport-Security]("https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Strict-Transport-Security")ヘッダーを設定するだけで良さそうです。

このヘッダーをブラウザが認識して初めてHSTSが有効になるので、
初回のアクセス時(ヘッダーを読んでいない時は)設定の有無に関わらず従来の前述の`Location`ヘッダーによる転送が発生します。

これに関しては`HSTS preload`という機能で回避できるようです。
これはクライアント側に事前に設定されたリストのドメインは上記のヘッダーを認識していない状態でも
各ブラウザが指定されているリストからアクセス先のドメインが存在するかを探し、
HSTSによるHTTPS転送が行われのちアクセスするようです。
リスト入りを希望するのであれば`Strict-Transport-Security`ヘッダーに`preload`パラメータを設定する必要があるようです。


拡張仕様か何かと思ったら同RFC内の12章に定義がありました。
なお `This section is non-normative.` が見えたのでブラウザの対応がどうか次第...
と書こうと思ったんですが
[ここ]("https://rms.ne.jp/sslserver/basis/hsts/")曰く大概の最新のブラウザは対応しているらしいです。



# ヘッダー設定した見たけどうまくいかない...

自分が実装する前に別の環境でやってる人が設定したのに301の転送(Apacheの設定)になるんだが...ってハマってたので。

## ポートの設定は問題ないか

これは結局今回の問題になかったので実際の挙動は未確認ですが、
[RFC6797]("https://tools.ietf.org/html/rfc6797")内8.3において以下の記載があります

> if the URI contains an explicit port component of "80", then the UA MUST convert the port component to be "443", or if the URI contains an explicit port component that is not equal to "80", the port component value MUST be preserved; otherwise, f the URI does not contain an explicit port component, the UA MUST NOT add one. 

要は明示的にポートが指定されている場合かつポート80の場合は443に変換するけど、
それ以外の場合は保持してね！感じらしいです。

この部分のすぐ後ろの中役にも明示的にこれ以外のポートが指定されている場合は失敗するよって書いてあります。
なのでローカル環境などで8080等指定している環境はどうするんでしょう。
今回hostsファイルを設定した上でApacheの`VirtualHost`を設定して複数環境をポート80/443で持っていたので影響はありませんでした。

## 自己証明書を利用していないか

[RFC6797]("https://tools.ietf.org/html/rfc6797")の11.3に以下の記載があります。
今回知り合いはこれが原因で失敗していました。

>    If all four of the following conditions are true...
>   o  a web site/organization/enterprise is generating its own secure
>      transport public-key certificates for web sites, and
>
>   o  that organization's root certification authority (CA) certificate
>      is not typically embedded by default in browser and/or operating
>      system CA certificate stores, and
>
>   o  HSTS Policy is enabled on a host identifying itself using a
>      certificate signed by the organization's CA (i.e., a "self-signed
>      certificate"), and
>
>   o  this certificate does not match a usable TLS certificate
>      association (as defined by Section 4 of the TLSA protocol
>      specification [RFC6698]),
> ...then secure connections to that site will fail, per the HSTS design. This is to protect against various active attacks, as discussed above.

ざっくりかなり意味合いを省いて言えば自己証明書とか企業で独自で発行したやつ(多分PrivateCAとかの)証明書使ってるサイトは、
安全性のためにHSTSができないよ！って感じです。

ただし上記の部分の少し後ろにも書いてある通りそのCAをブラウザとかに登録すればいけるよってやつなので、
自己証明書を発行したときに使ったルート証明書をブラウザに登録するとか、
もしくは何かの環境で使ってるしっかりした証明書を持ってきてhostsファイル割り当ててやるとかで確認できるかと思います。


# 終わりに

知らない側からすればHTTPSに転送しているから大丈夫！みたいになることも多いですが
Web上でもあるURL入れたらフリーでセキュリティ診断できるようなサービスでも指摘されることが多い印象です。
設定自体はヘッダーのみですので影響等をみて問題なければ導入するのが良いでしょう。