# SSL/TLS を触ってみよう
# 事前準備
このハンズオンでは、dockerをただの隔離環境として扱っています。
apache/nginx のハンズオンの際のものが残っているのであればそのまま流用できますので、以下の手順はスキップしてください。
以下のようにdocker pullをしたあと、ハンズオン用のコンテナを立ち上げてログインしてください。
$ docker pull python:latest
latest: Pulling from library/python
f32f49ce655a: Pull complete
8a7504cd2818: Pull complete
b53089dca505: Pull complete
8d6d44b254da: Pull complete
0a4465cc9f09: Pull complete
c965dce520b3: Pull complete
61719a06ef52: Pull complete
Digest: sha256:250e5c97be05e1eb2272fbdbd810dfd638f9012e1e6f65c99390ad3239943a08
Status: Downloaded newer image for python:latest
docker.io/library/python:latest
$ docker run --rm -itd --name test-debian -p 8080:80 -p 8082:82 -p 8088:88 -p 8089:89 -p 8443:443 -p 8444:444 python:latest /bin/bash
a0da070e286fd52ebb323e5faff9c960014bfcd8eb1e509cb5a12daa9fb9a85e
$ docker exec -it test-debian /bin/bash
root@a0da070e286f:/#
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
動作確認済みバージョン
root@eacb8fc335e0:/# uname -a
Linux eacb8fc335e0 6.8.0-90-generic #91~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 15:20:45 UTC 2 x86_64 GNU/Linux
root@eacb8fc335e0:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
NAME="Debian GNU/Linux"
VERSION_ID="13"
VERSION="13 (trixie)"
VERSION_CODENAME=trixie
DEBIAN_VERSION_FULL=13.5
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@eacb8fc335e0:/# python --version
Python 3.14.5
root@eacb8fc335e0:/#
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nginxをインストールします。
root@0e82a6ed6aae:/# apt update
Hit:1 http://deb.debian.org/debian trixie InRelease
Get:2 http://deb.debian.org/debian trixie-updates InRelease [47.3 kB]
Get:3 http://deb.debian.org/debian-security trixie-security InRelease [43.4 kB]
Get:4 http://deb.debian.org/debian trixie/main amd64 Packages [9671 kB]
Get:5 http://deb.debian.org/debian trixie-updates/main amd64 Packages [5412 B]
Get:6 http://deb.debian.org/debian-security trixie-security/main amd64 Packages [176 kB]
Fetched 9944 kB in 1s (7832 kB/s)
25 packages can be upgraded. Run 'apt list --upgradable' to see them.
root@0e82a6ed6aae:/# apt install -y apache2 apache2-dev nginx telnet neovim
Installing:
apache2 apache2-dev neovim nginx telnet
~~~略~~~
Setting up libapr1-dev (1.7.5-1) ...
Setting up libaprutil1-dev (1.6.3-3+b1) ...
Setting up debhelper (13.24.2) ...
Setting up apache2-dev (2.4.67-1~deb13u2) ...
Processing triggers for libc-bin (2.41-12+deb13u3) ...
Processing triggers for hicolor-icon-theme (0.18-2) ...
root@0e82a6ed6aae:/#
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
以下のコマンドでバージョンが表示されれば成功です。
root@0e82a6ed6aae:/# nginx -v
nginx version: nginx/1.26.3
2
# nginx の初期設定
nginxのサイトは88 portでリクエストを受け付けるようにします。
root@a0da070e286f:/# echo 'Hello Bootcamp!!' > /var/www/html/index.html
root@a0da070e286f:/# vim /etc/nginx/sites-enabled/default
2
server {
listen 88 default_server; # 80 => 88 に変更
listen [::]:88 default_server; # 80 => 88 に変更
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
変更したらnginxを起動しましょう。
root@a0da070e286f:/# service nginx start
[ ok ] Starting nginx: nginx.
2
localhost:8088 (opens new window) にアクセスしてみてください。Hello Bootcamp!!のHTMLが見えていれば成功です。

アクセスログも確認してみましょう。
root@a0da070e286f:/# tail /var/log/nginx/access.log
ここまでが事前作業となります。間に合わなければ、座学の間で準備してください。
# SSL/TLS とは(座学)
例えば、HTTP は基本的に平文でデータをやりとりします。
ということは、途中でパケットキャプチャをすると、やり取りの内容を読み取ることができます。
もしそこにパスワード情報など見られてはいけない情報が含まれていたら...怖いですね。
そこで、SSL/TLS (Secure Socket Layer/Transport Layer Securityの技術)を用いて通信路の暗号化を行うHTTP over SSL いわゆるHTTPS を重要な情報のやりとりを行う際には用いるのが一般的となっています。
SSL/TLSは通信路の暗号化として汎用性のあるものとなっており、HTTPに限らず様々なプロトコルでの暗号化に用いられています。
- ファイル転送: FTP -> FTPS
- メール: SMTP -> SMTPS, POP3 -> POP3S, IMAP -> IMAPS
プロトコルとまではなっていなくても、mysqlなどでもレプリケーションの経路を暗号化するために用いられていたりもします。
SSL/TLS の概要と歴史についてIPAの資料がよくまとまっているため、そちらの2章1節を読むのがよいでしょう。 ざっくりというと、公開鍵暗号を用いて共通鍵を共有し、その共通鍵を用いて以後の経路の暗号化を行う、というものになります。
TLS 暗号設定ガイドライン (opens new window)
この講義では、主にHTTPSをメインに触っていこうと思います。 また、以下ではSSL/TLSの総称としてTLSということにします。
# 証明書 とは(座学)
TLSは公開鍵暗号と共通鍵暗号の組み合わせという話をしました。 この公開鍵暗号の部分を担う重要なパーツとして証明書があります。
証明書には、以下の役割があります。
- 公開鍵暗号の公開鍵情報の共有手段
- 通信相手が確かにアクセスしようとしたドメインのサーバであることの確認手段
後者について、どのように確認が行えるのでしょうか?
試しにIIJの公式サイト (opens new window)の証明書を覗いてみましょう。
ブラウザでHTTPSなサイトを開くと、アドレスバーの横に鍵のマークが出ているかと思います。 そこから、証明書の情報を見ることができます。
まずは、本体の証明書を見てみましょう。 見るところは色々ありますが、この辺りを注意してみましょう。 ブラウザによって表記が異なる場合がありますので、いくつか名称は並べて書きます
- 共通名、一般名、Common Name(CN)
- 基本的にはサイトのドメイン
- 昔は、ここのドメインをもって確認をしても良いことになっていましたが、現在では禁止されています(RFC9110)
- 組織、Organization(O)
- この証明書を用いたサイトの管理組織
- 有効期間
- この証明書の有効期間
- 昔は5年ものなどもありましたが、年々商用証明書の有効期限は短縮されています。詳しくは後述します。
- 主体者代替名、サブジェクトの代替名、サブジェクトの別名、Subject Alternative Names(SANs)
- いくつか項目がありますが、このうち重要なのはDNS名。この証明書が有効であるサイトを示しています。
- 複数存在しうる
- 現在では、証明書が管理するサイトを示す唯一の項目となります。
- 公開鍵
- この証明書が提供する公開鍵。これを用いて共通鍵の共有を行います。
ここまで確認したところで、証明書に添付された公開鍵を用いて暗号化通信を始めてよいのでしょうか? ここまでで確認したことは、単に私はこのサイトの管理者だぞ!と自称しているに過ぎません。 信用して暗号化した経路でクレジットカードの情報を送ったのに、実は送った相手は偽サイトを運用する悪人だった、 となったら目も当てられません。 確かにこのサイトの管理者だと信じるには、信頼できる第三者の担保が欲しいですね。
ここで登場するのが、認証局(Certification Authority: CA)です。
証明書に戻ると、発行者(発行元、Issuer)と証明書の署名という項目があるのがわかります。 具体的な検証手段は割愛しますが、この署名を検証することで、確かに証明書が発行者に認められたものであることがわかります。
では、その発行者は信頼できるのでしょうか? 証明書の情報を見ると、階層構造のようになっている部分があり、本体の証明書の上に発行者の証明書が表示されているかと思います。 発行者の証明書もまた同じような構造をしており、有効期間や組織などの情報はもちろん、またその上位の発行者と署名があります。 こうして証明書のチェインがつながっていった結果、最終的にRootCA の証明書に行きつきます。
ブラウザのユーザは、少なくともそのブラウザは信用して使っているのだと思います。 ブラウザは、そのブラウザを含めて世界的に信用している認証局の証明書を、リストとして保持しています。 その認証局がRootCAと呼ばれているものです。 証明書のチェインがつながって、RootCAのものまで辿れれば、それは信頼できる認証局が証明したものとして信用できる、と判断できるようになります。
このような証明書のチェインを通じて公開鍵の正当性を担保するための枠組みを公開鍵基盤(Public Key Infrastrcture:PKI)と呼びます。
TIP
政府がRootとして正当性を担保する、政府認証基盤(Government PKI:GPKI)というものもあります。 例えばマイナンバーカードの証明書は公的個人認証サービス(JPKI)というものが発行していますが、JPKIの正当性はGPKIと相互認証する形で担保していたりします。
# 実際に手を動かしてみる
まずは、証明書を作ってみて、nginxでhttpsの通信が出来るようにしてみましょう。
# 証明書と秘密鍵を作ってみる (check1)
通常、証明書は以下の手順で入手します。
- 秘密鍵を生成する
- 秘密鍵からCSR (Certificate Signing Request) を生成する
- CSR を証明局に提出し、審査を受け、証明局の持つ秘密鍵で署名された証明書を発行してもらう
秘密鍵を用いると、暗号化した通信を復号することが出来ます。 そのため、絶対に外部に漏らしてはいけません。例え証明局であっても例外ではありません。 証明書の発行には公開鍵があれば十分なため、秘密鍵から生成した公開鍵と、 その他発行に必要な情報を盛り込んだCSRを用いて証明書を発行します。
ここでは、3を簡略化して1 で生成した鍵で署名する、自己署名証明書(いわゆるオレオレ証明書)を作ります。 このdocker image に既にインストールされている、openssl ツールで一通りの操作を行うことができます。
# 1. 秘密鍵を生成する
ここではRSA の2048 bit の秘密鍵を生成します。
TIP
サブコマンドであるgenrsa はRSA 暗号の秘密鍵を生成するものとなります。
root@a0da070e286f:/# mkdir /etc/nginx/ssl
root@a0da070e286f:/# openssl genrsa 2048 > /etc/nginx/ssl/private.key
Generating RSA private key, 2048 bit long modulus (2 primes)
........................+++++
...........................................................................................................................+++++
e is 65537 (0x010001)
2
3
4
5
6
# 2. 秘密鍵からCSR (Certificate Signing Request) を生成する
1 で作った秘密鍵から、CSR を生成します。
TIP
サブコマンドであるreq はCSR を扱うためのものとなります。
証明書で表示する情報をここで入力することになります。 実際に発行する際は、正当性を担保したい対象であるCommon Name は特に間違わないようにしましょう。
root@a0da070e286f:/# openssl req -new -sha256 -key /etc/nginx/ssl/private.key -out /etc/nginx/ssl/server.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]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Chiyoda
Organization Name (eg, company) [Internet Widgits Pty Ltd]:IIJ
Organizational Unit Name (eg, section) []: Test
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 []:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3. 署名された証明書を発行する
1 で作った秘密鍵、2 で作ったCSR から証明書を発行します。
TIP
サブコマンドであるx509 は、証明書の標準規格を指しています。 -req でinput がCSR であることを示し、signkey に1 で作った秘密鍵を指定することでこれで署名します。
root@a0da070e286f:/# openssl x509 -req -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt -signkey /etc/nginx/ssl/private.key -days 365
Certificate request self-signature ok
subject=C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
2
3
出来上がったら、証明書の中を覗いてみましょう。text オプションでテキスト出力をすることができます。
root@a0da070e286f:/# openssl x509 -in /etc/nginx/ssl/server.crt -text
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
45:ef:45:48:8c:89:e0:e5:38:74:f7:fc:21:32:35:eb:2b:bc:10:6b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
Validity
Not Before: Aug 1 16:29:36 2022 GMT
Not After : Aug 1 16:29:36 2023 GMT
Subject: C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
Subject Public Key Info:
(...省略...)
2
3
4
5
6
7
8
9
10
11
12
13
14
実際に発行されたものを確認する際は、期間(Not BeforeとNot After)とSubject (CN が正しいか)に特に注意しましょう。
秘密鍵と証明書のペアが正しいかを確認するには、RSA のものならmodulus を比較するのが簡単です。
root@a0da070e286f:/# openssl rsa -in /etc/nginx/ssl/private.key -modulus -noout
Modulus=FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B
root@a0da070e286f:/# openssl x509 -in /etc/nginx/ssl/server.crt -modulus -noout
Modulus=FB1908BE2B1567D1B8B7EE99DF3480CE2EDF57EC73ADD08AE2FA37A833321C84CF49D6D3F8011419BDAF8882B6E610C097D7016D173A14B7343E8D1381B8CF7FCD14CAA5717594B6F5CD586BF13EB90D2673E03B73EB25463333BD8D4384477C7910E87C8CEB2E71C83E59DD3BAC61E9B19DB97545AA9DB96DC995B01B2F96FA62CD8C777C0DA3A0377F71E0F6251CE7511964F2B4604D7F88472759C0178ECA1C7B21F9D9198166F28097A6EDF76925247119B7BEBDA73DD387607BD6320444E0242E127108C234B7F0D6CD6EB7E496747BDE7249E606BA44024E1FCC61E9ADBBE1BDABE51B342AF7DA5801AE36393E11EFFFAE60047EA7FE1E8E9A12FFF57B
2
3
4
5
# https の設定(check2)
事前作業で作ったhttp で受けていたサイトをhttps でも受けられるようにしてみます。
/etc/nginx/sites-enabled/default の一番下に以下を丸ごと追記していきます。
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
追記したら、nginx をリスタートしましょう。
root@dea1ac0e1edb:/# service nginx restart
[ ok ] Restarting nginx: nginx.
2
確認をしてみましょう。 今回は、https での通信となるため、URLの先頭がhttpではなくhttpsとなっています。 https のデフォルトは443ポートのため、ポート指定の部分は省略可能です。
root@dea1ac0e1edb:/# curl https://localhost:443/
curl: (60) SSL certificate problem: self-signed certificate
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.
2
3
4
5
6
7
今回は自己署名証明書であるため、curl は正規の証明書ではないとして検証エラーとなります。 無視してコンテンツを取得するには、--insecure(-k) オプションを使います。
root@dea1ac0e1edb:/# curl -k https://localhost:443/
Hello Bootcamp!!
2
渡される証明書を見てみたい場合は、openssl のs_client サブコマンドを用いるとよいでしょう。 ここでは、繋がったらCtrl+C で抜けます。
root@dea1ac0e1edb:/# openssl s_client -connect localhost:443
CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
verify error:num=18:self-signed certificate
verify return:1
depth=0 C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
verify return:1
---
Certificate chain
0 s:C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
i:C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: Aug 3 16:41:31 2025 GMT; NotAfter: Aug 3 16:41:31 2026 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDQzCCAisCFCeXemGW/mzBWsHN4rmA9eHstX27MA0GCSqGSIb3DQEBCwUAMF4x
(中略)
G9hAF0OGhQagV6jcIgjGYk0iIITzss6fujD+eSAwDfp685F84jsayUVdkBnCqVMy
1c1kiqV+wv2XaWHvm2pl/zX94ReoGv4=
-----END CERTIFICATE-----
subject=C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
issuer=C = JP, ST = Tokyo, L = Chiyoda, O = IIJ, OU = Test, CN = localhost
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1395 bytes and written 377 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)
---
(中略)
Start Time: 1754239905
Timeout : 7200 (sec)
Verify return code: 18 (self-signed certificate)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK
(Ctrl+C を入力して抜ける)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
openssl s_client は、TLSのhandshakeを行って暗号化した経路を用いるtelnetのようなものです。 ここではCtrl+Cで抜けましたが、続けてHTTPのリクエストを送るとHTTPSの通信となります。
ブラウザで確認する場合は、443 は8443 にポートフォワードの設定が入っているため、8443 ポートにアクセスしてみましょう。
https://localhost:8443/ (opens new window)
今回は自己署名証明書であるため、ほとんどのブラウザは正当な証明書ではないと判断し、注意喚起の画面が表示されます。
危険性を承知で閲覧すると、Hello Bootcamp!!の表示が確認できるかと思います。
また、ブラウザ上で暗号化に使っている証明書の内容が確認できるので、確認もしてみましょう。
# 証明書 の有効期限(座学)
証明書に関する業界標準については主にCA/Bフォーラム (opens new window)で議論されます。 証明書を発行する証明局(CA)はもちろん、利用者側として暗号化通信の信頼性に責任を持つブラウザベンダ(B)などが メンバー (opens new window)となっています。 昨今は、Web以外でも証明書は活用されているため、それらのアプリやOSのベンダもConsumerとして参加しています。
CA/Bフォーラムで長らく話題になっていたのが、証明書の最長有効期限の短縮です。 秘密鍵の漏えい対策、仕様変更のサイクルの高速化、などが理由とされています。 最初は特に制限なしだったところから、5年、39か月、825日と段々と縮んでいき、 2020/9より397日まで短縮されていました。 そして、2025/4/11、新たな短縮スケジュールがCA/Bフォーラムで可決されました。
当面の有効期限は以下の通りです。
- 2026/3/14 までは、これまで通り397日
- 2027/3/14 までは、200日
- 6か月 + 1か月の半分 + 1日
- 2029/3/14 までは、100日
- 3か月 + 1か月の1/4 + 1日
- 2029/3/15 以降は、47日
- 1か月 + 1か月の半分 + 1日
ここまで短くなると、手動で更新し続けることは困難となります。
そこで、証明書更新についてACMEプロトコル (opens new window)を利用して自動化することができます。 動作の概要はLet's Encryptのページ (opens new window)がわかりやすいです。
- ACMEクライアント (opens new window)
- 証明書を自動取得する際にクライアントサーバー側で動作するソフトウェアです。
Certbot や Lego 、cert-manager など多数のクライアントがあります。
- 証明書を自動取得する際にクライアントサーバー側で動作するソフトウェアです。
- チャレンジ (opens new window)
- 証明書が証明しようとしているドメイン名が自身のものであることを自動的に確認するために「チャレンジ」を行います。 HTTP プロトコルを利用する HTTP-01 や、 DNS プロトコルを利用する DNS-01, DNS-PERSIST-01 がありあます。
# 詳細な暗号設定
TLSの設定としては、主に、プロトコルのバージョン、暗号スイート、証明書、に何を使うかが大事になります。
# protocol を設定してみる(check3)
プロトコルについては、既にTLSv1.1 までは主要なブラウザやOSで禁止扱いになっていたりします。
サーバー側で受け入れる TLS バージョンは、nginx だとssl_protocolsで設定します。
まずは、デフォルトの状態で接続を確認してみましょう。TLSv1.2で接続できています。
root@34cfcf7b6f05:/# curl -vvv -k --tls-max 1.2 https://localhost:443
* Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
(中略)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.22.1
< Date: Thu, 01 Aug 2024 01:58:28 GMT
< Content-Type: text/html
< Content-Length: 17
< Last-Modified: Mon, 29 Jul 2024 23:13:24 GMT
< Connection: keep-alive
< ETag: "66a82214-11"
< Accept-Ranges: bytes
<
Hello Bootcamp!!
* Connection #0 to host localhost left intact
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
では、/etc/nginx/nginx.conf に設定があるので、TLSv1.2 は受け入れないように書き換えてみましょう。 書き換えたら再起動して反映させます。
root@34cfcf7b6f05:/# vim /etc/nginx/nginx.conf
(前後省略)
##
# SSL Settings
##
ssl_protocols TLSv1.3; # Dropping SSLv3 (POODLE), TLS 1.0, 1.1 <= ここから TLSv1.2 を消してみる
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
(前後省略)
root@34cfcf7b6f05:/# service nginx restart
2
3
4
5
6
7
8
9
10
11
12
13
14
設定してみたら、接続できなくなることを確認してみましょう。
root@34cfcf7b6f05:/# curl -vvv -k --tls-max 1.2 https://localhost:443
* Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS alert, protocol version (582):
* OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version
* Closing connection 0
curl: (35) OpenSSL/3.0.9: error:0A00042E:SSL routines::tlsv1 alert protocol version
2
3
4
5
6
7
8
9
10
# 暗号スイートを設定してみる(check4)
暗号スイートは、
- 鍵交換方式
- 署名方式
- 暗号化方式
- ハッシュ関数
の組で構成されます。TLS1.3からは、ここから鍵交換方式、署名方式が外されて暗号化方式、ハッシュ関数で構成されます。 (鍵交換や署名の部分がプロトコルに最初から組み込まれるようになったため、指定する必要がなくなりました)
openssl コマンドで、扱える暗号スイートの一覧を見ることが出来るので、見てみましょう。
root@34cfcf7b6f05:/# openssl ciphers -v
TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any Au=any Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256 TLSv1.3 Kx=any Au=any Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD
(以下略)
2
3
4
5
6
7
8
一番上のものは、TLS1.3 のものなので、暗号化がAES256のGCMモード、ハッシュ関数がSHA384 4番目のものは、TLS1.2 のものなので、鍵交換がECDHE、署名がECDSA、暗号化がAES256のGCMモード、ハッシュ関数がSHA384 といった感じになります。
注意する点として、この表記はOpenSSL流のものですが、世の中にはIANAが定めた表記も存在しています。 技術文書で出てきたものが見当たらないな、と思ったら別表記のものかもしれません。
参考
nginxでは、ssl_ciphersを用いて設定します。
root@34cfcf7b6f05:/# vim /etc/nginx/nginx.conf
(前後省略)
##
# SSL Settings
##
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE <= TLSv1.2 を元に戻す
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
# ↑↑↑ この行を追加
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
(前後省略)
root@34cfcf7b6f05:/# service nginx restart
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TIP
ssl_ciphers の設定部分は、openssl ciphers の引数に渡すことで、この設定で使えるcipher の一覧を表示させることができます。 今回は列挙した形ですが、!EXP のようにブラックリスト的な書き方もできるため、よくわからなくなったらこのように実際に設定されるものを確認するとよいでしょう。
openssl ciphers -v "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305"
openssl ciphersの一覧から適当なものを選んで接続してみて、nginx.confで設定したものは接続でき、設定しなかったものは接続できないことを確認してください。
root@34cfcf7b6f05:/# curl -vvv -k --tls-max 1.2 --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://localhost:443
* Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
(中略)
Hello Bootcamp!!
root@34cfcf7b6f05:/# curl -vvv -k --tls-max 1.2 --ciphers AES256-SHA256 https://localhost:443
* Trying 127.0.0.1:443...
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* Cipher selection: AES256-SHA256
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS alert, handshake failure (552):
* OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure
* Closing connection 0
curl: (35) OpenSSL/3.0.9: error:0A000410:SSL routines::sslv3 alert handshake failure
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 証明書について(check5)
証明書については、発行形式は証明局側がよしなにするので、こちらで主に気にすべきは秘密鍵の方です。 鍵長長ければ長いほどセキュリティ強度は高まりますが、復号するために余計にリソースを消費することになるため、 そこはトレードオフとなります。
ECDSAは暗号の特性上、短い鍵長でも高いセキュリティ強度を保つことができます。 RSAであれば2048bit、ECDSAであれば256bit のものを使うのがよいでしょう。 (量子コンピューターが実用化した場合はプロトコルや鍵長を見直す必要があります。詳しくは後述します。)
ここでは、試しにECDSAの秘密鍵を用いて証明書を作って使用してみます。 openssl コマンドで、ECDSAの秘密鍵も生成可能です。
root@a0da070e286f:/# openssl ecparam -genkey -name prime256v1 > /etc/nginx/ssl/ecdsa.key
生成した鍵を見てみると、形式がRSAのものと異なり、短くなっていることが見て取れるかと思います。 ここから先の手順は、RSAの時と同様です。出力も同様なので省略します。
root@34cfcf7b6f05:/# openssl req -new -sha256 -key /etc/nginx/ssl/ecdsa.key -out /etc/nginx/ssl/ecdsa.csr
root@34cfcf7b6f05:/# openssl x509 -req -in /etc/nginx/ssl/ecdsa.csr -out /etc/nginx/ssl/ecdsa.crt -signkey /etc/nginx/ssl/ecdsa.key -days 365
2
出来上がったものを見てみると、Public Key Info が変わっています。
root@34cfcf7b6f05:/# openssl x509 -in /etc/nginx/ssl/ecdsa.crt -text
(中略)
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:6b:b0:f6:cc:b5:8d:6a:b9:93:f2:1d:50:ec:4e:
59:82:39:70:33:61:f3:b4:3b:13:42:39:1d:68:27:
5a:27:3d:0a:df:14:78:3d:aa:fb:ec:f5:a8:8b:87:
3b:bc:cc:f7:47:b3:84:db:85:a5:e5:2e:9c:03:44:
8f:37:65:2e:e4
ASN1 OID: prime256v1
NIST CURVE: P-256
Signature Algorithm: ecdsa-with-SHA256
2
3
4
5
6
7
8
9
10
11
12
13
14
後は、nginx の参照を変更し、この証明書を使ってhttpsを提供してみましょう。
root@34cfcf7b6f05:/# vim /etc/nginx/sites-enabled/default
(中略)
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/nginx/ssl/ecdsa.crt; # <= ここを書き換え
ssl_certificate_key /etc/nginx/ssl/ecdsa.key; # <= ここを書き換え
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
(後略)
root@34cfcf7b6f05:/# service nginx restart
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ECDSAの鍵を用いたものでも問題なく接続できるかと思います。
パフォーマンスの観点からはECDSAの方が有利ですが、中にはECDSAに対応していないサービスもあります。 利用してみたい場合も証明書を使用するサービスで使えるかどうか確認してから発行するようにしましょう。
# 安全とされている設定(座学)
日本における暗号設定のセキュリティ上の最低ラインは先の暗号設定ガイドラインの3.1に記載されています。
TLS 暗号設定ガイドライン (opens new window)
表14がわかりやすいでしょう。ビットセキュリティについては、2.6の表11がわかりやすいです。
mozilla の推奨暗号スイートも参考になります。
mozilla Server Side TLS (opens new window)
今回のcipher設定についてはmozilla のintermediate のものを利用しています。 こちら (opens new window)で対応する設定を出力してくれるのも便利です。
安全とされる設定は技術進化や技術革新によって変化していきます。 最新の推奨設定を把握し、必要に応じて暗号スイートをより強固なものにすることが大切です。
特に、量子コンピューターが実用化した時は 2048bit RSA や 256bit ECDSA ですら短時間で突破されてしまう可能性があります。
量子コンピューターによる暗号解読に対して安全だと考えられる暗号アルゴリズムを「耐量子暗号」 (PQC: Post-Quantum Cryptography) と呼びます。 現在のところ、量子コンピューターは公開鍵暗号アルゴリズムへの脅威となっている一方で、多くの共通鍵暗号やハッシュ関数は量子コンピュータからの攻撃に対して比較的安全と考えられています。 耐量子暗号アルゴリズムについても研究・議論が続いており、こちらも定期的に確認するとよいでしょう。