• 診断項目 No.107/127
  • フォーム・パスワード入力
  • 重大度 最重要(critical)

パスワードをPOSTで送信とは?GETで送ると漏れる理由・method="post"の設定方法・診断基準

パスワードをPOSTで送信するには、フォームにmethod="post"を明示します。method未指定は既定でGETになりURLに載る仕組み、HTTPSでも防げない漏えい経路、修正例と確認方法、診断基準を解説します。

結論(パスワードをPOSTで送信)

パスワードをPOSTで送信とは、パスワード欄を含むフォームに method="post" を指定し、入力値をURLではなくリクエスト本文で送ることです。HTMLのformは method を指定しないと既定でGETになり、パスワードが ?password=... の形でURLに付加されます。URLはブラウザ履歴やアクセスログに残るため、HTTPSでも防げない漏えい経路になります。OWASP ASVS 5.0.0 V14.2.1も機密データをURLに含めないよう求めています。

この記事のポイント

  • formのmethodは指定しないと既定値がGETで、入力値がURLのクエリ文字列に載ります。
  • URLはブラウザ履歴、ブックマーク、Webサーバー・プロキシのアクセスログ、画面共有などに残り、HTTPSでは防げません。
  • JavaScriptで送信していても、読み込み失敗時などはブラウザの既定動作で送信されるため、HTML側にmethod="post"を明示します。
  • サーバー側でも認証処理はPOSTだけを受け付け、過去にGETで送っていた場合はログの削除・マスキングとパスワード変更を行います。
  • セキュリティAIチェッカーは、method未指定・GETなどPOST以外のパスワードフォームが1件でもあれば×と判定します。
診断項目名パスワードをPOSTで送信
項目IDforms.password-submit-method
分類フォーム・パスワード入力
重大度・配点の重み最重要(critical) 重み 5
関連する標準・ガイドラインOWASP ASVS 5.0.0 V14.2.1(機密データはHTTPメッセージ本文またはヘッダーで送り、URLとクエリ文字列に含めない/L1)、RFC 9110 17.9(URIに機密情報を含めることの危険)、OWASP WSTG-CRYP-03(認証情報の送信経路の試験。旧WSTG-ATHN-01を統合)、CWE-598(Use of HTTP Request With Sensitive Query String)、IPA「安全なウェブサイトの作り方」6-(i)-a注記(機密データの送信にGETを使わない)

パスワードをPOSTで送信とは

パスワードをPOSTで送信とは、パスワード欄を含むHTMLフォームに method="post" を指定し、入力値をURLではなくリクエスト本文で送ることです。

HTMLフォームの送信方法(method 属性)には get と post があり、指定しない場合の既定値はGETです。GETで送信すると、入力値は ?user=...&password=... の形でURLに付加されます。URLはブラウザ履歴、ブックマーク、Webサーバーやプロキシのアクセスログ、解析ツール、画面共有など、さまざまな場所に残ります。

セキュリティAIチェッカーのこの項目は、パスワード欄を含むフォームが method="post" で送信される設定になっているかを確認します。パスワードがURLに載る設計は、暗号化(HTTPS)していても防げない漏えい経路を生むため、重大度criticalとしています。

GETとPOSTの違い

観点GETPOST
入力値の置き場所URLのクエリ文字列リクエスト本文
ブラウザ履歴・ブックマーク残る本文は残らない
サーバー・プロキシのアクセスログ標準設定で記録される本文は標準では記録されない
RefererヘッダーページURLとして送られ得る送信値はURLに含まれない
HTTPSでの暗号化通信路上は暗号化される同左

HTMLの仕様上の挙動

  • <form> の method は get/post/dialog のいずれかで、大文字小文字を区別せず、既定値はgetです(MDN)。
  • 送信ボタン(<button>、<input type="submit">、<input type="image">)の formmethod 属性は、フォームの method を上書きします。

なぜパスワードはGETではなくPOSTで送るべきなのか

HTTPSでもURLは端末やサーバーで平文になる

HTTPSはURLを含む通信内容を暗号化しますが、URLは送信元・送信先の端末やサーバーでは平文として扱われます。RFC 9110の17.9節は、URIは共有される前提のものであり、機密情報・個人を識別できる情報を含めるのは賢明でないと述べ、GETフォームのクエリに機密データが入る場合にはPOSTが好まれると説明しています。

OWASP ASVS 5.0.0 V14.2.1(L1)も、機密データはHTTPメッセージ本文またはヘッダーで送り、URLとクエリ文字列に含めないことを求めています。IPA「安全なウェブサイトの作り方」も、機密データの送信にGETを使わないよう注記しています。

