mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-06 23:35:08 +00:00
fix(web): refuse cross-site state-changing requests (Origin/Referer check)
The web interface had no CSRF protection, on the reasoning that anyone who can forge a request on the LAN can also send it directly. That misses the browser as a confused deputy: any website a LAN user opens can make their browser POST a plain HTML form to http://<pi>:5000. CORS does not stop that request, only hides its answer, and /api/v3/system/action accepted form bodies, so a hostile page could reboot or power off the Pi, pull code, or reach any other mutating route. - web_interface/origin_guard.py: an app-wide before_request hook refuses POST/PUT/PATCH/DELETE whose Origin (or, without one, Referer) is not the host the request was addressed to, and Origin "null", with 403 CROSS_SITE_REQUEST. Requests with neither header (curl, Home Assistant, the MQTT bridge) are not from a browser and pass. Host and port are compared, not the scheme, so a TLS proxy that passes Host through works; X-Forwarded-Host is not trusted (no ProxyFix). - /api/v3/system/action refuses a non-JSON body (415) unless HX-Request is set; every caller in the interface already sends JSON. - app.py comment states the real threat model; SECURITY.md, REST_API_REFERENCE.md, WEB_INTERFACE_GUIDE.md and CHANGELOG updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,28 @@ accepts both, but the store flags the old spelling as deprecated
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Security
|
||||
|
||||
- The web interface refuses state-changing requests (`POST`, `PUT`, `PATCH`,
|
||||
`DELETE`) sent by another website's page. Any site a LAN user visited could
|
||||
make their browser submit a plain HTML form to `http://<pi>:5000` -- CORS
|
||||
does not stop such a request, only hides its answer -- and
|
||||
`/api/v3/system/action` accepted form bodies, so that page could reboot or
|
||||
power off the Pi, pull code, or reach any other mutating route. A request
|
||||
whose `Origin` (or, without one, `Referer`) is not the host it was sent to,
|
||||
or is `null`, now gets 403 `CROSS_SITE_REQUEST`
|
||||
(`web_interface/origin_guard.py`). `/api/v3/system/action` also refuses a
|
||||
form-encoded or `text/plain` body (415) unless it carries HTMX's
|
||||
`HX-Request` header; every caller in the interface already sends JSON.
|
||||
- **Behaviour change for API scripts:** clients that send no `Origin` or
|
||||
`Referer` -- curl, Python `requests`, Home Assistant, the MQTT bridge --
|
||||
are unaffected. A browser page served from a *different* origin (a
|
||||
dashboard or userscript on another host) can no longer call the mutating
|
||||
API; call it server-side instead. Anyone posting a form body to
|
||||
`system/action` must switch to JSON. Behind a reverse proxy, forward the
|
||||
original `Host` (`proxy_set_header Host $host;`); `X-Forwarded-Host` is
|
||||
not trusted.
|
||||
|
||||
## 3.7.0
|
||||
|
||||
Sports consolidation stage 3 (#672). No behaviour change: nothing in core
|
||||
|
||||
Reference in New Issue
Block a user