DNS RobotDNS Propagation Checker
ホームDNS検索WHOISIP検索SSL
DNS RobotDNS Propagation Checker

次世代DNS伝播チェックツール

プライバシーポリシー利用規約私たちについてブログお問い合わせ

DNSツール

DNS検索DNS速度テストドメインからIP変換NS検索MX検索すべて表示

メールツール

SPFレコードチェッカーDMARCチェッカーDKIMチェッカーSMTPテストツールメールヘッダー解析すべて表示

ウェブサイトツール

WHOIS検索ホスティングチェッカードメイン空き状況確認サブドメイン検索CMS検出ツールすべて表示

ネットワークツール

PingツールトレースルートポートチェッカーHTTPヘッダーチェックSSL証明書チェックすべて表示

IPツール

IP検索自分のIPアドレス確認IPブラックリストチェックIPからホスト名変換ASN検索すべて表示

ユーティリティツール

QRコードスキャナーQRコード生成UPI QR Code GeneratorWiFi QR Code Generatorモールス信号変換すべて表示
© 2026 DNS Robot. 開発: ❤ Shaik Brothers
全システム正常稼働中
Made with
ホーム/ブログ/Webセキュリティ対策ガイド:6つの層で守るベストプラクティス(2026年版)

Webセキュリティ対策ガイド:6つの層で守るベストプラクティス(2026年版)

Shaik Vahid2026年9月29日30 分で読める
Webセキュリティ対策を6つの層で示した図:ドメインとDNS、TLS、HTTPヘッダー、アプリケーション、依存関係、サーバーと各層の確認チェック
Webセキュリティ対策を6つの層で示した図:ドメインとDNS、TLS、HTTPヘッダー、アプリケーション、依存関係、サーバーと各層の確認チェック

ポイント

Webセキュリティ対策は層で考えるのが基本です。ドメインとDNSを固め(レジストラロック、DNSSEC、CAA)、HSTSで最新のTLSを強制し、厳格なHTTPセキュリティヘッダーを送信します。パスワードはArgon2idでハッシュ化したうえでMFAとレート制限で守り、入力の検証とアクセス制御はサーバー側で行います。依存関係はバージョンを固定して監査し、提供していないポートはすべて閉じ、侵害に気づけるだけのログを残しましょう。以下の各対策には、具体的な設定と、それが本当に有効になっているかを無料で確認する方法を添えています。テストしていない対策は、存在しないのと同じだからです。

Advertisement

Webセキュリティ対策のベストプラクティスとは

Webセキュリティ対策のベストプラクティスとは、Webサイトやwebアプリケーションが乗っ取られたり、改ざんされたり、訪問者への攻撃に悪用されたり、気づかないうちにデータを抜き取られたりするのを防ぐための一連の対策です。1つの製品や1つの設定で完結するものではありません。ドメインレジストラから始まり、DNS、TLS、サーバーが送るHTTPヘッダー、ログインや入力を処理するコード、土台となるサードパーティのパッケージ、サーバーそのもの、そして問題の発生を知らせてくれるログまでを貫く、判断の積み重ねです。

層で考えるべき理由は、攻撃者がそう考えているからです。Verizonの2026年版データ侵害調査報告書(DBIR)によると、侵害の31%はソフトウェアの脆弱性の悪用から始まっており、初めて盗まれた認証情報を抜いて最も多い侵入経路になりました。また、全侵害の48%にランサムウェアが関与しています。パッチの適用漏れが1つ、APIキーの流出が1つ、レート制限のない管理画面が1つあれば、それで十分なのです。このガイドで紹介する対策に特殊なものはありません。侵害されるサイトの多くは、ある層を磨き上げる一方で、別の層の基本を飛ばしてしまったサイトです。

このガイドは外側から内側へと順に構成しています。各対策では、なぜ重要なのか、具体的に何を設定するのか、そしてどう確認するのかを、コマンドや無料ツールとあわせて説明します。HTTPヘッダーチェックやSSLチェックで最もよく見かける失敗は、判断の誤りではなく、誰かが有効だと思い込んだまま一度も確認していなかった設定だからです。

メモ

1時間しかないなら、次のことから始めてください。レジストラでレジストラロックとMFAを有効にする、HSTSでHTTPSを強制する、レイヤー3の表にあるセキュリティヘッダーを追加する、パスワードをArgon2idでハッシュ化する、既知のCVEがある依存関係をすべて更新する、80番と443番以外のポートをすべて閉じる。これだけで、現実に起きているWeb侵害の大半の侵入口をカバーできます。

Webセキュリティの全体像:守るべき6つの層

Webサイトへの攻撃は、必ず6つの層のどれかを狙います。以下の表は、このガイド全体の地図です。各層に何があり、典型的にどう攻撃され、どの無料チェックで防御が実際に機能しているかを確認できるかをまとめました。

層攻撃者がここで行うこと主な対策確認方法
1. ドメインとDNSドメインの乗っ取り、ネームサーバーの書き換え、放置されたサブドメインの乗っ取り、不正な証明書の取得レジストラロック、MFA、DNSSEC、CAA、古いレコードの棚卸しWHOIS検索、DNSルックアップ、サブドメイン検索
2. 通信(TLS)HTTPへのダウングレード、暗号化の除去、古い暗号スイートの悪用、期限切れの証明書TLS 1.2以上、preload付きのHSTS、更新の自動化SSLチェッカー
3. HTTPヘッダースクリプトの注入(XSS)、クリックジャッキングのためのフレーム埋め込み、コンテンツタイプの推測、リファラーの漏えいCSP、frame-ancestors、nosniff、Referrer-Policy、Permissions-PolicyHTTPヘッダーチェッカー
4. アプリケーションクレデンシャルスタッフィング、インジェクション、アクセス制御の不備、CSRF、安全でないアップロードArgon2id+MFA+レート制限、パラメータ化クエリ、サーバー側での認可、SameSite Cookieコードレビュー、OWASP ZAP、パスワード強度テスト
5. 依存関係悪意あるパッケージ、ライブラリの既知のCVE、CIパイプラインの侵害ロックファイル、監査、バージョン固定、SBOM、最小権限のトークンnpm audit、Dependabot、Trivy
6. サーバーと運用開いたままのポート、初期パスワード、未適用のパッチ、バックアップなし、ログなしファイアウォール、鍵認証のみのSSH、パッチ適用、3-2-1バックアップ、ログの集中管理ポートチェッカー、IPブラックリストチェッカー、ドメインヘルスチェック

ヒント

上から順に進めてください。レイヤー1〜3は午後の数時間で終わる設定作業で、すべてのページを一度に守れます。レイヤー4〜6は継続的な習慣であり、コードレビューとデプロイのプロセスに組み込んでおく必要があります。

Advertisement

レイヤー1:ドメインとDNSを守る

このガイドのほかのすべての対策は、あなたがまだ自分のドメインを管理できていることを前提にしています。攻撃者がレジストラにログインしたりネームサーバーを書き換えたりできれば、トラフィックを好きな場所へ向け、あなたのドメイン名で有効な証明書を取得し、あなた宛てのメールをすべて読めてしまいます。その間、あなたのアプリケーションのコードは一度も実行されません。だからこそドメインとDNSのセキュリティはWebセキュリティ対策の第一歩であり、その大部分は設定だけで完了します。

まず自分のドメインでWHOIS検索を行い、3つの点を確認してください。ステータスコードにclientTransferProhibitedが含まれていること、有効期限が1年以上先であること、そしてレジストラが実際に自分でアカウントを持っているところであることです。企業のドメインが、以前の外注業者のレジストラアカウントに残ったままになっているケースは意外なほどよくあります。表示されるステータスコードの意味は、WHOIS検索ガイドですべて解説しています。

