証明書エラー時でのHSTSヘッダは意味があるのか

HSTSヘッダがいまいちわかっていなかったので、これを機会にヘッダに対して理解を深めたいと思います。
今回の検証では、同一対象（80ポート、443ポート、証明書エラーがある443ポート）に対してブラウザとcurlコマンドでアクセスしてみてそれぞれの違いを観察しました。

HSTSヘッダを利用しているサイトの実例として、今回は©明治大学 情報セキュリティ研究室の齋藤孝道先生が公開されている「Webアプリケーションの脆弱性の解説＆体験ページ」を使います。HSTSヘッダを見るだけであればサイトのコンテンツはあまり関係ありませんが、このサイトは脆弱性を学ぶ上で非常にわかりやすいサイトとなっておりますので、この機会にご一読いただくと、とくに初学者にはおすすめだと思います。

https://contents.saitolab.org/taiken/

※不正アクセス禁止法などに抵触しないか確認してから検証してください。

# 80ポート
http通信は、通信内容が暗号化されません。そのため、httpからはじまるサイトを閲覧すると、通信内容が誰からでも容易に見られてしまうリスクがあります。例えば、カフェや空港で提供されている無料の公衆Wi-Fi経由でhttpサイトにアクセスし、サイトにログインするためにIDやパスワードを入力してしまうとその秘匿情報が誰からでも容易に見られてしまうリスクがあるということです。

## ブラウザで確認
対象に対して、ブラウザでアクセスします。その際のHTTPレスポンスヘッダを確認します。

`http://contents.saitolab.org/samples/login.html`

```
HTTP/1.1 302 Found
Date: Tue, 25 Jan 2022 12:00:00 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Frame-Options: SAMEORIGIN
Location: https://contents.saitolab.org/samples/login.html
Content-Length: 319
Connection: close
Content-Type: text/html; charset=iso-8859-1
```
Locationヘッダにより、`https://contents.saitolab.org/samples/login.html`へ遷移しました。

遷移先：`https://contents.saitolab.org/samples/login.html`

```
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 942
Content-Type: text/html
Date: Tue, 25 Jan 2022 12:00:00 GMT
Etag: W/"942-1384843284000-gzip"
Last-Modified: Tue, 19 Nov 2013 06:41:24 GMT
Strict-Transport-Security: max-age=31536000
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Connection: close
Server: nghttpx nghttp2/0.6.6
Via: 1.1 nghttpx
```

## curlコマンドで確認
対象に対して、curlコマンドでアクセスします。`-I`はレスポンスヘッダのみを表示するオプション。
ブラウザと違ってリダイレクトされた後の通信までは確認できませんでした。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I http://contents.saitolab.org/samples/login.html                          
HTTP/1.1 302 Found
Date: Tue, 25 Jan 2022 12:11:07 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Frame-Options: SAMEORIGIN
Location: https://contents.saitolab.org/samples/login.html
Content-Type: text/html; charset=iso-8859-1
```

curlでリダイレクトの遷移を追う場合は、`-L`オプションを指定します。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I -L http://contents.saitolab.org/samples/login.html
HTTP/1.1 302 Found
Date: Tue, 25 Jan 2022 12:15:56 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Frame-Options: SAMEORIGIN
Location: https://contents.saitolab.org/samples/login.html
Content-Type: text/html; charset=iso-8859-1

HTTP/2 200 
accept-ranges: bytes
content-length: 942
content-type: text/html
date: Tue, 25 Jan 2022 12:15:56 GMT
etag: W/"942-1384843284000"
last-modified: Tue, 19 Nov 2013 06:41:24 GMT
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
server: nghttpx nghttp2/0.6.6
via: 1.1 nghttpx
```

