Skip to content

fix(manager): POST送信経路によらずCSRFトークンを必ず付与する - #473

Merged
yama merged 1 commit into
mainfrom
fix/470-csrf-token-none
Sep 22, 2026
Merged

yama merged 1 commit into
mainfrom
fix/470-csrf-token-none

Conversation

@yama

@yama yama commented Sep 22, 2026

Copy link
Copy Markdown
Member

Fixes #470

What

管理画面から manager/index.php への POST に、送信経路によらず必ず CSRF トークンが付くようにしました。

  • CSRF 自動付与を header.inc.php のインラインから manager/media/script/csrf.js に切り出し、shell.js より先に読み込むように変更
  • リソース編集のプレビュー後、フォームの action / target を元の状態に戻すよう修正(jscripts.tpl)

Why

CSRF token validation failed | action: 5 | method: POST | token: none | valid_tokens_count: 1 が繰り返し報告されている(同系統で 3 回目。過去の個別修正は #267 / #357 / #448)。

原因:

  1. プレビュー(preview_mode=1)がフォームを target="prevWin" で送信した後、target="main" を設定して戻していた。シェル化で main フレームはなくなったため、以後の保存・テンプレート変更は shell.js を経由しないネイティブ送信になる(結果も別タブに開く)
  2. AJAX 遷移で差し込まれたフォームには hidden の csrf_token がない。旧実装は DOMContentLoaded 時にしか注入せず、form.submit() / jQuery(form).submit() は submit イベントを発火しないので送信時の補完も働かなかった
  3. 同じ理由で target 付きフォーム(バックアップ a=93 など)もトークンが欠落しうる。jQuery は ajaxSetup の beforeSend で付与していたが、呼び出し側が beforeSend を指定すると上書きされる

画面ごとの修正では別経路で再発するため、共通層で保証する形にしました。

How

csrf.js が以下を担います(トークンは従来どおり <meta name="csrf-token"> から取得)。

送信経路 付与方法
クリック / Enter / requestSubmit submit イベントのキャプチャ段階で hidden csrf_token を補う(shell.js が FormData を作る前)
form.submit() / jQuery(form).submit() HTMLFormElement.prototype.submit をラップして補う(target 付き・chromeless のネイティブ送信を含む)
XHR(jQuery AJAX 含む) 同一オリジンの POST 等に X-CSRF-Token ヘッダを付与。呼び出し側で付けている場合は重複させない
fetch 同上
  • 付与は同一オリジンのみ
  • 既存の hidden フィールドやヘッダがあれば上書き・重複しない(docmanager の X-CSRF-TOKEN と共存)
  • shell.js は form.submit を退避して使うため、csrf.js を先に読み込む

Test

ローカル Docker 環境で Playwright(headless Chromium)を使い、全 POST の csrf_token フィールド / X-CSRF-Token ヘッダの有無とレスポンスを記録して確認しました。

修正前(再現): SPA 遷移でリソース編集 → プレビュー → 保存 で 403。システムログは報告と完全に一致しました。

CSRF token validation failed | action: 5 | method: POST | token: none | valid_tokens_count: 1 | uri: /manager/index.php

修正後:

シナリオ 結果
A. 報告の手順(SPA 遷移 → プレビュー → 保存) a=5 がフィールド+ヘッダ付きで 302。プレビュー後の target は残らず、別タブも開かない
B. 共通層のみ(target="main" を強制して保存) a=5 がフィールド付きで 302
C. beforeSend を独自指定した jQuery POST / 生 fetch / Request の fetch / 生 XHR / 独自 X-CSRF-TOKEN 付き jQuery すべて 200。ヘッダの重複なし
E. バックアップ(target="fileDownloader") a=93 がフィールド付きで 200
F. TV 並べ替え(a=117 モーダル) フィールド+ヘッダ付きで 200
G. chromeless(chromeless=1)でプレビュー → 保存 a=5 がフィールド付きで 302
H. フルページ読み込みの編集画面で保存 a=5 がヘッダ付きで 302
  • 修正後の CSRF 失敗ログ: 0 件
  • php -l manager/actions/header.inc.php / node --check manager/media/script/csrf.js: OK

互換性・影響

  • 破壊的変更なし。運用手順の変更なし
  • header.inc.php を経由せず完全な HTML を出力するモジュールは対象外。従来どおりフォームに csrfTokenField() を出力してください
  • window.fetch と XMLHttpRequest.prototype をラップします。同一オリジンの非 GET リクエストにヘッダを 1 つ追加するだけです

🤖 Generated with Claude Code

https://claude.ai/code/session_01VqS5g34F8nY9QhMKxX6VGV

リソース編集のプレビュー後にフォームへ target="main" が残り、以後の保存が
シェルを経由しないネイティブ送信になっていた。AJAX遷移で差し込まれた
フォームには hidden の csrf_token がなく、form.submit() は submit イベントを
発火しないため、token: none の 403 になっていた。

- CSRF自動付与を media/script/csrf.js へ切り出し、shell.js より先に読み込む
- submit イベントと form.submit() の両方で送信直前に csrf_token を補う
- XHR(jQuery含む)と fetch の同一オリジンPOSTに X-CSRF-Token を付与する
  (ajaxSetup の beforeSend は呼び出し側の指定で上書きされるため廃止)
- プレビュー送信後は action / target を元の状態へ戻す

Fixes #470

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VqS5g34F8nY9QhMKxX6VGV
@yama yama added security セキュリティ関連 バグ修正 labels Sep 22, 2026
Copilot AI lite review requested due to automatic review settings September 22, 2026 21:57
@yama yama added security セキュリティ関連 バグ修正 labels Sep 22, 2026
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 51 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: modxcms-jp/evolution-jp/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 6a757985-06f0-4b5d-99d3-c2e36b2493e9

📥 Commits

Reviewing files that changed from the base of the PR and between 40fc7aa and 2d77cac.

📒 Files selected for processing (4)
  • assets/docs/troubleshooting/solved-issues.md
  • manager/actions/header.inc.php
  • manager/media/script/csrf.js
  • manager/media/style/common/jscripts.tpl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Fetch requests may overwrite an existing request token when init.headers is provided.

Review effort: Lite
Findings: None

What changed in this PR

This pull request centralizes CSRF protection for manager POST requests and restores form state after previews.

Changes:

  • Adds CSRF handling for forms, XHR, and fetch.
  • Loads csrf.js before shell.js.
  • Restores preview form action and target.
  • Documents the resolved issue.
File Changes
manager/​media/​style/​common/​jscripts.tpl Restores form state after preview.
manager/​media/​script/​csrf.js Adds CSRF tokens to submission paths; fetch header merging needs correction.
manager/​actions/​header.inc.php Loads csrf.js before shell.js.
assets/​docs/​troubleshooting/​solved-issues.md Records the Issue #470 resolution.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@yama
yama merged commit 742bb57 into main Sep 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security セキュリティ関連 バグ修正

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(manager): 管理画面の POST で CSRF トークンが送信されず 403 になる(token: none)

2 participants