レジストラロック、MFA、有効期限アラート

レジストラロック(トランスファーロックとも呼ばれます)を有効にして、明示的に解除しない限りドメインをほかのレジストラへ移管できないようにし、レジストラのアカウント自体でも多要素認証(MFA)をオンにしましょう。1つのログインで下流のすべてを支配できるため、レジストラのアカウントはフィッシングの格好の標的です。レジストラが対応していれば、SMSではなくハードウェアキーや認証アプリを使ってください。

ドメインは期限切れにならない支払い方法で自動更新に設定し、そのうえで有効期限の60日前にカレンダーのアラートも入れておきましょう。失効したドメインはドロップキャッチ業者に数時間で取得され、購入者はあなたのメール、トラフィック、そしてそのドメインを信頼しているOAuthログインをすべて引き継ぎます。

  • レジストリロック(レジストリ側で手動かつ帯域外で行うロック)は、事業が依存しているドメインなら追加料金を払う価値があります。レジストラのアカウントが侵害されても、ネームサーバーの変更を防げます。

  • 役割を分けます。 ドメインの費用を払う人、ドメインを所有するアカウント、DNSプロバイダーをそれぞれ文書化し、特定の社員のメールボックスに紐づけないようにします。

  • WHOISプライバシーを有効にして、連絡先情報がフィッシングの地図にならないようにします。アカウントにはdomains@のような、監視されている役割アドレスを登録しておきましょう。

DNSSECでゾーンに署名する

DNSSECはDNSゾーンに署名し、リゾルバーが偽造された応答を検知できるようにします。DNSSECがなければ、リゾルバーのキャッシュを汚染できる攻撃者は、ブラウザにあなたのドメイン名を表示させたまま訪問者を偽のサーバーへ誘導できます。多くのマネージドDNSプロバイダー(Cloudflare、Route 53、Google Cloud DNS、そして多くのレジストラ)では、スイッチ1つで有効にできます。手作業が必要なのは、レジストラでDSレコードを公開する手順だけです。確認はdigか、ドメインヘルスチェックに含まれるDNSSECチェックで行えます。

bash
dig +dnssec +short yourdomain.com A
# A signed zone returns the record AND an RRSIG line:
# 203.0.113.10
# A 13 2 3600 20261015000000 20260924000000 34505 yourdomain.com. Kx3f...==

dig +short yourdomain.com DS
# A DS record at the parent zone proves the chain of trust is complete
# 34505 13 2 6A1B...C9

CAAレコードで証明書の発行元を制限する

CAAレコード(RFC 8659)は、どの認証局(CA)があなたのドメインの証明書を発行してよいかを指定します。すべての公的CAは発行前にこれを確認する義務があるため、CAAレコードがあれば、別のCAのドメイン認証をだました攻撃者を締め出せます。一般的なケースは次の3つのレコードでカバーできます。証明書を発行できるCA、ワイルドカード証明書を発行できるCA、そして拒否された発行リクエストの報告先です。

dns
yourdomain.com.  CAA 0 issue "letsencrypt.org"
yourdomain.com.  CAA 0 issuewild ";"
yourdomain.com.  CAA 0 iodef "mailto:security@yourdomain.com"

注意