パスワードの送信方法についてよくある誤解

  • 「JavaScriptでfetchしているからmethodは関係ない」:JavaScriptの読み込み失敗、実行前の素早い送信、エラー発生時などに、ブラウザは既定動作でフォームを送信します。method 未指定ならGETとなり、パスワードがURLに載ります。フレームワークで送信処理を置き換える場合でも、HTML側に method="post" と適切な action を明示しておくことが安全側の設計です。
  • 「HTTPSなのでGETでも安全」:上表のとおり、暗号化は通信路だけの保護です。端末の履歴やサーバーのログには残ります。
  • 「hiddenで送っている」:hidden項目もGETならURLに載ります。

送信先がHTTPS・同一オリジンかどうかは「認証情報の送信先をHTTPS・同一オリジンへ限定」、パスワード以外のトークンやAPIキーがURLに載っていないかは「機密情報をURL・GETフォームへ含めない」、セッションIDのURL埋め込みは「セッションIDをURLへ含めていない」で確認します。このページは「パスワードフォームの送信方法」に絞って解説します。

パスワードをPOSTで送信の診断基準(セキュリティAIチェッカー)

取得対象

外部診断強化モジュールが、トップページと、Ver2巡回で見つかった同一オリジンの追加ページ(ログイン・管理・アカウント系URLを優先)から最大2ページ、合計最大3ページのHTMLを解析します。追加のリクエストはGETのみで、フォームを実際に送信することはありません。

検出の手順

  1. HTMLから <form ...> と、その後に最初に現れる </form> までを1組として抽出し、その内部に type="password"(引用符なしも可)の <input> を含むものを「パスワードフォーム」とします。
  2. 各パスワードフォームの開始タグの method 属性を読み取り、値がない場合はHTML仕様の既定値どおりGETとみなします。
  3. 小文字化した値が post 以外のフォームを「POST以外」と数えます。

判定条件

判定条件得点への影響
○ 問題なし(pass)パスワードフォームがすべて method="post"重み5を満点で加算
△ 要確認(warn)使用しない—
× 要改善(fail)1件でも method 未指定、get、その他の値のパスワードフォームがある重み5に対して0点
対象外(na)パスワード欄を含むフォームがない分母・分子から除外

method 未指定を×とするのは、静的HTMLの状態でブラウザが実際にGETで送信する設定だからです。スクリプトによる送信の置き換えは、上記のとおりJavaScript失敗時の保護にならないため考慮しません。根拠欄には「n/m件がPOST以外」のように件数を表示します。

判定の範囲と限界

  • <form> 要素の外にあるパスワード欄(JavaScriptだけで送信するSPA)は、フォームとして数えないため本項目の対象外です。その場合、送信方法は開発者ツールのネットワーク表示で確認してください。
  • 閉じタグ </form> のないフォーム、属性値に < や > を含む <form>・<input> タグは抽出できず、対象外になります。
  • 送信ボタンの formmethod による上書きは評価していません。
  • JavaScriptで後から生成されるフォームは取得HTMLに含まれないため、判定できません。

自治体サイトでの判定例

状況判定
公式サイト内にログインフォームがない対象外
職員向けや事業者向けのログイン画面が <form action="/login.php">(method未指定)×
<form action="/login" method="post">○

パスワードをGETで送るとどんなリスクがあるか

  • アクセスログからの漏えい:Webサーバー、ロードバランサー、WAF、CDN、プロキシは標準でリクエストURLを記録します。ログを閲覧できる運用者、委託先、ログ転送先、ログの漏えい時に第三者が、IDとパスワードの組を入手できます。
  • 共用端末の履歴:図書館、公民館、窓口の共用PCで、ブラウザ履歴やアドレスバーの候補から他人のパスワードが見えます。
  • Refererによる外部送信:ログイン後のページURLにパスワードが残り、そのページから外部リソースを読み込むと、Referrer-Policyの設定次第では外部サイトへURLが送られます。現行の主要ブラウザの既定(strict-origin-when-cross-origin)ではクロスオリジンにはオリジンだけが送られますが、ポリシーを緩めている場合は全URLが渡ります。
  • 画面共有・スクリーンショット:オンライン会議や操作マニュアル作成時にアドレスバーが映り込みます。

いずれも暗号化では防げないため、被害は認証情報の漏えいとなりすましに直結します。

パスワードをPOSTで送信する設定方法(HTML・PHP・Next.js・Apache)

1. フォームに method="post" を明示する

<form action="/login" method="post" autocomplete="on">
  <label for="uid">利用者ID</label>
  <input id="uid" name="username" autocomplete="username" required>
  <label for="pw">パスワード</label>
  <input id="pw" type="password" name="password"
         autocomplete="current-password" required>
  <input type="hidden" name="csrf_token" value="(サーバー生成値)">
  <button type="submit">ログイン</button>
</form>

