HTML hidden入力:送信値・JavaScriptでの変更と安全性の限界

input type="hidden"は、入力ボックスを表示せずにフォームに名前と値を含めます。nameは送信されたフィールド名で、valueはその値です。ユーザーはこれを確認または変更できるため、パスワード、権限、価格、その他の機密的な決定を保護するためには使用しないでください。

隠し入力はいつ使用すべきですか?

編集フォームとともに対象の投稿IDを送信するため、または検索条件を次のリクエストに引き継ぐために使用します。ユーザーが入力する必要のない値を渡しますが、それらが有効かどうかを判断する責任はサーバー側にあります。

投稿IDを受け取ったからといって、その投稿を編集する許可があるわけではありません。サーバーは認証済みセッションを通じてユーザーを識別し、投稿へのアクセス権を確認する必要があります。クライアントが隠しフィールドで送信しただけのアカウント情報やクライアントIP情報を信用してはいけません。

タイプ 画面上の挙動 フォームデータ
type="hidden" 入力用のUIはなく、フォーカスも受けません。 名前が付けられていて無効にされていなければ含まれます。
disabled コントロールを無効にします。 除外されます。
通常の入力はCSSの display: none で非表示にされます。 CSSは通常の入力をレイアウトから隠します。 CSSだけで隠しても、送信からは除外されません。

id はスクリプトで要素を見つけることはできますが、送信される名前を置き換えることはありません。名前なしの隠し入力は、通常のフォーム送信では名前/値のエントリとして含まれません。required が付いていても、隠し入力はブラウザの制約バリデーションには参加しません。

見えない値が送信されることを確認してください

この完全なコードをhidden.htmlとして保存し、ブラウザで開いてください。サーバーは必要ありません。送信すると、ネットワークリクエストを行うのではなく、FormDataで作成された名前/値エントリーが表示されます。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>hidden値の確認</title>
  <style>
    body { margin: 24px; font-family: sans-serif; }
    label { display: block; margin: 12px 0; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <h1>色を選択</h1>
  <form id="demo">
    <label><input type="checkbox" name="color" value="blue" checked> 青</label>
    <label><input type="checkbox" name="color" value="red"> 赤</label>
    <input type="hidden" id="size" name="size" value="big">
    <input type="hidden" name="excluded" value="not-sent" disabled>
    <button type="submit">送信値を確認</button>
    <button type="button" id="change">hidden値をsmallに変更</button>
    <button type="button" id="restore">hidden値をbigに戻す</button>
  </form>
  <p id="status" aria-live="polite">初期のhidden値はbigです。</p>
  <pre id="result" aria-live="polite"></pre>
  <script>
    const form = document.querySelector('#demo');
    const size = document.querySelector('#size');
    const result = document.querySelector('#result');
    const status = document.querySelector('#status');
    form.addEventListener('submit', (event) => {
      event.preventDefault();
      result.textContent = JSON.stringify([...new FormData(form).entries()], null, 2);
    });
    function setSize(value) {
      size.value = value;
      status.textContent = `現在のhidden値:${value}。もう一度「送信値を確認」を押してください。`;
    }
    document.querySelector('#change').addEventListener('click', () => setSize('small'));
    document.querySelector('#restore').addEventListener('click', () => setSize('big'));
  </script>
</body>
</html>
  1. 最初に送信する: color=blueとsize=bigが表示されます。サイズボックスは表示されませんが、その値は含まれます。excludedは無効になっているため省略されます。
  2. 赤を選択して再度送信すると、繰り返されたカラー名の下に青と赤が表示されます。両方のチェックボックスをクリアすると、サイズだけが残ります。
  3. 隠し値をsmallに変更するボタンを押して再度送信すると、size=smallが表示されます。隠し値はJavaScriptを通じても変更できます。
  4. 復元ボタンでbigに戻して再度送信します。変更ボタンは動作を観察するための練習用であり、すべてのサービスに必要な機能ではありません。

フォームを実際のサーバーに送信します

実際のサービスでは、アクションを既存の処理ルートに設定し、method="post"のようなサポートされているメソッドを選択してください。例の送信ハンドラーはリクエストをブロックします; それを削除するか、データを送信するサーバーリクエストコードに置き換えてください。サーバーのフォーム処理機能は、通常のテキストフィールドと同様に隠しフィールドも受け取ります。

名前が同じ複数のチェックボックスを受け取る場合は、サーバーフレームワークの配列および複数値ルールを確認してください。一部のパーサーは最後に繰り返された値のみを読み取ります。JavaScriptの出力は繰り返された名前を保持するので、その違いを確認できます。

隠すこととセキュリティを混同しないでください

隠し値はHTMLソース、開発者ツール、ネットワークリクエストで確認可能です。HTTPSは値を送信するユーザーから隠すものではありません。製品IDを受け取った場合は、サーバーで製品と価格を確認し、許可されたサイズの値を信頼できるサーバー情報と照合してください。

隠しフィールドはCSRF(Cross-Site Request Forgery、サイト間リクエスト偽造)トークンを保持できます。保護は、サーバー側での適切なトークン発行と検証から来ます。隠し入力に任意の固定文字列を入れても、それだけでは偽造リクエストを防ぐことはできません。

MDN 隠し入力リファレンス は、その動作とバリデーション対象外であることを説明しています。