CAAを追加する前に、現在あなたのドメインの証明書を発行しているCAをすべて洗い出してください。CDNやホスティングのコントロールパネルの裏で使われているCAも含みます(Cloudflareは複数のCAを切り替えて使い、多くのホスティング会社はSectigoやLet's Encryptを使っています)。実際に使っているCAを含まないCAAレコードは、次回の更新を黙って失敗させます。まずSSLチェッカーで現在の発行元を確認しましょう。

古いレコードを棚卸しする:サブドメイン乗っ取り

サブドメイン乗っ取りは、利用をやめたサービスをDNSレコードがまだ指しているときに起こります。削除済みのGitHub Pagesサイト、Azureのアプリ、S3バケット、SaaSベンダーのホスト名へのCNAMEなどです。放置されたその宛先は誰でも登録でき、そうなるとold-app.yourdomain.com上で、あなたの名前のまま、有効な証明書付きでコンテンツを配信できてしまいます。レコードの存在を誰も覚えていないため、バグバウンティプログラムで最もよく報告される問題の1つです。

証明書の透明性(CT)ログを読み取るサブドメイン検索にドメインをかけると、これまでに証明書が発行されたすべてのホスト名がわかります。次に、それぞれをDNSルックアップで名前解決し、宛先がNXDOMAINやプロバイダーの「no such app」ページを返すレコードは削除します。これを四半期ごとに繰り返し、サービスを廃止する際の手順にDNSレコードの削除を含めておきましょう。

ヒント

証明書の透明性は早期警報システムにもなります。Cert SpotterやCloudflareのCTモニタリングを使えば、どこかのCAがあなたのドメインの証明書を発行するたびにメールで通知を受け取れます。身に覚えのない証明書は、DNSやレジストラが侵害されたことを示す最初の目に見える兆候であることが少なくありません。

レイヤー2:HTTPSとTLSを正しく設定する

HTTPSはもはやベストプラクティスではなく、最低限の前提です。ブラウザは素のHTTPのページを「保護されていない通信」と表示します。安全なサイトと単に暗号化されているだけのサイトを今も分けているのは、受け入れるTLSのバージョン、そもそもHTTPで到達できる経路が残っているかどうか、そして2026年3月に始まった証明書有効期間の短縮に耐えられるほど更新が自動化されているかどうかです。

Advertisement

TLS 1.2を最低ラインに、TLS 1.3を優先

TLS 1.0と1.1は2021年にRFC 8996で正式に非推奨となり、現行のブラウザはどれもこれらをネゴシエートしません。サーバーでは無効にし、TLS 1.3(既知の弱い暗号スイートをすべて排除し、1往復でハンドシェイクを完了します)を優先してください。TLS 1.2は、ECDHE-ECDSA-AES128-GCM-SHA256やECDHE-RSA-CHACHA20-POLY1305のようなAEAD暗号スイートに限って残します。

外部からテストするときに重要なのは2行です。ネゴシエートされたプロトコルと、証明書チェーン全体が検証されたことを示すVerify return code: 0です。中間証明書の欠落は、「Chromeでは動くのにcurlやAndroidでは失敗する」という報告の最も一般的な原因です。修正方法はSSL証明書チェーンのガイドで解説しており、SSLチェッカーは不完全なチェーンを直接指摘します。以下はdnsrobot.netに対して実行した例です。

bash
echo | openssl s_client -connect dnsrobot.net:443 -servername dnsrobot.net 2>/dev/null \
  | grep -E 'Protocol|Cipher|Verify return'

# Protocol  : TLSv1.3
# Cipher    : TLS_AES_256_GCM_SHA384
# Verify return code: 0 (ok)

# Confirm the old versions are refused:
openssl s_client -connect dnsrobot.net:443 -tls1_1 2>&1 | grep -iE 'alert|no protocols'

メモ

コピー&ペーストした設定が古くなりやすいのが暗号スイートのリストです。手書きする代わりに、Mozilla SSL Configuration Generatorでnginx、Apache、Caddy、HAProxy向けの最新の推奨サーバー設定を生成しましょう。非常に古いクライアントをサポートする必要がない限り、「Intermediate」プロファイルを選んでください。

HSTS:ブラウザに二度とHTTPを使わせない

HTTPからHTTPSへのリダイレクトだけでは不十分です。訪問者がhttp://yourdomain.comに送る最初のリクエストは依然として平文で送られ、同じネットワーク上の攻撃者はリダイレクトより先に応答できてしまいます。これを解決するのがHTTP Strict Transport Security(HSTS)(RFC 6797)です。一度このヘッダーを受け取ったブラウザは、以後あなたのドメインへのhttp://のURLを、何かを送信する前にすべてhttps://に書き換えます。

max-ageは最低1年(31536000秒)に設定し、すべてのサブドメインがHTTPSで配信できるようになったらincludeSubDomainsを追加します。そのうえでhstspreload.orgにドメインを登録すれば、Chrome、Firefox、Safari、Edgeに最初から組み込まれて配布されます。プリロードによって、初回アクセス時のすき間は完全になくなります。ヘッダーはHTTPヘッダーチェッカーで確認できます。HSTSはA〜Fのセキュリティスコアの評価項目に含まれています。

http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

注意

HSTSは一方通行の扉です。includeSubDomainsがプリロードされると、有効なHTTPSを提供できないサブドメインは、社内ツールや忘れられたdev.ホストも含めて、ブラウザから到達できなくなります。段階的に展開してください。まずmax-age=300で1週間、次に1か月、次に1年とし、そのあとで初めてpreloadを追加します。

有効期間47日の証明書へ:今すぐ更新を自動化する

CA/Browser Forumの投票SC-081により、すべての公的TLS証明書の最大有効期間は段階的に短縮されています。最初の短縮は2026年3月15日に発効したため、今日購入・更新する証明書はすでに200日に制限されており、2029年にはおよそ6週間ごとに証明書を差し替える必要が出てきます。

発効日証明書の最大有効期間ドメイン認証の再利用期間
2026年3月15日より前398日398日
2026年3月15日200日200日
2027年3月15日100日100日
2029年3月15日47日10日

手動の更新ではこのスケジュールに耐えられません。ベストプラクティスはACMEによる完全自動の発行です。certbot、acme.sh、Caddyを通じたLet's EncryptやGoogle Trust Services、あるいはCloudflare、Vercel、Netlify、多くのホスティングパネルに組み込まれた自動証明書を使いましょう。更新の経路は必要になる前にテストしてください(certbotならcertbot renew --dry-run)。注意すべき失敗は、前回の更新以降に追加されたCAAレコードやファイアウォールのルールが検証をブロックしているケースです。何を使うにしても、有効期限は外部からも監視しましょう。SSLチェッカーは残り日数を表示します。証明書が期限切れになると、すべての訪問が全画面のブラウザ警告に変わってしまいます。

レイヤー3:HTTPセキュリティヘッダー

セキュリティヘッダーは、サーバーがブラウザに対して、ページで何をしてよく、何をしてはいけないかを伝える指示です。どのスクリプトを実行できるか、ページをフレームに埋め込めるか、ブラウザがコンテンツタイプを推測してよいか、訪問者がどこから来たかをほかのサイトにどこまで伝えるか、といった内容です。コストはかからず、すべてのページに一度に適用され、攻撃の種類ごとまとめてブロックできます。一方で、多くのサイトが部分的に間違えている層でもあり、だからこそHTTPヘッダーチェッカーはこれを採点しています。以下は、dnsrobot.netが送信しているヘッダーをセキュリティ関連に絞ったものです。

bash
curl -sI https://dnsrobot.net/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'

strict-transport-security: max-age=31536000; includeSubDomains; preload
content-security-policy: default-src 'self'; script-src 'self' ... https://*.googletagmanager.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
x-frame-options: DENY
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(self), microphone=(), geolocation=(), payment=(), usb=()

すべてのサイトが送るべき7つのヘッダー

一般的なサイトで目指すべき値は次のとおりです。最初の5つが評価を左右するヘッダーで、残りの2つはほかが揃ったあとに手軽に追加できるものです。

ヘッダー推奨値防げる攻撃
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preloadプロトコルのダウングレード、HTTP経由でのCookie窃取
Content-Security-Policynonceベースのscript-src('strict-dynamic'付き)、object-src 'none'、base-uri 'none'、frame-ancestors 'none'クロスサイトスクリプティング(XSS)、データインジェクション、クリックジャッキング
X-Content-Type-Optionsnosniffアップロードされたファイルを実行可能なスクリプトに変えてしまうMIMEスニッフィング
Referrer-Policystrict-origin-when-cross-origin完全なURL(トークンや検索語)の第三者への漏えい
Permissions-Policycamera=(), microphone=(), geolocation=(), payment=()サードパーティのスクリプトによる強力なブラウザAPIの無断使用
X-Frame-OptionsDENY(frame-ancestorsのレガシーな代替)CSP Level 2に対応していないブラウザでのクリックジャッキング
Cross-Origin-Opener-Policysame-originwindow.openerを介したクロスウィンドウ攻撃

サイトを壊さずにContent Security Policyを導入する

Content Security Policy(CSP)は、インジェクションのバグが存在していても注入されたスクリプトの実行を止められるため、XSSに対して最も効果的な防御策です。難点は、厳格なポリシーを適用すると、忘れていたインラインスクリプトやサードパーティのタグがすべて動かなくなることです。Googleが推奨し、MDNでも解説されている現在のアプローチはnonceベースのポリシーです。サーバーがレスポンスごとにランダムなnonceを生成して、意図して出力するすべてのscriptタグに付与し、ブラウザはそれ以外のスクリプトをすべて拒否します。

このポリシーを実用的にしているのが'strict-dynamic'です。nonce付きのスクリプトは、個別に許可リストへ載せなくても追加のスクリプト(アナリティクス、タグマネージャー、ウィジェット)を読み込めます。一方、https:と'unsafe-inline'のトークンは最新のブラウザでは無視され、古いブラウザ向けのフォールバックとしてのみ機能します。まずContent-Security-Policy-Report-Onlyで展開し、1週間ほど違反レポートを確認してから強制モードに切り替えましょう。Next.js、Rails、Djangoなどのフレームワークは公式にnonceをサポートしています。静的サイトでは、代わりにハッシュベースのscript-srcを使えます。

http
Content-Security-Policy:
  script-src 'nonce-r4nd0m1z3dV4lu3' 'strict-dynamic' https: 'unsafe-inline';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  form-action 'self';
  report-to csp-endpoint

<!-- Only scripts carrying the nonce run; strict-dynamic lets them load their own dependencies -->
<script nonce="r4nd0m1z3dV4lu3" src="/app.js"></script>

メモ

script-src 'self' https://cdn.example.comのような許可リスト型のポリシーは何もないよりはましですが、許可したホスト上のJSONPエンドポイントや古いライブラリを使って回避されるおそれがあります。まだnonceを使えない場合でも、少なくともobject-src 'none'とbase-uri 'none'は設定してください。よくある回避手法のうち2つをすぐに塞げます。

クリックジャッキング対策:frame-ancestorsとX-Frame-Options

クリックジャッキングは、攻撃者のページの中にあなたのサイトを見えない状態で読み込み、訪問者に見えないボタンをクリックさせる攻撃です。CSPのframe-ancestors 'none'(自サイトのページを埋め込む場合は'self')で現行のすべてのブラウザで防げ、X-Frame-Options: DENYで古いブラウザもカバーできます。埋め込み可能なウィジェットを意図的に提供する場合は、そのパスだけを、必要なオリジンに対してだけ除外してください。X-Frame-Optionsガイドでは、具体的なサーバーのルールと、テスト中に遭遇する「refused to connect」エラーを詳しく解説しています。

nosniff、Referrer-Policy、Permissions-Policy、COOP

X-Content-Type-Options: nosniffは、ブラウザがContent-Typeを勝手に推測し直すのを防ぎます。この推測こそが、HTMLを含むユーザーアップロードの「画像」を実行可能なページに変えてしまう原因です。Referrer-Policy: strict-origin-when-cross-origin(現行ブラウザのデフォルトですが、明示的に設定しましょう)は、ほかのサイトにはオリジンだけを送るため、URLに含まれるパスワードリセット用のトークンや検索クエリが外部に漏れません。Permissions-Policyは使っていないブラウザ機能を無効にするので、侵害されたサードパーティのスクリプトがカメラを起動したり位置情報を読み取ったりできなくなります。Cross-Origin-Opener-Policy: same-originは、開いたページとのwindow.openerのつながりを切断し、クロスウィンドウ攻撃の一群をブロックします。

削除すべきヘッダーも重要です。ServerとX-Powered-Byは正確なソフトウェアのバージョンを脆弱性スキャナーに知らせてしまいます。X-XSS-Protectionは非推奨で、古いブラウザではバグの原因になりうるため、0に設定するか削除してください。CMS検出ツールを使えば、スキャナーがヘッダーだけからあなたのスタックについて何を把握できるかがわかります。

ヒント

ヘッダーはページごとではなく、エッジ(CDN、リバースプロキシ、フレームワークのミドルウェア)で一度だけ設定しましょう。Next.jsならproxyまたはmiddlewareのファイル、nginxならserverコンテキストのadd_headerブロック、ApacheならHeader always setです。そしてデプロイのたびにHTTPヘッダーチェッカーを再実行してください。フレームワークの新バージョンやCDNの設定変更で、ヘッダーが1つ黙って消えることがあるためです。

レイヤー4:クレデンシャルスタッフィングに耐える認証

攻撃者がパスワードを1つずつ推測することはほとんどありません。ほかのサイトから流出した何十億ものメールアドレスとパスワードの組み合わせを使い回し(クレデンシャルスタッフィング)、残りはフィッシングで盗みます。そのため認証のベストプラクティスは3つの要素からなります。データベースが流出してもパスワードは流出しないように保存すること、盗まれたパスワード単体では役に立たないようにすること、そしてブルートフォースを意味がないほど遅くすることです。OWASP Top 10:2025では、Authentication Failures(認証の失敗)がA07に位置づけられています。

Advertisement

パスワードはArgon2idまたはbcryptでハッシュ化する

パスワードをそのまま保存してはいけませんし、高速なハッシュで保存してもいけません。MD5、SHA-1、さらにはSHA-256も高速に計算できるよう設計されているため、流出したハッシュの一覧は1枚のGPUで毎秒数十億回の推測にかけられます。パスワードごとに固有のソルトを付けたうえで、低速でメモリ負荷の高いパスワードハッシュ関数を使いましょう。OWASP Password Storage Cheat Sheetは、次の順で推奨しています。

  • Argon2id:メモリ最低19 MiB、反復回数2、並列度1(または46 MiBで反復回数1)。

  • scrypt:Argon2が使えない場合に、N = 2^17、r = 8、p = 1で使用。

  • bcrypt:ワークファクター10以上(入力が72バイトまでという制限に注意。長いパスワードを事前ハッシュする場合は慎重な実装が必要です)。

  • PBKDF2-HMAC-SHA256:FIPS準拠が必須の場合に限り、反復回数600,000回で使用。

javascript
// Node.js with the argon2 package
import argon2 from "argon2";

const hash = await argon2.hash(password, {
  type: argon2.argon2id,
  memoryCost: 19456, // KiB = 19 MiB
  timeCost: 2,
  parallelism: 1,
});

// Later, on login:
const ok = await argon2.verify(hash, submittedPassword);

メモ

ログインサーバー上でハッシュ1回がおよそ100〜250ミリ秒かかるようにコストを調整しましょう。実際のユーザーには気づかれない程度ですが、攻撃者の推測速度を数十億回ではなく、1コアあたり毎秒数千回に抑えられます。パラメータを引き上げたときは、ログイン成功時に再ハッシュすれば、古いハッシュが自動的にアップグレードされます。

本当に役立つパスワードルール(NIST SP 800-63B)

多くのサイトがいまだに強制している文字種の組み合わせルール(大文字を1つ、記号を1つ、90日ごとに変更)は、Summer2026!のような予測しやすいパスワードを生み、使い回しを促してしまいます。現在のNIST SP 800-63Bのガイダンスは、これらを本当に重要な点を測るルールに置き換えています。

  • MFAも必須にする場合は最低8文字、パスワード単体で使う場合は最低15文字とし、少なくとも64文字まで入力できるようにします。

  • 文字種の組み合わせルールは設けず、定期的な変更も強制しません。変更を求めるのは、侵害の証拠がある場合だけです。

  • 新しいパスワードはすべて流出リストと照合し(Have I Been Pwnedのrange APIを使えば、パスワード自体を送信せずに照合できます)、既知の流出パスワードは拒否します。

  • 貼り付けやパスワードマネージャーを許可し、スペースやUnicodeも受け付け、ルールではなくエントロピーにもとづく強度メーターを表示します。

パスワード強度テストを使うと、これらの指標が古いルールとどう違うかがわかります。エントロピーとパターン検出から解読にかかる時間を推定し、流出データとも照合するため、赤字の「記号が必要です」というメッセージよりはるかに役立つフィードバックが得られます。

MFAとパスキー

多要素認証を導入すれば、盗まれたパスワードは行き止まりになります。すべてのユーザーに提供し、管理者、経理、サポートの役割では必須にしましょう。選択肢はフィッシング耐性で順位づけします。パスキーとFIDO2セキュリティキーは、認証情報が正規のオリジンに結びついているためフィッシングされません。認証アプリ(TOTP)は良い選択肢です。SMSコードはないよりはましですが、SIMスワップによって傍受されるおそれがあります。パスキーは現行のすべてのブラウザとOSでサポートされており、パスワードそのものをなくせるため、クレデンシャルスタッフィングという攻撃カテゴリー自体も消えます。

アカウント復旧の経路もログインと同じくらい慎重に守りましょう。リカバリーコードはハッシュ化して保存し、メールによるリセットには15分以内に失効する使い捨てトークンを使い、秘密の質問は使わないようにします。

レート制限、アカウントロック、ボット対策

ログイン、登録、パスワードリセット、MFAの各エンドポイントには、IPごとかつアカウントごとにレート制限をかけ、上限に達したら429 Too Many RequestsをRetry-Afterヘッダー付きで返します(クライアント側の対応はHTTPエラー429のガイドで解説しています)。これに加えて、1つのアカウントで失敗が続いた場合は段階的に遅延を増やすか一時的にロックし、分散型の攻撃を受けるエンドポイントにはCAPTCHAやプルーフ・オブ・ワークのチャレンジを導入します。失敗したログインはすべて送信元IPとともに記録しておけば、クレデンシャルスタッフィングのパターンを数か月後ではなく数分で見つけられます。

nginx
# nginx: 5 login attempts per minute per client IP
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location = /api/login {
    limit_req zone=login burst=4 nodelay;
    limit_req_status 429;
    proxy_pass http://app;
}

注意

「ユーザーが存在しない」場合と「パスワードが違う」場合でエラーメッセージを同一にし、応答時間も揃えてください(ユーザーが存在しないときはダミーのパスワードをハッシュ化します)。そうしないと、ログインフォームがユーザー列挙用のAPIとして使えてしまいます。

セッション、Cookie、CSRF

ログイン後は、セッションCookieがユーザーそのものです。したがって、パスワードと同等の保護が必要です。3つのCookie属性と1つの名前プレフィックスで、対策のほとんどをまかなえます。

  • Secureを付けると、Cookieは平文のHTTPで送信されなくなるため、公衆Wi-Fiで盗聴されません。

  • HttpOnlyを付けるとJavaScriptから見えなくなるため、XSSのバグがあってもCookieを読み取られません。

  • SameSite=Lax(管理画面ならStrict)を付けると、クロスサイトのPOSTリクエストでCookieが送られなくなり、ほとんどのCSRF攻撃を無力化できます。

  • __Host-プレフィックスを付けると、Secureがあり、Path=/で、Domain属性がない場合を除いてブラウザがCookieを拒否するため、サブドメインから上書きされなくなります。

http
Set-Cookie: __Host-session=9f1c7e2a...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800

ヒント

状態を変更する操作は、決してGETで実行できるようにしてはいけません。/account/delete?id=42のようなリンクは、ユーザーが訪れた任意のページ上の<img>タグから実行できてしまいます。Cookieのフラグがどれほど適切でも同じです。

CSRF:SameSiteの背後にもう1つの防御線を

クロスサイトリクエストフォージェリ(CSRF)は、ログイン中のブラウザをだまし、別のページからあなたのサイトへ状態を変更するリクエストを送信させる攻撃です。SameSite Cookieで一般的なケースは防げますが、GET以外のすべてのリクエストにはもう1つ防御を残しておきましょう。hiddenフィールドやカスタムヘッダーに入れるセッションごとのCSRF対策トークンか、Origin(またはSec-Fetch-Site)ヘッダーが自サイトのオリジンと一致するかのチェックです。ほとんどのフレームワーク(Django、Rails、Laravel、Next.jsのServer Actions)はデフォルトでこれを行います。よくある失敗は、APIエンドポイントでこれを無効にしたうえで、そのエンドポイントをブラウザから呼び出すことです。

セッションID、ローテーション、JWT

セッションIDは少なくとも128ビットのランダム性をもって生成し、セッション固定攻撃を防ぐためにログイン時と権限が変わるたびにIDをローテーションし、一定時間操作がなければセッションを失効させ、サーバー側でセッションを無効化する「すべてのデバイスからログアウト」ボタンを用意しましょう。JWTは有効期間を短くし(数分程度にして、失効させられるリフレッシュトークンと組み合わせます)、どのスクリプトからも読めるlocalStorageには決して保存しないでください。署名鍵が漏れたら完全な侵害として扱います。その鍵でこれまでに署名されたすべてのトークンが偽造可能になるからです。

インジェクションとXSS:入力を検証し、出力をエンコードする

インジェクション(Injection、A05:2025)とクロスサイトスクリプティングは、場所が違うだけで同じ間違いです。ユーザーからのデータを、まるでコードであるかのようにインタープリター(SQL、シェル、LDAPクエリ、HTMLページ)に渡してしまうことです。対策もどこでも同じで、データとコードを分離し、文字列の連結でコマンドを組み立てないことです。

Advertisement

文字列を組み立てず、パラメータ化クエリを使う

SQLでは、プリペアドステートメントか、それを生成するORMを使いましょう。そうすればデータベースは入力を値として扱い、クエリの構造が変わることはありません。同じルールはシェルコマンド(文字列ではなく引数の配列で渡す)、NoSQLのフィルター(ユーザー入力に含まれる{"$gt": ""}のような演算子オブジェクトを拒否する)、ファイルパス(パスを解決し、想定したディレクトリの配下に収まっていることを確認する)にも当てはまります。

javascript
// Vulnerable: the input becomes part of the query text
const rows = await db.query(`SELECT * FROM users WHERE email = '${email}'`);

// Safe: the driver sends the value separately from the query
const rows = await db.query("SELECT * FROM users WHERE email = $1", [email]);

// Python / psycopg: same idea
cur.execute("SELECT * FROM users WHERE email = %s", (email,))

出力エンコードでXSSを防ぐ

クロスサイトスクリプティングは、ユーザーが制御できるテキストを、出力先のコンテキストに合わせてエンコードせずにページへ書き込んだときに発生します。最近のテンプレートエンジン(ReactとJSX、Vue、Jinja2、Blade、ERB)はデフォルトでエンコードするため、現在のXSSの多くは抜け道から生まれます。dangerouslySetInnerHTML、v-html、|safe、innerHTML、そしてユーザーデータからURLやインラインのイベントハンドラーを組み立てる処理です。出力先のコンテキストに正確に合わせてエンコードし、リッチなHTMLは正規表現ではなくDOMPurifyのような専用ライブラリでサニタイズしましょう。

データの出力先エンコード方法例
HTML本文HTMLエンティティ<は&lt;に、"は&quot;に変換
HTML属性属性値を引用符で囲んだうえでのエンティティ化属性値は必ず引用符で囲み、"と'をエンコード
JavaScriptスクリプトのテキストに直接埋め込まない。data-属性かJSONの<script type="application/json">ブロック経由でデータを渡す文字列内の</script>は\u003c/script\u003eに変換
URLパラメータパーセントエンコーディングencodeURIComponent(value)
CSSの値一切使わない。クラス名を許可リストから選ぶユーザー入力をstyleに埋め込まない

SSRF、ファイルアップロード、デシリアライズ

サーバーサイドリクエストフォージェリ(SSRF):サーバーがユーザーから指定されたURLを取得する場合(Webhook、画像プロキシ、リンクプレビューなど)、攻撃者はその宛先を内部サービスやクラウドのメタデータエンドポイント169.254.169.254に向け、認証情報を読み取れます。まずホスト名を名前解決し、プライベートおよびリンクローカルの範囲を拒否し、リダイレクトを無効にし、取得処理は内部の何にも到達できないネットワークセグメントに置きましょう。

ファイルアップロード:ファイルの種類は、拡張子やクライアントが送るContent-Typeではなく、中身を調べて検証します。サイズの上限を設け、ファイルはWebルートの外かオブジェクトストレージに、自分で生成したランダムな名前で保存し、可能であればnosniffとContent-Disposition: attachmentを付けて別のオリジンから配信します。アップロードされたファイルが、Webサーバーに実行される場所に置かれることがあってはなりません。

デシリアライズとマスアサインメント:信頼できないデータを、任意のクラスをインスタンス化できる形式(Javaのネイティブシリアライズ、Pythonのpickle、PHPのunserialize、カスタムタグ付きのYAML)でデシリアライズしてはいけません。スキーマ付きのJSONを使いましょう。リクエストボディは明示的な許可リストのフィールドにだけバインドし、たまたまそのカラムを持つモデルにユーザーが"role": "admin"をPOSTできないようにします。

注意

バリデーションは形式(メールアドレスか、正の整数か、5つの値のどれかか)を確かめるためのもので、セキュリティのためのものではありません。<script>や引用符を取り除くブロックリスト方式は、必ず回避されます。想定したものだけを受け付け、出力時にはエンコードし、保存時にはパラメータ化しましょう。

アクセス制御の不備(Broken Access Control):第1位のリスク

Broken Access Control(アクセス制御の不備)は2021年からOWASP Top 10の首位を守り続け、A01:2025でもその座にあります。これは単一のバグではなく、習慣の問題です。リクエストのたびに、相手が誰か(認証)は確認しても、その人が何に触れてよいか(認可)の確認を忘れてしまうのです。典型的な形が安全でない直接オブジェクト参照(IDOR)です。GET /api/invoices/1042は請求書の所有者には機能しますが、番号を書き換えた誰にでも機能してしまいます。

  • デフォルトで拒否します。 すべてのルートでアクセスを許可する明示的なルールを必須にし、ルールがなければ200ではなく403を返します。

  • サーバー側で、オブジェクトごとに強制します。 UIでボタンを隠すのはアクセス制御ではありません。一括処理のエンドポイント、エクスポート、バックグラウンドジョブも含め、すべての読み書きで、現在のユーザーがその特定のレコードを所有しているか、アクセスを許可されているかを確認します。

  • 役立つ場面では推測困難なID(UUID)を使いますが、それに頼ってはいけません。隠すことは認可ではありません。

  • CORSを厳格にします。 認証情報付きのAccess-Control-Allow-Origin: *や、リクエストが送ってきたOriginをそのまま返す設定は、ユーザーが訪れるあらゆるWebサイトにAPIを明け渡すことになります。

  • ディレクトリ一覧表示を無効にし、.git、.env、バックアップファイル、設定ファイルへのアクセスをWebサーバーのレベルでブロックします。管理画面は公開インターネットから切り離すか、MFAとIP許可リストで保護します。

  • 認可の失敗にもレート制限をかけ、ログに記録します。 1つのセッションから403が連続するのは、列挙攻撃が進行中のサインです。

javascript
// Express: ownership check on every object access
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
  const invoice = await Invoice.findById(req.params.id);
  if (!invoice || invoice.ownerId !== req.user.id) {
    return res.status(404).end(); // 404, not 403: don't confirm the record exists
  }
  res.json(invoice);
});

