🔑

HTTPSサーバーを作成し、SSL/TLSについて深掘りした

2023/05/06に公開

概要

現代ではSSL/TLSを利用してHTTPSによる暗号化通信を行うことは当たり前になっています。
様々なサービスやツールが現れたため、HTTPS通信を簡単に利用できるようになった一方で、その実態や仕組みについて詳しく触れることはあまりありませんでした。

本記事ではNodeJSでのHTTPSサーバー実装を通じて、その詳細について学んだ記録兼体験記になります。

SSL/TLSについて

SSL/TLSは通信を暗号化することによってセキュリティリスクを低減するための仕組みです。
http通信では情報をそのまま生で送信してしまうため、その通信を盗聴されると情報が抜き取られてしまうリスクがあります。これを回避するために通信する情報を暗号化する必要があり、SSL/TLSはこの暗号化通信のデファクトスタンダードになっています。

SSL/TLS通信の手順

SSL/TLS通信の実態は以下のようになっています。
画像
画像は「ねこまるのADフリーク」様よりお借りしています

手順についてざっくり要約すると次のようなものです。

  1. 通信開始のハンドシェイク行う
  2. サーバーが「デジタル証明書」と「公開鍵」を送信する
  3. クライアントが「デジタル証明書」の確認をする
  4. クライアントが送信された「公開鍵」を利用して「共通鍵」をサーバーに伝える[1][2]
  5. 共通鍵」で暗号化通信をする

ポイントは以下の2点です。

  1. デジタル証明書を送る
  2. 安全に共通鍵をクライアントとサーバーで共有する

従ってサーバーには「デジタル証明書」と「鍵」の2つの用意が必要となります。
暗号化や鍵の詳細については今回は触れません。詳細を知りたい人は下のURL等を参考にしてください。
https://medium-company.com/ssl-仕組み/
https://qiita.com/n-i-e/items/41673fd16d7bd1189a29

httpsサーバーの実装

デジタル証明書の発行

証明書を以下のコマンドで発行します[3]

$ openssl req -subj '/C=JP/ST=TestState/O=TestCompany' -addext 'subjectAltName = IP:127.0.0.1'  -x509 -nodes -days 3650 -newkey rsa:2048  -keyout ./server-key.pem  -out ./server-cert.pem

このコマンドを実行すると以下の2つのファイルが出力されます。

  • server-key.pem
  • server-cert.pem

server-key.pemは暗号化用の秘密鍵になります。今回はRSAによる暗号化を行っています。server-cert.pemはデジタル証明書です。署名は自分自身で行っています。

今回の実装では署名を自分自身で行ったデジタル証明書、通称「オレオレ証明書」を使用します。
ブラウザは証明書の署名が登録されている認証局によるものではない場合に「安全でない接続」と判断するようになっています。ブラウザの設定変更を特に行っていない場合、自分自身による署名はブラウザに「安全でない接続」と判断されてしまいます。[4]

サーバー作成

Nodeの標準ライブラリ「https」を使用して実装します。
サーバーアプリケーションとして、index.jsを作成します。

index.js
const https = require('https')
const fs = require('fs')

const host = '127.0.0.1'
const port = 8443
const options = {
  key: fs.readFileSync('./server-key.pem'),
  cert: fs.readFileSync('./server-cert.pem'),
}

const server = https.createServer(options)

server.on('request', (req, res) => {
  res.writeHead(200)
  res.write('Hello World!\n')
  res.end()
})

server.listen(port, host)

index.jsをnodeで実行することでサーバーが立ち上がります。

$ node index.js

接続

次のURLからアクセスできます。https://127.0.0.1:8443/
オレオレ証明書による接続ではセキュリティに引っ掛かるため、画像のように表示されてしまいます。
「詳細設定」をクリックし、「127.0.0.1 にアクセスする(安全ではありません)」を再度クリックしてアクセスをしてください。
画像2
「HelloWorld!」が表示され、SSL通信ができました!
画像3
URLの警告マークから通信に使われた証明書の内容を表示することができます。
コマンド内で入力した「TestCompany」などの情報が確認できます。
画像4

感想

普段はSSL/TLSについてはcertbotやACMにお任せしているため、こういった実装の中身について考えるのは良い勉強になりました。
今回は暗号化の仕組みや実装のさらに深いところまで踏み込むことはせず、流れを追うに留めました。機会があれば踏み込んでみたいですね、

参考URL

  1. Node.js v12でTLSv1.3のテストサーバを手軽に構築
  2. ネットワークエンジニアとして, "Disital Certificate"
  3. IT Sakura, Chromeでルート証明書を確認する
  4. SSL/TLS(SSL3.0~TLS1.2)のハンドシェイクを復習する
  5. Node.js v20.1.0 documentation
脚注
  1. 図ではクライアント証明書を送信しているが、こちらはオプションのため今回はないものとして説明している。 ↩︎

  2. 共通鍵は乱数等を使用して生成する。そのため、通信毎に異なる共通鍵となる。 ↩︎

  3. 証明書発行にはopensslを使用しています。 ↩︎

  4. Chromeに登録されている認証局を調べる方法は参考URLの3を参照 ↩︎

Discussion