結論(WebSocketをWSSで暗号化)
WebSocketをWSSで暗号化するとは、WebSocketの接続先URLに暗号化なしの ws:// ではなく、TLSで保護された wss:// を使うことです。ws:// は通信経路上で内容を読まれ、書き換えられ、偽サーバーへのなりすましも検知できません。OWASP ASVS 5.0.0 V4.4.1(L1)はすべてのWebSocket接続にWSSを求めており、公開サイトでは wss:// だけを使うべきです。
この記事のポイント
- ws:// は暗号化もサーバー証明書による検証もなく、盗聴・改ざん・なりすましを防げません。
- HTTPSページからの ws:// 接続は主要ブラウザが混在コンテンツとして拒否するため、機能が止まる原因にもなります。
- 相対URLや location.protocol に合わせた切り替えなら、HTTPSページでは自動的に wss:// になります。
- WSSは通信路を守るだけなので、ハンドシェイク時のOrigin許可リスト検証(CSWSH対策)も別に必要です。
- セキュリティAIチェッカーは、トップページのHTMLに平文の ws:// 参照が1件でもあれば×と判定します。
| 診断項目名 | WebSocketをWSSで暗号化 |
|---|---|
| 項目ID | html.insecure-websocket |
| 分類 | HTML・JavaScript・外部資産 |
| 重大度・配点の重み | 重要(high) 重み 4 |
| 関連する標準・ガイドライン | OWASP ASVS 5.0.0 V4.4.1(L1: すべてのWebSocket接続にWSSを使用)、V4.4.2(L2: ハンドシェイク時のOrigin許可リスト検証)/OWASP WSTG-CLNT-10「WebSockets」/CWE-319(重要情報の平文送信) |
WebSocketをWSSで暗号化するとは
WebSocketをWSSで暗号化するとは、WebSocketの接続先に暗号化なしの ws:// ではなく、TLSで保護された wss:// を使うことです。
WebSocketは、HTTPのハンドシェイクで接続を確立したあと、クライアントとサーバーが同じTCP接続上で双方向にメッセージをやり取りする仕組みです。URLスキームには暗号化なしの ws:// と、TLSで保護された wss:// の2種類があります。ws:// は通信経路上で内容をそのまま読まれ、書き換えられる可能性があるため、外部に公開するサイトでは wss:// だけを使うべきです。
// 推奨: TLSで保護された wss:// で接続する
const ws = new WebSocket("wss://chat.example-vendor.jp/ws");
セキュリティAIチェッカーのこの項目は、トップページのHTML(インラインスクリプトを含む)にWebSocket利用の兆候があるかを確認し、ある場合に暗号化されない ws:// の参照が含まれていないかを判定します。自治体公式サイトでWebSocketが使われる例としては、チャットボット、問い合わせ窓口の有人チャット、災害情報のリアルタイム配信、混雑状況表示などがあります。
WSとWSSの違い
| スキーム | 暗号化 | サーバー証明書による なりすまし防止 | HTTPSページからの接続 |
|---|---|---|---|
ws:// | なし | なし | 主要ブラウザが混在コンテンツとして拒否 |
wss:// | TLSで暗号化 | あり | 接続できる |
wss:// はHTTPSと同じくTLSの上でハンドシェイクとメッセージを送るため、盗聴・改ざん・なりすましに対する保護が得られます。ws:// には暗号化も、サーバー証明書によるなりすまし防止もありません。
WebSocketの接続先URLはどう決まるのか(ws・wss・http・https)
WHATWGのWebSockets Standardでは、new WebSocket(url) に渡せるスキームは ws、wss、http、https の4種類です。http は ws に、https は wss に読み替えられます。
相対URLを渡した場合はスクリプトの基準URLから解決されるため、HTTPSページから new WebSocket("/chat") のように書けば wss:// で接続されます。ただし、http・https・相対URLの受け付けは、MDNのブラウザ互換性データではChrome 125、Firefox 124、Safari 17.3からで、それより古いブラウザでは例外(SyntaxError)になります。古いブラウザも対象にする場合は、ページのURLから wss:// のURLを組み立てる書き方(後述)が確実です。
HTTPSページから ws:// に接続するとどうなるか
W3CのMixed Content仕様では、自動アップグレードの対象は画像・音声・動画に限られ、それ以外の混在コンテンツは「ブロック対象(blockable)」として扱われます。主要ブラウザはHTTPSページからの ws:// 接続を混在コンテンツとして拒否するため、「HTTPSサイトなら ws:// を書いても実害はない」と考えるのは誤りです。実際には次のどちらかが起こります。
- HTTPSページでは接続自体が失敗し、機能が動かない(可用性の問題)
- HTTPページやアプリ内ブラウザなど混在コンテンツ制御が効かない環境では、平文で通信される(機密性・完全性の問題)
どちらの場合でも、ws:// の固定記述は修正すべき設定です。画像やCSSなど通常の混在コンテンツは「Mixed Contentがない」で別に確認します。
WSSだけでは足りない点(Origin検証)
WSSは通信路を守るだけで、「誰が接続してよいか」は判定しません。WebSocketのハンドシェイクにはブラウザが自動でCookieを付けるため、サーバーがOriginヘッダーを検証しないと、攻撃者のサイトから利用者のCookieを使って接続される「Cross-Site WebSocket Hijacking(CSWSH)」が成立します。
ASVS 5.0.0 V4.4.2とOWASP WebSocket Security Cheat Sheetは、ハンドシェイクごとにOriginを明示的な許可リストで検証するよう求めています。このOrigin検証は外部からのHTML取得だけでは確認できないため、この項目の判定範囲外です。WSS化とあわせて、サーバー側の実装を確認してください。
WebSocketの暗号化についてよくある誤解
- 「
ws://はローカル開発用だから問題ない」:開発用の接続先が本番HTMLに残ると、本番でも同じURLへ接続しようとします。 - 「WSSにすればメッセージの検証は不要」:メッセージ本文は信頼できない入力として扱い、サイズ上限や構造検証が必要です。
- 「接続先URLに
https://を書くと動かない」:現行の仕様ではhttps://はwss://に読み替えられます(古いブラウザを除く)。
WebSocketをWSSで暗号化の診断基準(セキュリティAIチェッカー)
取得対象
- 入力URLへGETし、リダイレクト(最大4回)を追った最終URLのHTML本文(最大2MB、Content-Typeがtext系・HTML、または未指定)を対象にします。
- 解析対象はHTML本文の文字列そのもの(インライン
script要素、属性値、コメントを含む)です。外部JavaScriptファイルの中身は取得しないため、外部ファイル内のWebSocket利用は判定に含まれません。
判定ロジック
- 利用兆候:
new WebSocket(またはws://・wss://の文字列があるかを、大文字小文字を区別せず検索します。 - 平文参照の件数:
ws://の出現数を数えます。 - プロトコル追従コードの除外:
location.protocolを参照し、文字列リテラルとしての'wss://'と'ws://'の両方を含む場合は、ページのプロトコルに合わせて切り替えるコード(HTTPSページではwss://になる)とみなし、単独リテラル'ws://'の出現は件数から除外します。ws://example.jp/...のようにホスト名まで固定された記述は除外しません。
判定条件
| 判定 | 条件 | 得点への影響 |
|---|---|---|
| ○ 問題なし(pass) | 利用兆候があり、平文 ws:// 参照が0件(wss://、相対URL、プロトコル追従のみ) | 重み4を満点で加算 |
| × 要改善(fail) | 利用兆候があり、平文 ws:// 参照が1件以上 | 重み4のうち0点 |
| 対象外(na) | 取得HTMLにWebSocket利用の兆候がない | 採点の分母から除外 |
- △ 要確認(warn)はありません。平文接続は機密性に直結し、ASVS 5.0.0でもL1(最低限)要件であるため、確認できた場合は×とします。
- ×の場合、根拠欄には「暗号化されないws://参照を○件確認しました。」と件数を表示します。
- 重大度はhigh、重みは4です。この項目の分類は「API・通信設定」です。
自治体サイトでの判定例
| 取得HTMLの記述 | 判定 |
|---|---|
チャットボットの埋め込みコード new WebSocket("wss://chat.example-vendor.jp/ws") | ○ |
災害情報の速報表示に var sock = new WebSocket("ws://192.0.2.10:8080/alert"); が残っている | × |
const proto = location.protocol === "https:" ? "wss://" : "ws://"; new WebSocket(proto + location.host + "/live"); | ○(プロトコル追従) |
外部JSファイル chat.js の中でのみWebSocketを使用し、HTMLには記述がない | 対象外(外部ファイルは解析しないため、別途ソースレビューが必要) |
WebSocketをWSSで暗号化しないとどんなリスクがあるか
- 盗聴:公衆無線LANなどの経路上で、チャットで入力された氏名・住所・相談内容などが平文で読まれます。
- 改ざん・なりすまし:経路上の攻撃者がサーバーからのメッセージを書き換え、偽の避難情報や偽の案内リンクを表示させることができます。証明書による検証がないため、偽サーバーへの誘導も検知できません。
- 機能停止:HTTPSページからの
ws://はブラウザが遮断するため、チャットや速報表示が動かず、問い合わせや情報提供の手段が失われます。 - CSWSH(WSS化しても残るリスク):サーバーがOriginを検証しない場合、利用者がログイン中の状態で攻撃サイトを開くと、攻撃サイトからWebSocketで接続され、メッセージの送受信を乗っ取られます。WSTG-CLNT-10でもOriginヘッダー検証の確認が主要な試験手順です。
災害時の速報やチャット相談のように、住民が行動の根拠にする情報をWebSocketで届けている場合、改ざんの影響は特に大きくなります。
WebSocketをWSSで暗号化する設定方法(クライアント・nginx・Apache・IIS・Next.js)
1. クライアント側の書き換え
// 同一オリジン: ページのURLから組み立てる(HTTPSページでは wss:// になる。古いブラウザでも動く書き方)
const ws = new WebSocket(new URL("/ws/chat", location.href).href.replace(/^http/, "ws"));
// 新しいブラウザだけを対象にできるなら new WebSocket("/ws/chat") でも同じ結果
// 外部サービスなら wss:// を固定
const ws2 = new WebSocket("wss://chat.example-vendor.jp/ws");
ベンダー提供の埋め込みコードに ws:// が含まれる場合は、ベンダーにWSS対応の接続先を確認してください。
2. サーバー側でWSSを終端する
nginx(TLS終端してバックエンドへ転送)
server {
listen 443 ssl;
server_name www.city.example.lg.jp;
# ssl_certificate / ssl_certificate_key は既存のHTTPS設定を流用
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 300s;
}
}
Apache 2.4(mod_proxy_wstunnel または mod_proxy_http)
<VirtualHost *:443>
ServerName www.city.example.lg.jp
SSLEngine on
# 2.4.47以降は mod_proxy_http の upgrade=websocket が利用可能
ProxyPass "/ws/" "http://127.0.0.1:8080/ws/" upgrade=websocket
ProxyPassReverse "/ws/" "http://127.0.0.1:8080/ws/"
</VirtualHost>
IIS
「WebSocket プロトコル」機能を有効化し、HTTPSバインドのサイトでARR(Application Request Routing)とURL Rewriteを使ってバックエンドへ転送します。
Next.js(Vercel)
Vercel FunctionsのWebSocket対応は2026年6月にパブリックベータとして公開された段階です。本番で使えるかは最新の公式ドキュメントで確認し、難しい場合はWSS対応の外部リアルタイム配信サービスを使います。どちらの場合もクライアントは wss:// で接続します。
3. Origin検証(CSWSH対策)
// Node.js (ws ライブラリ) の例
const allowed = new Set(["https://www.city.example.lg.jp"]);
const wss = new WebSocketServer({
server,
verifyClient: (info, done) => done(allowed.has(info.origin), 403),
});
4. CSPで接続先を制限
Content-Security-Policy: connect-src 'self' wss://chat.example-vendor.jp
connect-src に ws: や http: を含めないことで、誤った平文接続をブラウザ側でも止められます。いきなり強制すると既存機能を止める恐れがあるため、Content-Security-Policy-Report-Only で違反の有無を確認してから適用してください。CSP全体の設計は「Content Security Policy」、HTTPの配信元を許可していないかは「CSPでHTTP配信元を許可していない」で確認します。
5. 設定後の確認方法
# ハンドシェイクがHTTPS上で101を返すか確認(ws:// のままでは接続先がTLSを話さない)
curl -i -N --http1.1 \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Origin: https://www.city.example.lg.jp" \
https://www.city.example.lg.jp/ws/chat
# 許可していないOriginで拒否されるか(403等)を確認
curl -i -N --http1.1 -H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Origin: https://evil.example" https://www.city.example.lg.jp/ws/chat
ブラウザの開発者ツール「ネットワーク」タブで種類「WS」を絞り込み、接続先が wss:// で、状態が101になっていることも確認します。修正後はセキュリティAIチェッカーで再診断し、この項目が○になることを確認してください。
サイトを壊さないための注意点
- バックエンドをWSS化する前にクライアントだけ
wss://に変えると接続できなくなります。サーバー側の受け口を先に用意してください。 - プロキシのアイドルタイムアウトが短いと接続が頻繁に切れます。
proxy_read_timeoutや定期的なping/pongで調整します。 connect-srcを強制する前に、解析タグや外部チャットが使う接続先を洗い出してください。
よくある質問(FAQ)
WSとWSSの違いは何ですか?
ws:// は暗号化されないWebSocket、wss:// はTLSの上でハンドシェイクとメッセージを送るWebSocketです。HTTPとHTTPSの関係と同じで、wss:// なら盗聴・改ざん・なりすましに対する保護が得られます。
HTTPSのサイトなら ws:// と書いても問題ありませんか?
問題があります。主要ブラウザはHTTPSページからの ws:// 接続を混在コンテンツとして拒否するため、チャットや速報表示が動かなくなります。また、混在コンテンツ制御が効かない環境では平文で通信されます。
new WebSocket に https:// や相対URLを渡してもよいですか?
現行のWebSockets Standardでは https:// は wss:// に、http:// は ws:// に読み替えられ、相対URLも解決されます。ただしMDNの互換性データではChrome 125、Firefox 124、Safari 17.3からの対応で、古いブラウザでは例外になります。
WSSにすればWebSocketは安全ですか?
WSSは通信路を守るだけで、誰が接続してよいかは判定しません。サーバーがハンドシェイク時にOriginヘッダーを許可リストで検証しないと、Cross-Site WebSocket Hijacking(CSWSH)が成立します。受信メッセージのサイズや構造の検証も必要です。
WebSocketの暗号化の診断で「対象外」になるのはどんな場合ですか?
トップページのHTMLに new WebSocket( や ws://・wss:// の文字列がない場合です。外部JavaScriptファイルの中身は解析しないため、外部ファイルだけでWebSocketを使っている場合も対象外になり、別途ソースレビューが必要です。
location.protocol で ws:// と wss:// を切り替えるコードは×になりますか?
なりません。location.protocol を参照し、'wss://' と 'ws://' の両方の文字列リテラルがある場合は、ページのプロトコルに合わせた切り替えとみなして単独の 'ws://' を件数から除外します。ただしホスト名まで固定した ws://example.jp/… は除外されません。
この項目を含む127項目を、URLを入れるだけで無料診断します。
参考資料
- OWASP ASVS 5.0.0 V4 API and Web Service(V4.4.1・V4.4.2)
- OWASP WSTG-CLNT-10 Testing WebSockets
- Vercel Changelog「WebSocket support is now in public beta」
- MDN browser-compat-data(api/WebSocket.json)
- OWASP WebSocket Security Cheat Sheet
- OWASP HTML5 Security Cheat Sheet
- WHATWG WebSockets Standard
- MDN WebSocket() constructor
- W3C Mixed Content Level 2