メモ

アクセス制御は攻撃者と同じ方法でテストしましょう。低い権限のユーザーでログインしてすべてのリクエストを記録し、それぞれを別のユーザーのIDに差し替えて再送し、さらにAuthorizationヘッダーを外して再送します。自動スキャナーはインジェクションを見つけますが、認可のバグはほとんど見つけられません。だからこそ、こうしたバグは何年も残り続けるのです。

レイヤー5:依存関係とソフトウェアサプライチェーン

一般的なwebアプリケーションで、自分で書いたコードはおそらく5%程度で、残りの95%はダウンロードしたコードです。だからこそ2025年版のOWASP Top 10ではSoftware Supply Chain Failures(ソフトウェアサプライチェーンの失敗)がA03に初登場し、2026年のDBIRでは脆弱性の悪用が盗まれた認証情報を上回りました。2025年9月には、フィッシングの被害にあった1人のメンテナーを通じて、chalk、debug、そしてほかの16のnpmパッケージ(合計で週20億回以上ダウンロードされています)に暗号資産を盗むマルウェアが仕込まれました。その期間中にバージョンを固定せずにインストールしたプロジェクトは、すべて自動的にそれを取り込んでしまいました。

  • ロックファイルをコミットし(package-lock.json、yarn.lock、pnpm-lock.yaml、poetry.lock、go.sum)、CIではnpm ciでインストールします。これでビルドの再現性が保たれ、上流の新しいリリースがレビューなしに紛れ込むことを防げます。

  • 継続的にスキャンします:npm audit、pip-audit、bundler-audit、更新にはGitHub DependabotやRenovate、OSレイヤーにはTrivyやGrypeのようなコンテナスキャナーを使います。

  • セキュリティ以外のアップグレードは数日遅らせます(RenovateのminimumReleaseAge)。そうすれば、悪意あるリリースは多くの場合、取り込む前に取り下げられます。ただし、セキュリティパッチはすぐに適用してください。

  • CIではサードパーティのアクションやスクリプトをコミットSHAに固定し、公開CDNから読み込むスクリプトにはすべてSubresource Integrity(integrity="sha384-...")を付けます。

  • CIには最小権限を与えます:可能な限り読み取り専用のトークンを使い、長期間有効なシークレットの代わりに短命のOIDC認証情報を使い、公開(publish)の権限はリリース用のジョブだけに限定します。

  • SBOMを生成します(CycloneDXまたはSPDX)。ビルド時に生成しておけば、次にLog4Shell級のCVEが公表されたとき、「自分たちは影響を受けるのか」に数分で答えられます。

