aegis-tls-proxy について
aegis-tls-proxy は、Linux ホストまたは設定リポジトリの TLS 終端リバースプロキシ層を読み取り専用で監査するスキルです。リスナー、仮想ホスト、TLS、ヘッダー、リクエスト制限、アクセス制御、上流の信頼性、証明書更新を確認します。
主な機能
- 01リスナーと仮想ホストの棚卸し
- 02TLS 設定と証明書の確認
- 03セキュリティヘッダーと継承の監査
- 04リクエスト制限とタイムアウトの確認
- 05管理パスと上流の信頼性の確認
- 06ACME 更新設定の監査
活用シーン
- 01Linux リバースプロキシ設定の監査
- 02TLS や証明書チェーンの問題調査
- 03管理パスの外部公開状況の確認
- 04プロキシからアプリへの通信とヘッダーの監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ as-foss/ aegis-skills --skill aegis-tls-proxyインストール後、Claude Code で/aegis-tls-proxyを入力して読み込まれたか確認します。
同梱ファイルreferences/background.mdreferences/further-reading.md
詳細
スコープと目的
Linux ホストまたはその設定をコードとして管理するリポジトリの、TLS を終端するリバースプロキシ層を読み取り専用で監査します。以下を確認します。
- ポート 80/443 をどのプロセスが所有しているか、またバージョン情報が漏えいしているか。
- 仮想ホストの全体像と、認識されない Host ヘッダーに対して何が応答するか。
- TLS プロトコルの最低バージョン、暗号スイート/曲線の選択、セッション処理、OCSP ステープリング、証明書チェーンの完全性、SAN の適用範囲、証明書の有効期限切れに対する自動化。
- エッジでのレスポンスヘッダー(HSTS と関連ヘッダー)および nginx の
add_header継承時の落とし穴。 - エッジのリクエストサイズとタイムアウトの上限と、アプリケーションが実際に受け入れる値との比較。
- CIDR の許可リスト、ベーシック認証、mTLS、または独立した管理用仮想ホストによる管理者パス(
/admin、/actuator、/metrics)の保護。 - プロキシからアプリケーションへの通信経路:トランスポートのセキュリティと、転送ヘッダー(
X-Forwarded-For、Host)の信頼設定が適切か。 - ACME/certbot の運用上の衛生管理:更新タイマー、デプロイフック、レート制限とチャレンジパスに関する落とし穴。
二次的な対象範囲:Caddy の自動 HTTPS モデル、HAProxy の bind 行における TLS のデフォルト、Apache における SSLProtocol/SSLCipherSuite の同等設定、WAF の選択肢としての mod_security。
対象外(関連する別モジュール):大規模なサービス拒否への防御とレート制限の詳細調整(DOS モジュール)、アプリケーションの認可ロジック(AUTHZ モジュール)、Host ヘッダー汚染ペイロードなどの HTTP プロトコル悪用(code-audit http-protocol モジュール)。What To Check/Where To Look/Exploitation 内のすべてのコマンドは、変更を加えない調査用です。変更を伴う手順は Remediation and Verification のみに記載します。
前提条件と用語
予備知識がない方向けの入門です。このモジュールで使用する用語をそれぞれ一行で説明します。各項目のより詳しい平易な説明は、リポジトリの GUIDE.md 用語集にあります。
- reverse proxy:訪問者からの接続を受け付け、アプリケーションに転送する前段のサービス
- TLS termination:暗号化されたトラフィックが復号される地点(このプロキシ)
- protocol/cipher floor:引き続き受け入れられる最も古い暗号化バージョンと強度。レガシー設定ではダウングレードの圧力を受ける可能性があります
- HSTS:今後、そのドメインでは HTTPS のみを使用するようブラウザーに指示するレスポンスヘッダー
- default_server / catch-all:未知のホスト名に対するリクエストに応答するサイト。最初に一致した設定による予期せぬ動作が生じることがあります
- add_header inheritance trap:location 内でヘッダーを一つ宣言すると、その location ではサーバーレベルのヘッダーがすべて継承されなくなる落とし穴
- ACME / certbot:チャレンジパスへの到達性を維持する必要がある、証明書の自動発行と更新の仕組み
- taint flow / source→sink:信頼できないデータが入口から危険な関数へと流れる経路
- finding status:Confirmed > Probable > Needs-Review。証拠に関する規則は templates/finding-report.md にあります
メンタルモデル
プロキシでは、2つの信頼境界が交わります。
- クライアントからエッジへ:TLS 終端、HSTS、ヘッダーポリシー、サイズ/時間制限、パスのゲート制御。この境界では、攻撃者がすべてを制御できます。
- エッジからアプリケーションへ:通常はプレーンテキストの HTTP です。マシンから外に出ない場合(
127.0.0.1、::1、Unix ソケット)に限り許容されます。ネットワークセグメントを1つでも通過する場合は、平文での送信(CWE-319)です。
ここでの検出事項の大半につながる構造上のルールは次のとおりです。
- 最初に一致したものが2回採用されます。どの
serverブロックもdefault_serverを宣言していない場合、nginx は最初に一致するserverブロックを選択します。そのため、名前のないキャッチオールによって、不明な Host ヘッダーが、最初に読み込まれたどれかの仮想ホストへ暗黙的にルーティングされます。ngx_http_access_module はallow/denyを上から順に評価し、最初に一致した時点で停止します。そのため、deny all;を最初に置くと、それより下のすべての allow は機能しないコードになります。 - 継承は一度リセットされます。nginx の
add_headerディレクティブは、現在のレベルで何も宣言されていない場合に限り、上位レベルから継承されます。location 内にadd_headerが1つあるだけで、その location のレスポンスではサーバーレベルのヘッダーがすべて消去されます。ディレクティブの配列にはリセットのセマンティクスがあるものと考えてください。 - 提供される証明書チェーンと、保存されている証明書チェーンは同じとは限りません。クライアントに見えるのは、証明書ディレクトリにあるものではなく、ハンドシェイク中にプロキシが送信したものです。チェーンの不足は、ブラウザー以外のクライアントでは「unknown authority」エラーとして表面化する一方、ブラウザーでは問題ないように見えます。
- 更新は設定から切り離されています。certbot は独自のタイマーで証明書を更新します。強化によって ACME チャレンジのパスがブロックされている場合や、reload フックが設定されていない場合、今日すぐに障害が起きることはなく、数週間後に証明書が期限切れになります。
以下を確認する際は、このモデルを念頭に置いてください。以下の検出事項のほとんどは、この4つのルールのいずれかに違反したものです。
確認項目
1. プロキシのインベントリと所有者
sudo ss -tlnp | grep -E '(:80|:443)\s' を実行し、[::]:443 の IPv6 ソケットも含め、各リスナーの users:(("name",pid=X,fd=Y)) の所有者を記録します。すべてのリスナーを設定ファイルに対応付けます。インターフェースを指定してバインドすべきなのに 0.0.0.0 にバインドされているリスナーには、フラグを立てます。
2. バージョンの開示(軽微)
curl -sI https://HOST/ | grep -iE '^(server|x-powered-by|x-aspnet)' を実行します。Server: nginx/1.24.0 はバージョンを漏えいさせるため、軽微な検出事項です。nginx での対処は server_tokens off; ですが、これでも Server: nginx は残ります(製品名は見えたままで、バージョンは非表示になります)。完全に抑制するにはサードパーティ製の headers-more モジュールが必要です。検出事項で、それ以外の方法で完全に抑制できると主張しないでください。X-Powered-By のような中継されたバナーは、proxy_hide_header が設定されていないことを示します。
3. 仮想ホストマップの抽出
対象マシンの root 権限で sudo nginx -T 2>&1 | less を実行します。すべての include を含む、有効な設定がダンプされます。注意点:root 権限が必要です(キーのパスとインクルードされたファイルを読み取るため)。ディスク上の状態が反映されるため、reload 待ちの実行時状態とは異なります(設定ファイルの更新時刻と systemctl status nginx のタイムスタンプを比較してください)。コンテナー化されたデプロイでは、バイナリや設定ファイルの場所が異なる場合があります。設定をコードとして管理している場合の代替手段として、grep -RInE '^\s*(server_name|listen|include)' /repo/path を使い、手作業でマップを再構築します。Caddy では /etc/caddy/Caddyfile を読み、HAProxy では bind/acl host/use_backend を grep し、Apache では grep -RIn '<VirtualHost\|ServerName' /etc/apache2/sites-enabled/ を実行します。
4. デフォルト仮想ホストのキャッチオール動作
暗黙のデフォルトを特定します。sudo nginx -T | grep -n 'default_server' を実行し、default_server の指定がない 443 の server ブロックのうち、どれが最初に現れるかを確認します。外部から読み取り専用でプローブします:curl -sk -o /dev/null -w '%{http_code}\n' --resolve probe.invalid:443:TARGET_IP https://probe.invalid/。200/301/302 が返る場合、不明な Host ヘッダーがアプリケーションに到達しています。これは Host ヘッダー攻撃の対象領域です(code-audit の http-protocol モジュールを相互参照してください)。接続がクローズされる動作(return 444;)か、明示的なキャッチオール用スタブが想定されます。
5. TLS プロトコルの最低バージョン、暗号スイート、曲線、セッション設定
有効な値を確認します:sudo nginx -T | grep -nE 'ssl_protocols|ssl_ciphers|ssl_ecdh_curve|ssl_session_tickets|ssl_prefer_server_ciphers'。ssl_protocols TLSv1.2 TLSv1.3; だけが指定されていることを必須とします。TLSv1、TLSv1.1、SSLv3 があれば検出事項です(認証情報を扱うエンドポイントを提供している場合は High)。ssl_protocols がない場合、ライブラリに依存するデフォルト値が適用されます。古い OpenSSL ビルドでは TLSv1.0 が有効になるため、実環境でプローブするまでは未検証として扱います(Exploitation R6)。暗号スイートの文字列を独自に作成してはいけません。デプロイ済みの nginx/OpenSSL の組み合わせに対する Mozilla SSL Configuration Generator の intermediate プロファイルの出力を必須とします。既知の正常なベースライン文字列は Remediation F1 に記載されています。曲線は ssl_ecdh_curve X25519:prime256v1:secp384r1; が想定値です。セッションについては、ssl_session_tickets off; にすると、再開セッションの前方秘匿性と引き換えに再開時のパフォーマンスが低下します。どちらを選んだ場合も、その選択を記録してください。
6. 証明書チェーン、SAN の適用範囲、ステープリング、期限切れの自動化
Exploitation R7/R8 にまとめられたコマンドで確認します。提供されるチェーンの長さが 2 以上(リーフ証明書+中間証明書)であること、SAN が提供されるすべての server_name をカバーしていること、-status でステープリングの状態を確認できること、有効期限まで30日を超えているか、更新用のタイマーが有効であることを確認します。自動化も確認します:systemctl list-timers --all | grep -iE 'certbot|acme|dehydrated'。インストールされている場合は、sudo certbot certificates も実行します。補足:Let's Encrypt を含む複数の大手 CA は、2025年以降、OCSP レスポンダーを廃止し、CRL に移行しています。CA が OCSP を一切提供していない場合、ステープリングがないことは Low/情報提供レベルです。
7. HSTS とエッジヘッダー
curl -sI https://HOST/ | grep -i strict-transport-security を実行します。最低要件は max-age=15552000(180日)です。includeSubDomains を追加するのは、すべてのサブドメインが有効な HTTPS を提供していることを確認してからにしてください。実質的な約束となる設定です。Preload の説明は Remediation/V4 にあります。出現回数を数えます。HSTS ヘッダーが複数ある場合は、エッジとアプリで二重に設定されて競合しています。レイヤーを1つだけ選び(統一性の点からエッジを推奨)、もう一方を無効にします。エッジでは、併せて X-Content-Type-Options nosniff、X-Frame-Options DENY(または CSP の frame-ancestors)、Referrer-Policy を設定します。add_header の継承に関する落とし穴(Mental Model)を監査します。add_header を宣言するすべての location で、サーバーレベルのヘッダーをすべて再指定する必要があります。影響を受けるパスごとに curl -sI でテストしてください。
8. リクエスト制限とタイムアウト
設定値を実際のトラフィック要件と比較します:
client_max_body_size(nginx のデフォルトは 1m)は、アプリケーションで正当に許容される最大ペイロードの種類に合わせる必要があります。小規模な JSON API では 1~10m とし、アップロード用エンドポイントには、その location に限って明示的に大きな値を設定します。サーバー全体には決して設定しないでください。- 上限のないアップロードを受け入れるアプリケーションのエンドポイントの前段に、制限のないプロキシがある場合は、Medium の DoS 検出事項です。
- スローロリス型のリソース占有を抑えるため、タイムアウトを
client_body_timeout 10s; client_header_timeout 10s; keepalive_timeout 15s; send_timeout 10s;に近づけて短縮します(デフォルトは 60s/75s)。ここでは概要のみを示します。詳細は DOS モジュールを参照してください。 - ヘッダーバッファ:
client_header_buffer_size(デフォルトは 1k)およびlarge_client_header_buffers(デフォルトは4 8k)により、異常に大きなヘッダーを 400/414 で拒否します。2 8kに下げると、接続ごとの最悪時メモリ使用量を削減できます。 - proxy_read_timeout に関する注意:実際の p99 エンドポイントレイテンシ(レポート、エクスポート、ストリーム)を下回る値を設定してはいけません。まず最も遅い実エンドポイントを測定し、そのうえで余裕を加えてください。そうしないと、DoS リスクと引き換えに機能が壊れます(レスポンスの切り詰め、不要な 504)。
9. 管理パスの遮蔽
設定ファイルやアプリのドキュメントから、機密性の高いパスを列挙します:/admin、/actuator、/metrics、/debug、/phpmyadmin、/wp-login。外部から次を実行します:for p in admin actuator metrics debug; do printf '%s ' "$p"; curl -s -o /dev/null -w '%{http_code}\n' "https://HOST/$p/"; done。インターネットから 200/302 が返る場合は、アクセス制御がないという検出結果になります。パスごとの必須の最終状態は、403(CIDR による拒否)、401(ベーシック認証)、または mTLS による拒否です。許可/拒否の設定がある場合は、順序を確認します:allow の行は deny all; より前になければなりません。optional_no_ca は、$ssl_client_verify による強制と組み合わせられていない限り、見せかけの設定として扱います(修正 F3)。
10. プロキシからアプリへのホップ
設定の実態を次で確認します:sudo nginx -T | grep -nE 'proxy_pass|grpc_pass|uwsgi_pass'。ループバックの宛先(127.0.0.1、::1、unix:)であれば、平文通信は妥当です。RFC1918 またはルーティング可能な宛先への平文通信は CWE-319 の検出結果です。アップストリーム TLS、mTLS、または WireGuard メッシュで修正します。転送ヘッダーが存在することを確認します:proxy_set_header Host ...、proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;(または意図的な書き換え — Taint Tracing を参照)、proxy_set_header X-Forwarded-Proto $scheme;。Host の意味を理解しておきます:Host $host はクライアントが指定した値を内部に転送します(エッジで不明なホストを拒否している場合に限り安全です。チェック 4 を参照)。$proxy_host は代わりにアップストリームの識別情報を送信するため、アプリを隔離できますが、仮想ホストを考慮したルーティングが機能しなくなる場合があります。proxy_hide_header X-Powered-By; / proxy_hide_header Server; を使ってバックエンドのバナーを隠します。
11. セカンダリプロキシ
- Caddy:グローバルな
emailオプションが設定されていることを確認します(有効期限の通知用)。tls internalの使用箇所をすべて特定します(プライベート CA — ルート証明書の保管場所と、信頼する対象を確認します)。ヘッダーのカスタマイズにはheader { ... }ブロック形式を使用します。 - HAProxy:
bind ... ssl crt ...の各行を調べます。グローバル設定にssl-default-bind-options ssl-min-ver TLSv1.2が必要です(HAProxy ≥ 2.2)。crt PEM には、リーフ証明書と中間証明書を連結して含める必要があります。 - Apache:
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1、Mozilla のジェネレーターにある Apache プロファイルからそのままコピーした SSLCipherSuite、およびHeader always set Strict-Transport-Security "..."が必要です。利用可能な WAF レイヤーとして mod_security(SecRuleEngine On)を確認します。
12. ACME/certbot の衛生管理
systemctl list-timers --all | grep -i certbot でアクティブなタイマーが表示されることを確認します。有効期限の健全性は sudo certbot certificates で確認します。デプロイフックが存在し、実行可能であることを確認します:ls -la /etc/letsencrypt/renewal-hooks/deploy/ に nginx のリロードスクリプトが含まれている必要があります(修正 F2)。certbot renew --dry-run で機能を確認します(LE のステージング環境にのみアクセスします)。ハードニングルールを調べ、/.well-known/acme-challenge/ を妨げる設定がないか確認します。認証ゲート、拒否ルール、またはチャレンジパスを覆い隠すロケーション単位のリダイレクトがあると、有効期限が近づいたときに更新が気づかれないまま失敗します。レート制限にも注意します。本番環境の LE クォータは週単位です(重複証明書数とドメイン数の上限)。パイプラインでは --staging を使用する必要があります。
確認箇所
証拠の収集:tools/sweeps/sweep-tls-proxy.sh は [TLS-nn] セクションをそのまま記録します。それらはこのモジュールの評価基準に照らして判定し、生の出力だけで判定しないでください。
ファイルシステム(ホスト):
/etc/nginx/nginx.conf— メインファイルです。Debian では/etc/nginx/conf.d/*.confと、/etc/nginx/sites-enabled/*からシンボリックリンクされた/etc/nginx/sites-available/*に分割されます。upstream/RHEL の構成では、単一のファイルまたは conf.d のみが使われます。- 実効設定の確認:
sudo nginx -T(ディスク上の状態。リロード待ちの場合がある点に注意してください)。 - 証明書:
/etc/letsencrypt/live/<name>/fullchain.pemとprivkey.pem。アーカイブされたコピーは../archive/にあります。更新パラメーターは/etc/letsencrypt/renewal/<name>.conf、フックは/etc/letsencrypt/renewal-hooks/{deploy,renewal}/にあります。 - Caddy:
/etc/caddy/Caddyfile。管理 API のデフォルトは localhost:2019 です。リモートに公開されていないことを確認してください。 - HAProxy:
/etc/haproxy/haproxy.cfg。証明書バンドルは、各crt引数で指定されたパスにあります。 - Apache:Debian では
/etc/apache2/{sites-enabled,mods-enabled,ports.conf}、RHEL では/etc/httpd/{conf,conf.d}/です。 - タイマー/サービス:該当するものに応じて、
systemctl list-timers --all、systemctl status nginx caddy haproxy apache2 httpdを実行します。 - パッケージのバージョン:
dpkg -l | grep -E 'nginx|caddy|haproxy|apache2'またはrpm -qa | grep -E 'nginx|caddy|haproxy|httpd'を実行します。 - 実行中のソケット/プロセス:
ss -tlnp、ps aux | grep -E '[n]ginx|[c]addy|[h]aproxy|[h]ttpd|[a]pache'を実行します。 - アクセスログ(読み取り専用で確認):
/var/log/nginx/access.logに異なる Host 値を含むエントリーがあれば、キャッチオールルーティングが行われていることを確認できます。
リモートプローブ(認証情報は不要):Exploitation & Reproduction に記載された openssl s_client および curl の呼び出しを使用します。
リポジトリの一括検索(設定をコードとして管理):
grep -RInE 'ssl_protocols|ssl_ciphers|client_max_body_size|Strict-Transport-Security|set_real_ip_from|optional_no_ca|proxy_pass|reverse_proxy|bind .*ssl' .- 仮想ホストのマップを再構築:
grep -RInE 'server_name|ServerName|<VirtualHost|listen ' .
パターンとシグネチャ
シグネチャの確認(nginx ディレクティブ):
ssl_protocolsにTLSv1、TLSv1.1、またはSSLv3が指定されている → レガシープロトコルが公開されています。ssl_protocolsがどこにもない → ライブラリのデフォルト下限は未検証です。スコアリングの前に実環境をプローブしてください。ssl_ciphersに RC4、3DES、CBC のみのスイート、または曖昧なエイリアス(HIGH、MEDIUM)が含まれている → Mozilla のジェネレーターで再生成してください。allow 0.0.0.0/0;がdeny all;より前にある、または置き換えている → 効果のないゲートです。deny all;が allow 行より前にある → allowlist がデッドコードになっています(可用性のバグ:全員がブロックされます)。$ssl_client_verifyによる強制がない状態でのssl_verify_client optional_no_ca;→ アクセス制御ではなく、本人確認を装っているだけです。set_real_ip_from 0.0.0.0/0;、または信頼する範囲がパブリックインターネット全体を含んでいる → クライアント IP の偽装が可能です。- チェック 4 を満たさないエッジでの
proxy_set_header Host $host;→ クライアントが制御する Host の汚染が内部に渡されます。仮想ホスト対応アプリでのHost $proxy_host→ 気付かないうちにルーティングが壊れます。 - HTTPS エッジの背後で
proxy_set_header X-Forwarded-Proto $scheme;がない → アプリが http:// URL を生成するか、リダイレクトループが発生します(リグレッションのシグネチャ)。 - 隣接する location に
add_headerがない location 内にadd_headerがある → そのパスではサーバーレベルのヘッダーが消失します(curl -sIでパスごとに確認してください)。 - 1 つのレスポンスに
Strict-Transport-Securityヘッダーが 2 つある → 二重設定による競合です。
準拠しているシグネチャ:
ssl_protocols TLSv1.2 TLSv1.3;+ ジェネレーターの intermediate 暗号スイート文字列 +ssl_session_tickets off;。- キャッチオール:
server { listen 443 ssl default_server; ssl_reject_handshake on; }(nginx ≥ 1.19.4)またはreturn 444;のスタブ。 - ゲート付き location 内の順序:まず
allow <cidr>;の行、次にdeny all;、最後にauth_basic。 http://127.0.0.1:<port>またはunix:ソケットを指すアップストリーム。alwaysフラグを使用して出力する、単一レイヤーの HSTS。
レスポンス形式のシグネチャ:
Server: nginxとServer: nginx/x.y.zの違い(情報開示)。- 大きなヘッダーのプローブで 400/414 が返る → バッファー制限が有効です。
- 上限を超える POST ボディに対して 413 が返る →
client_max_body_sizeが有効です。巨大なアップロードに 413 が返らず、アプリが受け付ける場合は、検出結果の組み合わせです。
ログのシグネチャ:$host フィールドが設定済みの server_name のいずれにも一致しない access.log の行が繰り返し記録されている → 本番環境でキャッチオールが確認されます。
汚染追跡のガイダンス
ソース(境界でクライアントが制御可能):Host ヘッダー、リクエスト行/絶対 URI 形式、X-Forwarded-For、X-Real-IP、Forwarded、Cookie、エンコーディングを含む元のパス。
XFF のルール:信頼された各プロキシは、ピアとして認識した値を末尾に追加します。正しく値を導出するには、リストを右から左へたどり、自分が運用するインフラプロキシの数だけ正確にスキップし、帰属先を特定できない最初の値で停止します。それより左の値はすべて攻撃者が記述したテキストです。設定ミスの重大度:
- アプリがチェーン全体または左端のエントリーを使用する → 簡単に偽装可能(AUTHZ のバイパス、レート制限の回避、ログポイズニング)。AUTHZ モジュールを相互参照してください。
- エッジで範囲が広すぎる
set_real_ip_fromを使ってreal_ipを実行する → エッジ自体が偽造入力から$remote_addrを書き換え、limit_conn/limit_reqのキーと監査ログを汚染します。 - 正しい構成:
set_real_ip_fromには自社の CDN/LB の範囲のみを列挙し、real_ip_headerにはそれらが制御するヘッダー(Cloudflare の背後ならCF-Connecting-IPなど)を指定し、正確な信頼対象の数に基づいてreal_ip_recursive on;を設定します。
Host の汚染:proxy_set_header Host $host; を使用すると、クライアントが送信した値がアプリのルーティング、URL 生成、キャッシュキー、パスワードリセットリンクに渡されます。まずエッジで検証します(明示的な server_name のリストと、要求を拒否する default_server を設定)。その後、転送される値は自社が所有する名前に限定されます。エッジで不明な Host を拒否できない場合は、$proxy_host を転送し、アプリ側で設定済みのベース URL から外部公開 URL を導出します。
エッジでの判定後に確認するシンクのチェックリスト:セッション Cookie のスコープ属性、リダイレクト先 Location の生成処理、Webhook/コールバックのバリデーター、キャッシュキー、XFF 由来のアドレスを使う管理者 IP 許可リスト。
追跡手順:受信リクエストの形を 1 つ選び(例:偽造 XFF と無関係な Host を含む POST /reset)、設定を上から順にたどります。どのサーバーに一致したか、どのロケーションに一致したか、どのヘッダーが転送されたか、アプリが何を導出しうるかを確認し、汚染が未検証のまま境界を越える箇所を記録します。
悪用と再現
すべての実証は、読み取り専用のネットワーク観測です。HOST を監査対象の名前に置き換えてください。特に指定がない限り、許可リストに登録された CIDR の外部から実行します。
R1. リスナーの所有者
sudo ss -tlnp | grep -E '(:80|:443)\s'
読み取り方:users:(("nginx",pid=X,fd=Y)) は終端処理を行うプロセスを示します。異なるアドレスで複数の 443 リスナーがある場合は、マッピングする価値のあるフロントエンドの分割が明らかになります。
R2. バージョンの開示
curl -sI https://HOST/ | grep -iE '^(server|x-powered-by|x-aspnet)'
読み取り方:Server: nginx/1.24.0 → 情報開示(低)。Server: nginx のみ → トークンは無効です。バックエンドからそのまま渡される X-Powered-By → バックエンドのバナー漏えい。
R3. 設定の実態
ルート:sudo nginx -T 2>&1 | grep -nE 'ssl_protocols|ssl_ciphers|ssl_ecdh_curve|ssl_session_tickets|server_tokens|client_max_body_size|client_body_timeout|keepalive_timeout|large_client_header_buffers|allow |deny |ssl_verify_client|set_real_ip_from|proxy_pass|proxy_set_header|add_header'
注意点:root が必要です。ディスク上の状態が表示されます。リロード待ちの場合、実行時の状態とは異なります。
リポジトリでの代替手段:grep -RInE '^\s*(listen|server_name|ssl_|add_header|client_|allow|deny|proxy_|set_real_ip_from)' .
読み取り方:出力から仮想ホストとディレクティブの対応表を作成します。空欄はすべて「未検証」を意味し、「安全」ではありません。
R4. キャッチオールの動作
curl -sk -o /dev/null -w '%{http_code}\n' --resolve probe.invalid:443:TARGET_IP https://probe.invalid/
読み取り方:200/301/302 → 不明な Host がアプリに到達しています(code-audit の http-protocol モジュールを相互参照)。curl の終了コードが 52 で 000 → 接続が閉じられています(return 444; の形)。curl -sk https://probe.invalid/ でアプリのレスポンス本文が観測された場合は、ルーティング範囲が広すぎる証拠です。
R5. ネゴシエートされた TLS のベースライン
echo | openssl s_client -connect HOST:443 -servername HOST -status 2>/dev/null | grep -E '^(New|Cipher|Server Temp Key|OCSP)'
各フィールドの読み方:
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384— ネゴシエートされたプロトコルと暗号方式です。TLSv1.2 以上であれば許容範囲ですが、それより古いものは重大度を引き上げます。Server Temp Key: X25519, 253 bits— 使用中の楕円曲線です。OCSP response: no response sent— OCSP ステープリングなしです。OCSP Response Status: successful (0x0)— ステープリングされています。 全出力には、チェーンの深さと、ローカルの信頼ストアに対するVerify return code: 0 (ok)も表示されます。
R6. レガシープロトコルへの強制接続(証明の試行)
curl 形式:curl -svo /dev/null --tlsv1.1 --tls-max 1.1 https://HOST/ ; echo exit=$?
強化済みのホスト:終了コードは 35 で、メッセージに alert protocol version または unsupported protocol が含まれます。脆弱なホスト:終了コードは 0 で、SSL connection using TLSv1.1 / ... の行が表示されます。
クライアントに関する注意点:最新の OpenSSL クライアントビルドでは TLS ≤1.1 の提示が拒否される場合があります(no protocols available)。この結果だけではサーバーについて判断できないため、次のコマンドを使用してください。
echo | openssl s_client -connect HOST:443 -servername HOST -tls1_1 2>&1 | grep -E 'alert|Cipher is|Verify return'
強化済み:sslv3 alert handshake failure または tlsv1 alert protocol version。脆弱:ネゴシエートされた TLSv1.1 の暗号方式と証明書チェーンが表示されます。TLSv1.0 については -tls1 を指定して繰り返します。
R7. チェーンの完全性
echo | openssl s_client -connect HOST:443 -servername HOST -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
ローカルでの照合:grep -c 'BEGIN CERTIFICATE' /etc/letsencrypt/live/<name>/fullchain.pem
読み方:完全なチェーンでは、2 枚(リーフ+中間証明書)、または場合によっては 3 枚(クロス署名されたルート証明書)が提供されます。保存されたファイルに 2 枚以上あるのに、提供される証明書が 1 枚だけの場合は、転送されるチェーンが不完全です。症状:ブラウザーでは成功する(中間証明書がキャッシュ済み、または AIA で取得される)一方、curl、wget、Java、Go、モバイルクライアントでは unable to get local issuer certificate / certificate signed by unknown authority が発生します。これは典型的な「こちらでは動くのに」というインシデントです。
R8. SAN の網羅性と有効期限までの日数
SAN:echo | openssl s_client -connect HOST:443 -servername HOST 2>/dev/null | openssl x509 -noout -ext subjectAltName
有効期限:end=$(echo | openssl s_client -connect HOST:443 -servername HOST 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2); echo "days=$(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 ))"
読み方:提供されるすべての server_name が SAN に含まれている必要があります。残り日数が 30 日未満の場合は、自動化に問題がある可能性があります(R12 を確認してください)。
R9. HSTS とヘッダーのセット
curl -sI https://HOST/ | grep -ic strict-transport-security に続けて curl -sI https://HOST/
読み方:件数が 1 より大きい場合は、二重設定による競合です。max-age 15552000 未満の値は不十分です。公開 HTTPS アプリで完全に欠落している場合は、Medium の判断根拠になります。関連するヘッダー(nosniff、フレームポリシー、referrer-policy)がそれぞれ 1 回ずつ表示されることも確認してください。
R10. 管理パスへの到達可能性(外部の観測点)
for p in admin actuator metrics debug; do printf '%s ' "$p"; curl -s -o /dev/null -w '%{http_code}\n' "https://HOST/$p/"; done
読み方:200/302 → インターネットから認証なしで到達可能です(検出事項。代表的なレスポンス本文は curl -s で別途取得してください。例:/actuator/health)。401 → 認証ゲートがあります。403 → 拒否/CIDR ゲートがあります。
R11. ホップ間のトランスポート(設定から判断)
出典:R3 の proxy_pass 行。
読み方:http://127.0.0.1:* / Unix ソケットは許容範囲です。http://<RFC1918-or-public-ip>:* のようにネットワークセグメントをまたいで平文通信する場合は、CWE-319 の判断根拠になります(Severity Assessment の AV:A ベクトルでスコアを付けてください)。
R12. 更新の仕組み
systemctl list-timers --all | grep -iE 'certbot|acme' ; ls -la /etc/letsencrypt/renewal-hooks/deploy/ 2>/dev/null ; sudo certbot certificates 2>/dev/null | grep -E 'Certificate Name|Expiry'
読み方:証明書は存在するのにタイマーがない場合は、手動更新の運用です。deploy ディレクトリが空で nginx を使用している場合、更新された証明書は手動でリロードするまで適用されません(気づかれないまま将来の停止につながります)。有効期限の列が 30 日未満であることは、R8 の結果を裏付けます。
修正
指摘ごとに適用し、nginx -t && systemctl reload nginx で検証してください。設定や証明書の変更には、再起動ではなくリロードを使用してください。再起動すると既存の接続が切断されるため、バイナリのアップグレード時に限って使用します。スニペットには # FIXED と記載しています。
F1. 強化された nginx エッジの完全な設定(# FIXED)
# FIXED - /etc/nginx/sites-available/example.com
# Unknown-host catch-alls: keep ACME alive, close everything else
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 444; }
}
server {
listen 443 ssl default_server; # nginx >= 1.19.4
ssl_reject_handshake on; # refuses unknown-SNI handshakes; needs no cert
}
# Named vhost: HTTP -> HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # nginx >= 1.25.1; older builds: append "http2" to listen
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Mozilla SSL Configuration Generator, INTERMEDIATE profile baseline.
# Regenerate for your exact nginx/OpenSSL pair instead of editing blind:
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;
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# add_header GOTCHA: these apply only because THIS level declares them.
# Any location below adding its own add_header must REPEAT every line here.
add_header Strict-Transport-Security "max-age=300" always; # stage upward, see V4
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
client_max_body_size 10m; # right-size to the app; tighter caps per location
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 15s;
send_timeout 10s;
large_client_header_buffers 2 8k;
server_tokens off;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host; # safe BECAUSE catch-alls reject aliens (F1 block above)
proxy_set_header X-Forwarded-For $remote_addr; # edge is sole proxy: rewrite, don't extend
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 30s; # raise only to measured p99 + headroom
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
}
Debian での有効化:ln -s ../sites-available/example.com /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx。
F2. certbot の更新デプロイフック(# FIXED)
# FIXED - /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh
#!/bin/sh
set -eu
/usr/sbin/nginx -t
/bin/systemctl reload nginx
logger -t certbot-hook "reloaded nginx for ${RENEWED_LINEAGE:-unknown}"
実行可能ファイルとしてインストール:install -m 0755 <src> /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh。
リロードと再起動の違い:リロードは、接続を一切切断せずにワーカーをグレースフルに入れ替えるため、証明書や鍵の更新(通常の更新ケース)には十分です。再起動が妥当なのは、リッスンソケット、バイナリ、またはモジュールセットが変更された場合に限られます。certbot renew --dry-run で一連の処理を検証してください(LE ステージング環境を使用するため、本番環境のクォータは消費されません)。
F3. 管理パスを保護するパターン(# FIXED)
CIDR の許可リストと基本認証のレイヤー(順序が重要です):
# FIXED
location ^~ /admin/ {
allow 203.0.113.0/24; # office/VPN egress
allow 198.51.100.0/24; # bastion subnet
deny all; # MUST follow the allows: first match wins
auth_basic "Administrators";
auth_basic_user_file /etc/nginx/admin_htpasswd;
proxy_pass http://127.0.0.1:8000;
}
location = /metrics {
allow 127.0.0.1;
deny all;
proxy_pass http://127.0.0.1:8000;
}
htpasswd の作成:sudo htpasswd -cB /etc/nginx/admin_htpasswd alice(apache2-utils を使用。-B の bcrypt は libxcrypt/glibc-crypt システム上の nginx で使用できます。APR1 を使用して移植性を高める場合は -B を省略します:sudo htpasswd -c /etc/nginx/admin_htpasswd alice)。追加ユーザーの作成時は -c を付けないでください(ファイルが切り詰められます)。
専用の管理サブドメインでの mTLS ゲート:
# FIXED - admin.example.com server block
server {
listen 443 ssl;
server_name admin.example.com;
ssl_certificate /etc/letsencrypt/live/admin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;
ssl_client_certificate /etc/nginx/client-ca.crt; # YOUR issuing CA only
ssl_verify_client on;
ssl_verify_depth 2;
# ... TLS/header/limit directives as in F1 ...
}
注意:ssl_verify_client optional_no_ca; はクライアント証明書を要求し、解析可能で自己検証に成功することを確認しますが、任意の CA からのチェーンを受け入れます。ログ記録やテレメトリーにのみ適しています。ゲートとして使用しないでください。テンプレートから引き継いでいる場合は on に変更するか、手動で強制してください:if ($ssl_client_verify != SUCCESS) { return 403; }。
別の方法として、管理プレーンを内部アドレスのみにバインドします(listen 10.0.0.5:443 ssl;)。こうすることで、ファイアウォールの設定前でもインターネットから到達できなくなります。
F4. Caddy の重要な設定行
{
email [email protected]
}
example.com {
tls [email protected]
header {
Strict-Transport-Security "max-age=15552000"
-Server
}
reverse_proxy 127.0.0.1:8000
}
自動 HTTPS は、ほぼ安全なデフォルト設定です(強制リダイレクト、管理された証明書、適切なプロトコルの下限)。それでも、残る項目を監査してください。アカウントのメールが確実に届くこと、tls internal の発行者(場所を特定し、プライベート CA を保護してください)の有無、ヘッダーのカスタマイズの有無を確認します。
F5. HAProxy の重要な設定行
global
ssl-default-bind-options ssl-min-ver TLSv1.2
frontend https_in
bind *:443 ssl alpn h2,http/1.1 crt /etc/haproxy/certs/example.com.pem
default_backend app
crt バンドルは、リーフ証明書、次に中間証明書を並べ、プライベートキーを連結したものにする必要があります(R7 で確認した fullchain の構成と同じです)。
F6. Apache の重要な設定行
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder off
Header always set Strict-Transport-Security "max-age=300"
</VirtualHost>
SSLCipherSuite は独自に組み立てるのではなく、Mozilla ジェネレーターの Apache プロファイルからそのままコピーしてください。Debian のモジュール:a2enmod ssl headers http2。WAF の選択肢として、a2enmod security2 と SecRuleEngine On を設定すると、mod_security がアプリの前段でリクエストをフィルタリングします。
F7. ACME チャレンジの例外設定に関する不変条件(# FIXED)
グローバルな deny/認証/リダイレクト設定を変更する前に、チャレンジパスが変更されずに残ることを確認してください:
location /.well-known/acme-challenge/ { root /var/www/html; } # stays PUBLIC
現時点ではチャレンジパスをブロックしても何も失敗しませんが、期限が近づくと更新がひそかに失敗します(メンタルモデル)。dns-01 チャレンジを優先するか、F1 に示す専用のキャッチオールを経由して HTTP-01 をルーティングしてください。
検証と妥当性確認
V1. プロトコルの下限(F1/F5/F6 の後)
echo | openssl s_client -connect HOST:443 -servername HOST -tls1_1 2>&1 | grep -i alert → handshake failure / protocol version アラートが表示されることを確認します。-tls1 でも繰り返します。次に、正常な最新プロトコルの経路を確認します:echo | openssl s_client -connect HOST:443 -servername HOST 2>/dev/null | grep '^New' → 強力な暗号スイートを使用した New, TLSv1.3, ...(または TLSv1.2)が表示されることを確認します。
V2. ヘッダー(F1/F4/F6 の後)
curl -sI https://HOST/ | grep -i strict-transport-security → 現在設定されている max-age を含むヘッダーが正確に 1 つだけあることを確認します。重複のリグレッションチェック:curl -sI https://HOST/ | grep -ic strict-transport-security の出力が 1 である必要があります。1 より大きい場合は、アプリ側でも設定されています。いずれか一方のレイヤーから削除してください(エッジ側を推奨します)。
V3. 否定系/リグレッションテスト
- サイトが読み込めること:
curl -sS -o /dev/null -w '%{http_code}\n' https://HOST/→ 200(またはアプリの通常の 30x リダイレクトが最終的に 200 になること)。 - 許可された CIDR からのヘルスチェック:許可リストにある範囲内の踏み台ホストから実行します:
ssh bastion 'curl -s -o /dev/null -w %{http_code} https://HOST/admin/'→ 200/有効な認証情報による 401 のフローが維持されていること。許可された範囲内からのアクセスが 403 になる場合は、deny all;が許可ルールより前に置かれています。F3 の順序に関するバグです。 - アップロードが引き続き制限内に収まること:
curl -sS -o /dev/null -w '%{http_code}\n' -F [email protected] https://HOST/upload→ 413 以外であること。その後、truncate -s 64M /tmp/opencode/big.binを実行して再度テストします → 413 が返れば上限が機能していることを確認できます。 - 不明なホストが引き続き拒否されること:R4 を再実行し、終了コードが引き続き 52/000 であることを確認します。
V4. 外部評価ツールによる抜き取りチェック(公開エンドポイントのみ):SSL Labs と testssl.sh ./testssl.sh HOST:443 を使用します。プロトコル/暗号スイート/証明書チェーンに関する警告がなく、A/A+ 評価となることを目指します。TLS を変更するたびに再テストしてください。サードパーティ製ツールによる情報提供を目的とした評価であり、監査した設定が引き続き唯一の正解です。
- 設定がクリーンであること:
nginx -t→ 構文が正しく、意図しない警告がないこと(古い形式と新しい形式の http2 構文に関する非推奨の通知は問題ありませんが、記録してください)。
V4. HSTS の段階的なロールアウト(順序の遵守が必須)
HTTPS の安定稼働を確認する前に長い max-age を適用すると、自らロックアウトを招くことになります。ブラウザーはポリシーをその期間いっぱい保持するため、対象のホスト/サブドメインで HTTPS のデプロイに問題が発生すると、その期間中アクセスできなくなります。段階的に設定します:max-age=300(24~48 時間観察し、監視に問題がないことを確認)→ 86400 → 604800 → 15552000。すべてのサブドメインを洗い出し、それぞれで有効な HTTPS を確認した後に限り、includeSubDomains を追加します。Preload への登録は最後に行います。削除には数か月かかります。
V5. 更新ループ(F2 の後)
sudo certbot renew --dry-run → 各リネージで成功することを確認します。フックが実行されることを確認します(journalctl --since today | grep certbot-hook を確認)。インストール済みの日付に変更がないことを確認します(ドライランでは何もインストールされません)。
V6. リポジトリのリグレッション検索(修正後。該当箇所は説明できるか、解消済みである必要があります)
- レガシープロトコル:
grep -RInE 'TLSv1(\.[01])?[";[:space:]]' --include='*.conf' . | grep -viE 'TLSv1\.2|TLSv1\.3' - 装飾的な mTLS:
grep -RIn 'optional_no_ca' . - IP 偽装につながる real_ip:
grep -RInE 'set_real_ip_from[[:space:]]+0\.0\.0\.0/0' . - リクエストボディの上限を設けずに中継する vhost:
grep -RL 'client_max_body_size' $(grep -RIl 'proxy_pass' --include='*.conf' .) - すべてのゲート付近で allow/deny の順序を目視確認します:
grep -RIn -B3 'deny all;' --include='*.conf' .
V7. 証拠の保持:R1~R12 の修正前と修正後の出力を監査チケットに保存します。差分が変更の証拠になります。
重大度の評価
CVSS v3.1 のベクトルに基づいて指摘事項を評価してください。
HIGH — 認証情報を扱うエンドポイントで、弱い暗号スイート(CBC/3DES/RC4)とともに TLSv1.0/1.1 が有効になっています。レガシークライアントに対するダウングレード攻撃と傍受を可能にします。CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N(7.4 High)。
MEDIUM — 公開 HTTPS アプリで HSTS が設定されていません。ネットワーク上の隣接する攻撃者による SSL ストリッピングの隙が生じます。悪用にはユーザー操作が必要です。CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N(4.2 Medium)。
MEDIUM — アップロード量に上限のないアプリのエンドポイントの前段に、実効性のあるリクエストボディサイズ制限がありません。大容量の POST により、低コストでストレージやメモリを枯渇させることができます。CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L(5.3 Medium)。
MEDIUM — ネットワークセグメントをまたぐプロキシからアプリへの通信が平文です(ループバック以外の RFC1918/パブリックネットワーク)。隣接する攻撃者が、通信中の認証情報、トークン、セッション Cookie を盗聴できます。CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N(6.4 Medium)。
LOW — バージョン情報の漏えい(Server: nginx/x.y.z、X-Powered-By)。悪用の手がかりになるだけで、該当する n-day が必要です。CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N(3.6 Low)。
LOW — OCSP ステープリングがありません。閲覧中のプライバシーに関する影響はわずかです。認証局が OCSP レスポンダーを CRL に切り替えて廃止するにつれ、影響はさらに小さくなっています。CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N(3.6 Low)。
修正要因:管理パスがインターネットから認証なしで到達可能な場合、そのアプリに関する指摘事項の重大度を 1 段階引き上げます(ゲートがないことで、その背後にあるすべての問題が悪化します)。証明書の期限切れやチェーンの不備があり、自動化されていない場合は、機密性への影響に加えて可用性インシデントとして評価します。
よくある誤検出
- CDN がオリジンの前段で TLS を終端している場合(Cloudflare/Akamai/CloudFront/ALB):
s_clientで確認できるのはエッジの証明書/設定であり、オリジンのものではありません。指摘事項は該当レイヤーに帰属させてください。特例 — Cloudflare の「Flexible」SSL モードでは、ビジターからエッジまでは HTTPS ですが、エッジからオリジンまでは平文の :80 です。オリジンがポート 80 でアプリに応答する場合、そのオリジンの露出は独立した指摘事項の分類に該当します(Full/Full-strict モードを必須とし、オリジンで :80 への応答を拒否してください)。DNS/インフラ文書からオリジン IP を特定し、CDN の範囲外から直接プローブして検出してください。 - クラウド ALB がプロバイダー管理のポリシー名で TLS を終端している場合:nginx 固有のディレクティブは存在しません。
ssl_protocolsがないことを理由にホストを不合格とするのではなく、ロードバランサーのセキュリティポリシー名をプロバイダーのドキュメントと照合して監査してください。 - 内部専用プロキシ(メッシュサイドカー、VPC 内のイングレス)で OCSP ステープリングやロギングの設定が緩和されている場合:同じプロトコルの最低基準を適用しますが、影響範囲が 1 つの信頼ゾーンに限られる場合は重大度を下げてください。黙って合格とせず、前提とする範囲を記録してください。
- テスト用/ステージング用の仮想ホストが意図的に公開されている場合(シードデータ、偽のログイン):書面で所有者を確認し、レビュー日と有効期限を記載して、スコアリング対象から除外してください。「準拠」とは決して判定しないでください。
- サイトが意図的に HTTP のみで運用されている場合(TLS がどこにもない):HTTPS における HSTS 未設定の指摘事項は該当しません。TLS 自体がないことは、より広範な暗号化の態勢に関する問題として扱います(適切な評価先に回してください)。
- 発行元の認証局がレスポンダーを廃止している場合(例:Let's Encrypt の 2025 年の段階的終了)に OCSP ステープリングが「ない」こと:情報提供のみで、対処は不要です。
- プロキシを経由しない、署名付き URL を使ったオブジェクトストレージへの直接アップロード:
client_max_body_sizeがないことはその経路には関係ありません。上限の確認対象はバケットポリシーに移ります(別のモジュールで扱います)。
参考資料
- Mozilla SSL Configuration Generator — サーバーとバージョンごとの標準的なプロトコル/暗号スイートのプロファイル:https://ssl-config.mozilla.org/(ミラー:https://mozilla.github.io/server-side-tls/ssl-config-generator/)
- nginx のドキュメント:https://nginx.org/en/docs/
- RFC 6797 — HTTP Strict Transport Security(HSTS)
- RFC 8446 — Transport Layer Security(TLS)プロトコル バージョン 1.3
- CWE-16:設定 — https://cwe.mitre.org/data/definitions/16.html
- CWE-319:機密情報の平文送信 — https://cwe.mitre.org/data/definitions/319.html
- OWASP Top 10:2021 A02 – 暗号化の失敗 — https://owasp.org/Top10/A02_2021-Cryptographic_Failures/
- Let's Encrypt のレート制限(ステージング環境と本番環境の計画):https://letsencrypt.org/docs/rate-limits/
- マニュアルページ:ss(8)、openssl-s_client(1ssl)、htpasswd(1)、curl(1)、パッケージに含まれる場合は certbot(1)。多くのディストリビューションでは nginx に nginx(8) が同梱されています。それ以外の場合は、上記の nginx オンラインドキュメントを参照してください。