本記事は、めんどい太郎の Advent Calendar 2023 7日目の記事です。
はじめに
この記事は初心者が書いています。
この前、WebSocketを利用したWebページを作成したところエラーが出てうまく行かなかったのでその原因と解決策をご紹介いたします。
エラー
Node.jsのwsでWebSocketのサーバーを建て、JavaScriptで接続しようとしたところ以下のようなエラーが発生しました。
Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.
これはつまりHTTPSのサイトでHTTPのWebSocket接続をしたらセキュリティ的によくないから接続しないよというエラーです。
Googleの検索結果をまとめてくれるAIによれば
これは、WebSockets を混合コンテンツ環境で使用しないこと、つまり、HTTPS で読み込まれたページから非セキュアな WebSocket 接続を開かないことを指します。
WebSockets では、制限なく接続をサーバーに送信できるため、攻撃者が DoS 攻撃でサーバーをフラッディングすることができます。
とのことです。よくないですね。
ということで、WebSocketのサーバーをSSL化していきます。
WebSocketのSSL対応
まずはSSL証明書を発行します。
今回は無料で気軽に発行できるLet's Encryptを使用していきます。
Let's Encryptの設定
Let's Encryptのホームページにやり方が乗っています。
今回はCertbotを用いて設定していきます。
こちらに沿ってやれば簡単にできます。
記事にもやり方を書いておきますが、方法が変わる可能性があることを忘れないでください。
環境はUbuntu 22.04.3です。
Snapdのインストール
まずはSnapdをインストールします。
サイトによれば、Ubuntu 18.04以降はプリインストールされているとのことですが私が借りたVPSには入っていなかったためインストールします。
といっても簡単。aptでインストールするだけです。
sudo apt update
sudo apt install snapd
これで終わり。
既存のCertbotを削除
aptなどで入っているCertbotを削除しろとのことです。
入っていなくてもとりあえずコマンド実行しときましょう
sudo apt-get remove certbot
Certbotのインストール
snapdを使用して、Certbotをインストールします。
sudo snap install --classic certbot
コマンドを実行して設定
コマンドを実行して設定をしていきます。
が、その前に準備が必要らしいです。
sudo ln -s /snap/bin/certbot /usr/bin/certbot
これでOK。
そうしたら、設定開始です。
sudo certbot certonly --standalone
質問に答えて、設定をします。
(質問は諸事情により省略します。)
自動更新のテスト
最後に自動更新のテストを行います。
sudo certbot renew --dry-run
これで正しく更新できていればOK。
サーバープログラムの修正
こちらの記事1を参考にサーバープログラムを修正します。
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
const server_port = 8080;
const server_domain = 'example.com';
const server = https.createServer({
key: fs.readFileSync("/etc/letsencrypt/live/"+server_domain+"/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/"+server_domain+"/cert.pem")
});
const wss = new WebSocket.Server({ server });
// 以下省略
server.listen(server_port);
これだけです。
修正ポイントとしてはfs
とhttps
を読み込み
fs
で証明書とキーファイルを読み込み、https
で鯖を建てます。
https
で建てた鯖をws
を使ってWebSocketの鯖として使います。
最後にserver.listen({port})
でポート番号を指定すればOKです。
最初をちょっと書き換えて、最後に一行付け足すだけなので簡単です。
さいごに
WebSocketのSSL化をすっかり忘れていました。
ブラウザがエラー吐いてくれるのでありがたいですね。
SSL化は思っていたよりも簡単で、良かったです。
WebSocket鯖用のドメイン(サブドメイン)を用意して、DNSの設定をするのを忘れずに!