bash
# Reproducible install + audit in CI
npm ci --ignore-scripts
npm audit --audit-level=high

# Pin a GitHub Action to a commit, not a floating tag
# uses: actions/checkout@v4                 <- can change underneath you
# uses: actions/checkout@<full-commit-sha>  # v4.2.2

# Subresource Integrity for a CDN script (hash from: openssl dgst -sha384 -binary lib.min.js | openssl base64 -A)
<script src="https://cdn.example.com/lib.min.js"
        integrity="sha384-<base64-hash>"
        crossorigin="anonymous"></script>

ヒント

npm ci --ignore-scriptsを使うと、ほとんどのnpmマルウェアが実行に使うインストール時のスクリプトをブロックできます。そのうえで、本当にビルド手順が必要な一部のパッケージに限ってスクリプトを再び有効にしましょう。

WordPressなどのCMSを運用している場合も、同じルールがプラグインとテーマに当てはまります。CMSの侵害の多くはそこから始まるからです。コアとすべてのプラグインを最新に保ち、使っていないものは削除し、スキャナーがバージョンについて何を把握できるかをCMS検出ツールで確認しましょう。

レイヤー6:サーバーのハードニング(ポート、パッチ、シークレット、バックアップ)

その下のサーバーがSSHでパスワードログインを受け付けていたり、データベースを公開ポートで動かしていたり、構築以来一度もパッチを当てていなかったりすれば、アプリケーション層の対策は意味をなしません。サーバーのベストプラクティスは地味ですが、ランサムウェアが侵入してくるのはまさにここです。

