[critical] fix: [website] register state-changing routes as POST, not GET - #878
Open
elhoim wants to merge 1 commit into
Open
[critical] fix: [website] register state-changing routes as POST, not GET#878elhoim wants to merge 1 commit into
elhoim wants to merge 1 commit into
Conversation
change_status (home.py) and the history remove_node_session/remove_node_tree routes were registered with methods=["GET"]. Flask-WTF's CSRFProtect only validates unsafe HTTP methods (POST/PUT/PATCH/DELETE), so a GET route that mutates state bypasses CSRF protection entirely: a module can be silently disabled, or a history node deleted, just by getting a logged-in admin's browser to load an attacker-controlled <img src="/change_status?module_id=.."> or similar cross-site request, with no confirmation and no CSRF token check. Switched the three routes to methods=["POST"] so CSRFProtect covers them, and updated their JS/template callers (modules_config.html, history_session.html, history_view.js) to issue POST requests carrying the X-CSRFToken header, matching the pattern already used by /change_config. Verified with py_compile on the two changed Python files (flake8 excludes website/) and the full test suite against a locally started modules server on port 6769: 161 passed, 4 skipped, 5 subtests passed, matching baseline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dfYpyaSZd1nxSRLr8suj8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BLUF — Three state-changing website routes were GET and thus unprotected by CSRF; they are now POST.
/change_statusinwebsite/app/home.pyand/history/remove_node_sessionand/history/remove_node_treeinwebsite/app/history/history.pyare registered as GET, and Flask-WTF'sCSRFProtectonly validates unsafe methods, so they carry no CSRF protection at all.modules_config.html,history_session.htmlandhistory_view.jsto send theX-CSRFTokenheader.Three state-changing Flask routes were registered as
GET, but Flask-WTF'sCSRFProtectonly validates "unsafe" HTTP methods (POST/PUT/PATCH/DELETE) by default and leaves GET requests unchecked:Because these are simple
GETrequests, they are trivially triggerable cross-site — e.g. via<img src="https://target/change_status?module_id=...">embedded on an attacker-controlled page — bypassing CSRF protection entirely.Impact: A logged-in MISP-modules website admin who visits a malicious page (or one with attacker-controlled content) can have a module silently disabled/enabled, or a history session/tree node silently deleted, without any interaction or confirmation, and without the CSRF token check that protects the equivalent
POSTroutes (e.g./change_config).Fix: Changed the three routes to
methods=["POST"]so CSRFProtect actually covers them, matching the existing pattern used by/change_config. Updated the corresponding JS/template callers (modules_config.html,history_session.html,history_view.js) to issuePOSTrequests with theX-CSRFTokenheader instead of plainGETfetches.This is a behaviour change only in HTTP method (GET → POST) for these three endpoints; the request parameters, handler logic, and responses are unchanged, and all callers in the codebase were updated to match.
Verification
The
website/app has no automated test coverage in this repository. Verification was:py_compileclean onwebsite/app/home.pyandwebsite/app/history/history.py161 passed, 4 skipped, 5 subtests passed in 74.97s (0:01:14)— matches baselineFound during a review of the repository; other findings are being submitted as separate PRs.
🤖 Generated with Claude Code