HTTPのURLにアクセスすると、HTTPSにリダイレクトしていることが分かります。これは、HTTPSを強制する目的で伝統的に使われてきた方法ですが、近年この対応だけでは不十分だとみなされるようになりました。
たとえば、利用者がWi-Fiの偽アクセスポイントを掴んでしまった場合、中間者攻撃により偽のサイトにアクセスを強要される可能性があります。偽のサイトではHTTPSへのリダイレクトもないため、利用者は気づかないままに偽サイトにHTTPでアクセスし続けることになります。
一方、ブラウザ側で強制的にHTTPSに誘導する機能があれば、偽サイトにアクセスした場合でも、ブラウザ側でHTTPSに強制することができます。この場合でも偽サイトにアクセスさせること自体は可能ですが、偽サイトには正規の証明書がないため、証明書エラーが表示されることによって、利用者は異常に気づくことができます。

# 443ポート
httpsでは、SSL／TLSを利用したセキュアな通信が可能となります。httpsのサイトでは第三者によって発行される「SSLサーバー証明書」を導入しています。この証明書の存在により、通信内容を暗号化して盗聴を防止できたり、ハッシュ関数を用いてデータの改ざん有無を検知できます。また、正規の証明書をサイトに導入することで、そのサイトが信頼できるか確認できます。

## ブラウザで確認
対象に対して、ブラウザでアクセスします。その際のHTTPレスポンスヘッダを確認します。

`https://contents.saitolab.org/samples/login.html`

```
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 942
Content-Type: text/html
Date: Tue, 25 Jan 2022 10:03:37 GMT
Etag: W/"942-1384843284000-gzip"
Last-Modified: Tue, 19 Nov 2013 06:41:24 GMT
Strict-Transport-Security: max-age=31536000
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Connection: close
Server: nghttpx nghttp2/0.6.6
Via: 1.1 nghttpx
```

## curlコマンドで確認
対象に対して、curlコマンドでアクセスします。その際のHTTPレスポンスヘッダを確認します。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I https://contents.saitolab.org/samples/login.html                         
HTTP/2 200 
accept-ranges: bytes
content-length: 942
content-type: text/html
date: Tue, 25 Jan 2022 11:57:06 GMT
etag: W/"942-1384843284000"
last-modified: Tue, 19 Nov 2013 06:41:24 GMT
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
server: nghttpx nghttp2/0.6.6
via: 1.1 nghttpx
```

# HSTSヘッダ
ここで注目したいのが、`Strict-Transport-Security`ヘッダです。これは、ウェブサイトがブラウザに対してHTTPの代わりにHTTPSを用いて通信をする用に指示するためのヘッダです。

HTTPからHTTPSにリダイレクトする際に中間者攻撃のリスクがあるので、ブラウザ側に、「http:// と打ち込まれても https:// でアクセスしてね」と覚えさせるための機能です。

今回、`Strict-Transport-Security: max-age=31536000`と表示されているため、再度アドレスバーに`http://contents.saitolab.org/samples/login.html`と指定しても、自動的に`https://contents.saitolab.org/samples/login.html`にアクセスすることが確認できました。

ちなみに、`Strict-Transport-Security`ヘッダがない場合や有効期間を過ぎた場合、「http://」でアクセス可能となります。

ただし、今回のようにいちいちレスポンスヘッダを確認するのが面倒な人は「ChromeでHSTS状態を確認する」とよいでしょう。参考にしたサイトは以下です。

https://chidakiyo.hatenablog.com/entry/chrome-hsts

# 証明書に不備がある場合だと

さて、証明書に不備があるサイトにアクセスした場合、どうなるでしょうか。
まず、証明書に不備があるサイトを用意します。とは言っても、さすがにすぐには見つけられないので、今回の対象に対してfqdnではなくIPアドレスで直接アクセスしてみます。

```
┌──(hoge㉿hoge)-[~]
└─$ dig contents.saitolab.org                   
...(略）...
;; ANSWER SECTION:
contents.saitolab.org.	258	IN	CNAME	saitolab.org.
saitolab.org.		258	IN	A	133.26.81.168
...(略）...
```

IPアドレスは`133.26.81.168`と判明しました。

# （※証明書エラーがある）80ポート
※証明書エラーがある： わかりやすいタイトルにしているだけです。
## ブラウザで確認

`http://133.26.81.168/samples/login.html`

```
HTTP/1.1 302 Found
Date: Tue, 25 Jan 2022 13:11:13 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Frame-Options: SAMEORIGIN
Location: https://contents.saitolab.org/samples/login.html
Content-Length: 311
Connection: close
Content-Type: text/html; charset=iso-8859-1
```