提供していないポートはすべて閉じる

公開Webサーバーで開けておく必要があるのは、80番と443番のポート、そして自社のIP範囲からのSSHだけです。データベース(3306、5432、27017)、Redis(6379)、Elasticsearch(9200)、管理画面、メトリクスのエンドポイントは、localhostかプライベートネットワークにのみバインドしましょう。変更のたびにポートチェッカーで外部から確認してください。それが攻撃者から見える姿であり、クラウドのセキュリティグループは読み違えやすいからです。

bash
# Ubuntu: default-deny firewall, allow only web + SSH from your office range
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp
sudo ufw enable

# SSH: keys only, no root password login
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sudo systemctl reload ssh

パッチは定期的に適用し、シークレットはコードに入れない

OSのセキュリティ更新を自動適用するよう設定し、ランタイムとフレームワークのセキュリティメーリングリストを購読し、インターネットに公開しているものの重大なCVEは当日中に対応すべき仕事として扱いましょう。サービスは非特権ユーザーとして、コンテナ内またはsystemdのサンドボックス機能を使って実行し、侵害されたプロセスがマシン全体を読み取れないようにします。

シークレット(データベースのパスワード、APIキー、署名鍵)は、リポジトリにも、Dockerイメージのレイヤーにも、クライアント側のJavaScriptにも置いてはいけません。デプロイ時に注入される環境変数かシークレットマネージャーから読み込み、アクセス権を持つ人が退職したらローテーションし、gitleaksのようなツールでリポジトリの履歴をスキャンしてください。一度コミットされた鍵は、コミットを削除したあとでも永久に漏えいしたものと見なすべきだからです。

注意

NEXT_PUBLIC_、VITE_、REACT_APP_で始まるものはすべてブラウザ向けにバンドルされるため、定義上、公開情報です。サードパーティのAPIキーは、代わりに自前のサーバールートの背後に置きましょう。フロントエンドが実際に何を公開しているかを確認する方法は、公開APIエンドポイントのテストのガイドで紹介しています。

ランサムウェアに耐えるバックアップと、前段のCDN