2. サーバー側でもGETを拒否する

HTMLを直しても、古いリンクやブックマークからGETで届く場合があります。認証処理はPOSTだけを受け付けます。

<?php
// login.php:POST以外は405
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Allow: POST');
    http_response_code(405);
    exit;
}
$user = $_POST['username'] ?? '';
$pass = $_POST['password'] ?? '';
// Next.js App Router:app/api/login/route.ts
export async function POST(request: Request) {
  const form = await request.formData();
  // 認証処理
  return new Response(null, { status: 204 });
}
// GETをexportしなければ、GETは405になります

3. 既存ログからパスワードを消す

過去にGETで送信していた場合、アクセスログにパスワードが記録されています。

  1. ログの保管場所(Webサーバー、WAF、CDN、ログ集約基盤、バックアップ)を洗い出します。
  2. password= などを含む行を確認し、保存規程に従って削除またはマスキングします。
  3. 影響を受けた利用者にパスワード変更を依頼します。

Apacheでは、該当パラメータを含むURLをログから除外する例として次の設定があります(暫定措置。根本対策はPOST化です)。

# SetEnvIfのRequest_URIはクエリ文字列を含まないため、式でQUERY_STRINGを判定
SetEnvIfExpr "%{QUERY_STRING} =~ /(^|&)(password|passwd|pwd)=/i" nolog_secret
CustomLog logs/access_log combined env=!nolog_secret

4. Referrer-Policyを設定する

URLに機密情報が残らないことが前提ですが、多層防御として Referrer-Policy: strict-origin-when-cross-origin(またはより厳しい same-origin、no-referrer)を設定します。

修正後の確認方法

# パスワードフォームのmethodを確認
curl -s https://www.example.lg.jp/login/ \
  | tr '\n' ' ' | grep -oiE '<form[^>]*>' 

# 認証エンドポイントがGETを拒否するか
curl -s -o /dev/null -w "%{http_code}\n" "https://www.example.lg.jp/login?username=a&password=b"

ブラウザの開発者ツール(ネットワーク)でログインし、リクエストのメソッドがPOSTで、URLに入力値がないことを確認します。修正後はセキュリティAIチェッカーで再診断し、この項目が○になることを確認します。

サイトを壊さないための注意点

  • method="post" にすると、サーバー側がGETの $_GET だけを読んでいる場合にログインできなくなります。HTMLとサーバー処理を同時に修正してください。
  • POSTにするとCSRF対策(トークン、SameSite属性)が必要になります。「POSTフォームのCSRF対策兆候」、「CookieのSameSite属性」もあわせて確認します。
  • POST後の画面をそのまま返すと、再読み込みで二重送信の確認ダイアログが出ます。ログイン成功後は303リダイレクトでGETの画面へ移す「PRGパターン」が一般的です。
  • パッケージ製品の場合、改修はベンダー対応となります。暫定策としてWAFやリバースプロキシで該当URLへのGETを拒否できるか検討してください。

よくある質問(FAQ)

formのmethodを指定しないとどうなりますか?

HTMLの仕様上、methodの既定値はGETです。そのため、method未指定のログインフォームはパスワードを ?user=...&password=... の形でURLに付けて送信します。

HTTPSならGETでパスワードを送っても安全ではありませんか?

安全ではありません。HTTPSは通信路上を暗号化しますが、URLは端末やサーバーでは平文として扱われ、ブラウザ履歴やアクセスログに残ります。暗号化は通信路だけの保護です。

JavaScriptのfetchで送信している場合もmethod="post"は必要ですか?

必要です。JavaScriptの読み込み失敗、実行前の素早い送信、エラー発生時などに、ブラウザは既定動作でフォームを送信します。method未指定ならGETになるため、HTML側にmethod="post"と適切なactionを明示しておくのが安全です。

過去にGETでパスワードを送っていた場合はどうすればよいですか?

Webサーバー、WAF、CDN、ログ集約基盤、バックアップなどのログ保管場所を洗い出し、password= などを含む行を保存規程に従って削除またはマスキングします。影響を受けた利用者にはパスワード変更を依頼します。

POSTに変えるときに注意することは?

サーバー側がGETの値だけを読んでいるとログインできなくなるため、HTMLとサーバー処理を同時に修正します。あわせてCSRF対策(トークン、SameSite属性)と、ログイン成功後に303リダイレクトでGET画面へ移すPRGパターンを検討します。

パスワードをPOSTで送信しているかは、セキュリティAIチェッカーでどう判定されますか?

パスワード欄を含むフォームがすべてmethod="post"なら「○ 問題なし」、method未指定・get・その他の値が1件でもあれば「× 要改善」(重み5に対して0点)、パスワード欄を含むフォームがなければ「対象外」です。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料