SSL証明書でIPアドレスからホスト名が漏れる

# IPアドレスからWebサーバの名前がわかるよー

下の結果は、`curl` でIPアドレスにHTTPSでアクセスしただけです。

```
% curl -v https://198.51.100.15/
*   Trying 198.51.100.15...
* Connected to 198.51.100.15 (198.51.100.15) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=devel.keys.jp
*  start date: Jun 30 12:37:10 2020 GMT
*  expire date: Sep 28 12:37:10 2020 GMT
*  subjectAltName does not match 198.51.100.15
* SSL: no alternative certificate subject name matches target host name '198.51.100.15'
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):
curl: (60) SSL: no alternative certificate subject name matches target host name '198.51.100.15'
```

わかりますか？

```
* Server certificate:
*  subject: CN=devel.keys.jp
*  start date: Jun 30 12:37:10 2020 GMT
*  expire date: Sep 28 12:37:10 2020 GMT
*  subjectAltName does not match 198.51.100.15
```

Apache httpd のバーチャルホスト機能を使ってHTTPSサーバを複数ホストしている場合、IPアドレスでアクセスした場合 **一番最初に定義されたバーチャルホストが使われる** ので、その証明書を使って検証することになっています。

>アスタリスクはすべてのアドレスにマッチしますので、主サーバはリクエストを扱いません。www.example.com は 最初にあるため、優先順位は一番高くなり、default もしくは primary のサーバと考えることができます。つまり、リクエストがどの ServerName ディレクティブにもマッチしない場合、 一番最初の VirtualHost により扱われます。
https://httpd.apache.org/docs/2.4/ja/vhosts/examples.html

# いやそれは困る…

場合によっては困る場合もあるでしょう。
その場合、バーチャルホストの一番最初の定義に、ダミーの設定を挟むことで回避できます。


```apache:_default.conf
<VirtualHost _default_:443>
    LogFormat "%h %l %u %t ¥"%r¥" %>s %b ¥"%{Referer}i¥" ¥"%{User-Agent}i¥"" combined
    CustomLog logs/ssl_access_log combined

    Redirect 403 /

    SSLEngine on
    SSLCertificateFile conf.d/dummy.crt
    SSLCertificateKeyFile conf.d/dummy.key
</VirtualHost>
```

バーチャルホストをファイルごとに分けている場合、以下のように先頭にアンダーバーを付けたファイルがあれば最初に読み込まれます。
あるいは `ssl.conf` の最後に書いてもいいかもですね。

```
 $ ls -la /etc/httpd/conf.vhosts.d
total 8
drwxr-xr-x 2 keys keys   48 Feb  4 10:15 ./
drwxr-xr-x 7 root root  138 Jan 31 18:06 ../
-rw-rw-r-- 1 keys keys 1062 Feb  3 13:49 _defailt.conf
-rw-rw-r-- 1 keys keys 1092 Jan 31 23:21 devel.keys.jp.conf
```

この設定で使うSSL証明書は自己署名証明書（オレオレ証明書）です。
CSRを作るときは `Common Name` だけ入力すれば大丈夫です。証明書の `CN` フィールドは、グローバルで設定した `ServerName` にしないと httpd の起動時に怒られるみたいです。

```
$ openssl genrsa 2048 > dummy.key
Generating RSA private key, 2048 bit long modulus (2 primes)
........+++++
.......................................................................+++++


$ openssl req -new -key dummy.key > dummy.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

$ openssl x509 -days 3650 -req -signkey dummy.key < dummy.csr > dummy.crt
Signature ok
subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = localhost
Getting Private key
```

これでもう一度試してみると…

```
$ curl -v https://198.51.100.15/
*   Trying 198.51.100.15...
* Connected to 198.51.100.15 (198.51.100.15) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: self signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.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.
```

自己署名証明書だ、と言って怒られて処理が終わります。

これでホスト名が漏れることを防ぐことができました。しかしもう一つ、問題が残っています。

## “Redirect 403 /”

ブラウザが止めようとなんと言おうと、自己署名証明書を無視してアクセスすることはできます。
curl に `--insecure` （自己署名証明書を無視するオプション）を付けてアクセスしてみます。

```
% curl --insecure https://198.51.100.15/
<html>

<head>
<title>hoge</title>
</head>

<body>
<p1>Hoge!</p1>
</body>

</html>
```

この場合も先頭のバーチャルホストの内容が使われます。そこからホスト名やサービスの内容が漏れる場合もあるでしょう。

これを防ぐには、先の設定ファイルにはあらかじめ書いておきましたが、一行追加しておくと防げます。

```apache:
Redirect 403 /
```

すると、IPアドレスでのアクセスはすべて `403` になり、見えなくなります。

```
$ curl --insecure https://198.51.100.15/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
</body></html>
```