3-2-1ルールに従いましょう。コピーを3つ、2種類の異なるメディアに、1つはオフサイトに保管し、少なくとも1つはイミュータブル(変更不可)またはオフラインにして、サーバーに到達したランサムウェアがバックアップまで暗号化できないようにします。バックアップは暗号化し、データベースとアップロードディレクトリを含め、そして何より、定期的にリストアをテストしてください。誰も復元を試したことのないバックアップは、計画ではなく願望にすぎません。インシデントが一時的な障害で済むか、会社の存続にかかわる事態になるかを決めるのは復旧時間です。

オリジンの前段にCDNまたはWAF(Cloudflare、Fastly、WAF付きのAWS CloudFront、またはホスティング会社の同等サービス)を置きましょう。大量のトラフィックによるDDoSを吸収し、一般的なインジェクションのペイロードや悪質なボットに対するマネージドルールを適用し、オリジンのIPを隠し、アプリケーションに手を加えずにレート制限や地域ルールを適用する場所になります。そのうえで、CDNのIP範囲からしかポート443に直接到達できないようにオリジンのファイアウォールを制限します。そうしないと、攻撃者は単にCDNを迂回するだけです。ホスティングチェッカーを使えば、CDNの背後にあるサイトの実際のオリジンが露出しているかどうかがわかります。

メール認証:SPF、DKIM、DMARC

Webセキュリティには、パスワードリセット、請求書、サポートの返信を運ぶメールも含まれます。SPF、DKIM、DMARCがなければ、誰でもbilling@yourdomain.comとしてメールを送れてしまいます。また2024年2月以降、GoogleとYahooは大量送信者にこの3つすべてを必須としています。必要なのは次の3つのDNS TXTレコードです。

dns
yourdomain.com.                    TXT  "v=spf1 include:_spf.google.com -all"
google._domainkey.yourdomain.com.  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
_dmarc.yourdomain.com.             TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; adkim=s; aspf=s"

メモ

DMARCのp=rejectはブランド保護の仕組みでもあります。フィッシングキャンペーンが差出人(From)欄にあなたのドメインそのものを入れるのを止められるのは、これだけです。集計レポート(rua=)を見れば、誰がなりすましを試みているかがわかります。

DMARCはまずp=noneで始めてレポートを収集し、正規の送信元(マーケティングプラットフォーム、ヘルプデスク、CRM)がすべてアライメントに合格したら、p=quarantine、さらにp=rejectへと移行します。MTA-STSとTLS-RPTのレコードを追加してメールサーバーへの暗号化された配送を強制し、メールを一切送信しないドメインにはnull MX(MX 0 .)を公開して、なりすましを完全に防ぎましょう。各レコードはSPFチェッカー、DKIMチェッカー、DMARCチェッカーで確認し、到達率が落ちたらIPブラックリストチェッカーで送信IPがブロックリストに載っていないか確認してください。

ログを取り、監視し、侵害に備える

OWASP 2025の10カテゴリーのうち2つは、ミスが起きたあとの対応に関するものです。Security Logging and Alerting Failures(セキュリティログとアラートの失敗、A09)と、新たに加わったMishandling of Exceptional Conditions(例外的な状況の不適切な処理、A10)です。典型的な侵害がいまだに何か月も発見されないのは、証拠がそもそも記録されていないか、記録されていても誰も見ていないからです。

  • セキュリティイベントを文脈付きで記録します:ログインの成功と失敗、MFAの変更、パスワードリセット、権限の変更、アクセス制御による拒否、入力検証の失敗、管理者の操作をすべて、タイムスタンプ、ユーザーID、送信元IP、ユーザーエージェントとともに記録します。

  • シークレットは決してログに残しません:パスワード、セッショントークン、カード番号全体、APIキーなどです。呼び出し箇所ごとではなく、ロギング層でマスクしましょう。

  • ログはサーバーの外に送ります:集中管理用のストア(CloudWatch、Loki、Elastic、SIEM)に少なくとも90日間保持し、root権限を得た攻撃者でも痕跡を消せないようにします。

  • 単発のイベントではなくパターンでアラートを出します:1分間に50回のログイン失敗、1つのセッションからの403の急増、営業時間外に作成された新しい管理者、自分で申請していないドメインの証明書の発行などです。

  • フェイルクローズにします:下流のチェック(認証サービス、レート制限、WAF)がエラーになったら、リクエストを拒否します。A10が存在するのは、本来ブロックするはずのチェックが例外を投げたときにアクセスを許可してしまうシステムが非常に多いからです。

  • 外部から監視します:稼働状況、証明書の有効期限、DNSの変更、ブロックリストへの掲載、ヘッダーの後退(リグレッション)を監視します。ドメインヘルスチェックは、DNS、SSL、メール認証、ヘッダーのチェックを1つのスコアにまとめており、デプロイのたびに再実行できます。

インシデント対応計画は必要になる前に書く

誰がオンコールを担当するか、すべての認証情報をどうローテーションするか、サイトをどう読み取り専用にするか、クリーンなバックアップはどこにあるか、どの規制当局や顧客に何時間以内に通知しなければならないか(GDPRでは72時間)を、事前に決めておきましょう。年に1回は机上演習(テーブルトップ演習)を実施してください。計画をリハーサルしたチームは数日で復旧し、そうでないチームは何週間も場当たり的な対応を続けることになります。

ヒント

/.well-known/security.txtにsecurity.txtファイル(RFC 9116)を置き、連絡先アドレスとPGP鍵を記載しましょう。あなたのサイトでバグを見つけた研究者はこれを使って連絡してきます。これがなければ、彼らはそのバグを公開してしまうかもしれません。

テストする:スキャナー、ペネトレーションテスト、継続的なチェック

ここまでの対策はすべて検証でき、その検証は形骸化しないよう可能な限り自動化すべきです。無料ですぐにできるものから、定期的に行う有料のものまで、現実的な段階は次のとおりです。

チェック項目ツール頻度
TLSの設定、チェーン、有効期限SSLチェッカー、SSL Labs、testssl.sh証明書やサーバーを変更するたび。有効期限は毎日
セキュリティヘッダーとCSPHTTPヘッダーチェッカー、MDN HTTP Observatoryデプロイのたび(CIに組み込む)
開いているポートポートチェッカー、nmapファイアウォールやクラウドの設定を変更するたび
DNS、DNSSEC、メール認証ドメインヘルスチェック、DMARCチェッカー毎月、およびDNSを編集するたび
放置されたサブドメインサブドメイン検索四半期ごと、およびサービスを廃止するたび
既知の脆弱性がある依存関係npm audit、Dependabot、Trivyビルドのたび
コードレベルの欠陥(SAST)Semgrep、CodeQLプルリクエストのたび
実行時のWeb脆弱性(DAST)OWASP ZAP、Nucleiステージング環境に対して毎週
認可とビジネスロジック手動のペネトレーションテストまたはバグバウンティプログラム年1回、および大きな機能の追加後

スキャナーはインジェクション、ヘッダー、古いコンポーネントの検出は得意ですが、認可やビジネスロジックは苦手です。お金や個人データを扱うものについては、少なくとも年1回は人による検査の予算を確保しましょう。修正は露出度の高い順に行います。インターネットから認証なしで悪用できるものが最優先です。

Webセキュリティ対策チェックリスト

これをプロジェクトのREADMEやチケット管理ツールにコピーしてください。各行は上で紹介した対策の1つで、完了か未完了かを判断できる形で書いています。最後の列がその証拠です。