Locationヘッダにより、`https://contents.saitolab.org/samples/login.html`へ遷移しました。

遷移先：`https://contents.saitolab.org/samples/login.html`

```
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 942
Content-Type: text/html
Date: Tue, 25 Jan 2022 13:11:14 GMT
Etag: W/"942-1384843284000-gzip"
Last-Modified: Tue, 19 Nov 2013 06:41:24 GMT
Strict-Transport-Security: max-age=31536000
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Connection: close
Server: nghttpx nghttp2/0.6.6
Via: 1.1 nghttpx
```

## curlコマンドで確認
curlでリダイレクトの遷移を追う場合は、`-L`オプションを指定します。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I -L http://133.26.81.168/samples/login.html
HTTP/1.1 302 Found
Date: Tue, 25 Jan 2022 13:14:56 GMT
Server: Apache/2.4.18 (Ubuntu)
X-Frame-Options: SAMEORIGIN
Location: https://contents.saitolab.org/samples/login.html
Content-Type: text/html; charset=iso-8859-1

HTTP/2 200 
accept-ranges: bytes
content-length: 942
content-type: text/html
date: Tue, 25 Jan 2022 13:14:56 GMT
etag: W/"942-1384843284000"
last-modified: Tue, 19 Nov 2013 06:41:24 GMT
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
server: nghttpx nghttp2/0.6.6
via: 1.1 nghttpx
```

# 証明書エラーがある443ポート
## ブラウザで確認
対象に対して、ブラウザでアクセスします。「警告: 潜在的なセキュリティリスクあり」と表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217966/599c8867-7386-4041-0eaf-722187da8fa3.png)

証明書に不備がある状態でアクセスできたことを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217966/6f7bd4c5-88c9-69b9-2c47-b01f8cad38f8.png)

`https://133.26.81.168/samples/login.html`

```
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 942
Content-Type: text/html
Date: Tue, 25 Jan 2022 12:55:06 GMT
Etag: W/"942-1384843284000-gzip"
Last-Modified: Tue, 19 Nov 2013 06:41:24 GMT
Strict-Transport-Security: max-age=31536000
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
Connection: close
Server: nghttpx nghttp2/0.6.6
Via: 1.1 nghttpx
```

## curlコマンドで確認

証明書に不備があるため、怒られてしまいました。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I https://133.26.81.168/samples/login.html        
curl: (60) SSL: no alternative certificate subject name matches target host name '133.26.81.168'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
```

ただし、状況によっては「どーしても、アクセスしたいんだ！」という方もいるかもしれません。
その場合は、`-k`オプションをつけます。

```
┌──(hoge㉿hoge)-[~]
└─$ curl -I -k https://133.26.81.168/samples/login.html                                                                      60 ⨯
HTTP/2 200 
accept-ranges: bytes
content-length: 942
content-type: text/html
date: Tue, 25 Jan 2022 13:02:51 GMT
etag: W/"942-1384843284000"
last-modified: Tue, 19 Nov 2013 06:41:24 GMT
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
server: nghttpx nghttp2/0.6.6
via: 1.1 nghttpx
```
ただ、このオプションは証明書のエラーを無視する指定なので、正常系のアクセス確認ではほとんど使うことが無いと思います。

さて、証明書に不備があるものの`strict-transport-security`ヘッダがついていることが確認できます。今回の場合は果たして問題ないのでしょうか。

結論から言うと、証明書エラーの場合、`strict-transport-security`ヘッダは原理的に無意味です。
なぜなら、`strict-transport-security`ヘッダは『HTTPからHTTPSにリダイレクトする際に中間者攻撃のリスクがあるので、ブラウザ側に、「http:// と打ち込まれても https:// でアクセスしてね」と覚えさせるための機能』なので、そもそも証明書に不備がある場合、https:// でアクセスしても中間者攻撃のリスクは回避できません。

よって、`strict-transport-security`ヘッダの状況を確認する場合は、正規の証明書が使われているという前提が必要だということが分かりました。