層対策完了の基準
ドメインレジストラロックとレジストラアカウントのMFA、自動更新をオンWHOISにclientTransferProhibitedが表示され、有効期限が12か月以上先
DNSDNSSECで署名済み、自分のCAだけを記載したCAAレコードdig +dnssecがRRSIGを返し、親ゾーンにDSレコードがある
DNS放置されたCNAMEレコードやAレコードがないサブドメイン検索で見つかったすべてのホスト名が、自分で管理しているものに解決される
TLSTLS 1.2以上のみ、TLS 1.3を優先、完全なチェーンを配信SSLチェッカーでチェーンが完全、TLS 1.0/1.1が拒否される
TLSmax-age 1年、includeSubDomains、プリロード済みのHSTShstspreload.orgに登録されている
TLSACMEで更新を自動化、有効期限を監視certbot renew --dry-runが成功し、期限14日前のアラートを設定済み
ヘッダーCSP(nonceベース)、frame-ancestors、nosniff、Referrer-Policy、Permissions-PolicyHTTPヘッダーチェッカーの評価がA
認証Argon2idまたはbcryptでのハッシュ化、NISTのパスワードルール、流出リストとの照合ハッシュ1回に100〜250ミリ秒かかり、文字種の組み合わせルールがない
認証MFAを全員に提供し管理者には必須、パスキーに対応第2要素なしでは管理者としてログインできない
認証ログイン、リセット、MFAのエンドポイントにIPごと・アカウントごとのレート制限1分以内の6回目の試行で429が返る
セッションSecure・HttpOnly・SameSite付きの__Host- Cookie、ログイン時にIDをローテーションDevToolsでCookie属性が確認でき、ログイン後は古いセッションが無効になる
入力すべてのクエリをパラメータ化、コンテキストごとに出力をエンコード、アップロードは中身で検証コード検索で文字列連結のクエリが見つからず、XSSのペイロードがテキストとして表示される
アクセス制御デフォルト拒否のルーティング、オブジェクトごとの所有者チェック、厳格なCORS別のユーザーのIDで再送すると404または403が返る
依存関係ロックファイルをコミット、npm ci、CIで監査、アクションをSHAで固定、CDNスクリプトにSRI深刻度の高いCVEがあるとビルドが失敗する
サーバー公開は80番と443番のみ、SSHは鍵認証のみ、セキュリティ更新の自動適用、シークレットは環境変数かVaultに保管ポートチェッカーで22、3306、6379番がインターネットから閉じている
バックアップイミュータブルなコピー1つを含む3-2-1、リストアをテスト済み最後に成功したリストアテストが90日以内
メールSPFは-all、DKIM、DMARCはp=reject、MTA-STSDMARCチェッカーで合格し、集計レポートが届いている
監視認証・認可のイベントを集中的に記録、パターンでアラート、稼働状況・証明書・DNSを外部から監視テスト用のアラートが発報され、受信できた
対応インシデント対応計画を文書化、security.txtを公開、机上演習を実施直近12か月以内に計画を見直している

OWASP Top 10:2025との対応表

OWASP Top 10は、webアプリケーションのリスクを示すリストとして最も多く引用されており、2025年版では順位が入れ替わり、2つの新しいカテゴリーが加わりました。顧客や監査人、コンプライアンスのフレームワークからどう対処しているかを聞かれた場合は、このガイドの対策との対応表として次を使ってください。

OWASP Top 10:2025このガイドでの対策
A01 Broken Access Control(アクセス制御の不備)デフォルト拒否、オブジェクトごとのチェック、厳格なCORS、管理画面の保護
A02 Security Misconfiguration(セキュリティ設定のミス)セキュリティヘッダー、HSTS、ポートの閉鎖、バージョンヘッダーの削除、ディレクトリ一覧表示の無効化
A03 Software Supply Chain Failures(ソフトウェアサプライチェーンの失敗、新規)ロックファイル、監査、アクションの固定、SRI、SBOM、最小権限のCI
A04 Cryptographic Failures(暗号化の失敗)TLS 1.2以上と1.3、HSTS、Argon2id、シークレット管理、バックアップの暗号化
A05 Injection(インジェクション)パラメータ化クエリ、出力エンコード、CSP、アップロードの検証
A06 Insecure Design(安全でない設計)層ごとの脅威モデル、フェイルクローズのデフォルト設定、設計段階からのレート制限
A07 Authentication Failures(認証の失敗)NISTのパスワードルール、流出リストとの照合、MFAとパスキー、セッションのローテーション
A08 Software or Data Integrity Failures(ソフトウェアまたはデータの整合性の失敗)SRI、署名・固定された依存関係、安全なデシリアライズ
A09 Security Logging and Alerting Failures(セキュリティログとアラートの失敗)ログの集中管理、パターンによるアラート、外部からの監視
A10 Mishandling of Exceptional Conditions(例外的な状況の不適切な処理、新規)フェイルクローズ、統一されたエラー応答、ユーザーにスタックトレースを見せない

メモ

OWASPのASVS(Application Security Verification Standard)は、同じ内容を監査用の番号付きチェックリストにまとめたものです。公開サイトならレベル1が現実的な目標で、個人データや金融データを扱うものならレベル2を目指しましょう。

サイトのセキュリティヘッダーを数秒でチェック

DNS Robotの無料HTTPヘッダーチェッカーは、任意のURLを取得してセキュリティヘッダーをA〜Fで評価します。HSTS、Content-Security-Policy、X-Frame-Options、X-Content-Type-Options、Referrer-Policy、Permissions-Policyについて、実際に見つかった値と不足しているものを表示します。登録は不要です。

試す HTTPヘッダーチェッカー

Advertisement

Webセキュリティ対策のよくある質問

最新のTLSとHSTSでHTTPSを強制し、厳格なセキュリティヘッダー(特にContent Security Policy)を送信し、パスワードはArgon2idでハッシュ化したうえでMFAとレート制限で守り、パラメータ化クエリと出力エンコードを使い、すべてのオブジェクトについてサーバー側でアクセス制御を行い、依存関係をパッチ適用済みかつバージョン固定の状態に保ち、使っていないポートを閉じ、セキュリティイベントを集中的に記録することです。ただし最初に取り組むべきはドメインとDNSの保護です。ほかのすべては、自分のドメイン名を管理し続けられていることが前提だからです。

関連ツール

HTTP Headers CheckSSL Certificate CheckPort CheckerDomain Health CheckerDMARC CheckerSubdomain FinderPassword Strength Tester

関連記事

SSL証明書チェーンとは?仕組みを解説X-Frame-Options Explained: Fix “Refused to Connect” in an iframeHTTPエラー429 Too Many Requestsの原因と解決方法WHOIS検索ガイド:ドメイン所有者の調べ方とWHOIS情報の見方

目次

  • Webセキュリティ対策のベストプラクティスとは
  • Webセキュリティの全体像:守るべき6つの層
  • レイヤー1:ドメインとDNSを守る
  • レイヤー2:HTTPSとTLSを正しく設定する
  • レイヤー3:HTTPセキュリティヘッダー
  • レイヤー4:クレデンシャルスタッフィングに耐える認証
  • セッション、Cookie、CSRF
  • インジェクションとXSS:入力を検証し、出力をエンコードする
  • アクセス制御の不備(Broken Access Control):第1位のリスク
  • レイヤー5:依存関係とソフトウェアサプライチェーン
  • レイヤー6:サーバーのハードニング(ポート、パッチ、シークレット、バックアップ)
  • メール認証:SPF、DKIM、DMARC
  • ログを取り、監視し、侵害に備える
  • テストする:スキャナー、ペネトレーションテスト、継続的なチェック
  • Webセキュリティ対策チェックリスト
  • OWASP Top 10:2025との対応表
  • よくある質問