Repository navigation
Harden HTTP endpoints against cross-site requests and XSS - #170
Conversation
Any web page opened in the home network could send requests to the proxy: a plain <img src=".../remove_keys?role=owner"> deleted the key, and with an Owner key a simple form POST could unlock the car. - Wrap the router in http.CrossOriginProtection. Cross-site browser POSTs are rejected; evcc, curl and Home Assistant send neither Origin nor Sec-Fetch-Site and pass unchanged. - /gen_keys and /remove_keys accept POST only (dashboard forms updated). - Render dashboard and log viewer with html/template so messages carrying user input (e.g. the VIN) are escaped. - Log instead of exiting when writing a response fails. - Make MessageStack safe for concurrent handlers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ted car Cross-site POSTs to door_unlock (Origin or Sec-Fetch-Site) are rejected before reaching the car, while clients without those headers still unlock it. GET /gen_keys and /remove_keys are refused, and form input from send_key renders escaped on the dashboard. tesla_test.go follows the new SetupRoutes(fs.FS, fs.FS) http.Handler signature. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🟡 Merge after the to-dosThe change is sound and evcc/Home Assistant/curl calls are not affected. Two small things remain: a possible 403 behind a reverse proxy, and a breaking change that should be in the release notes. Impact on existing users: Anyone who calls To-do for the author
Test before merging: Update a real install, open the dashboard in a browser, generate and remove a key, and send a command from evcc or curl. Technical details
|
Browsers send Sec-Fetch-Site only to secure origins. Over plain HTTP the cross-origin check falls back to comparing Origin with Host, so a reverse proxy that rewrites Host (nginx default) made dashboard buttons return 403. The new trustedOrigins env var (comma-separated scheme://host[:port]) exempts such origins; invalid entries are logged and skipped. Over HTTPS nothing needs to be set. README troubleshooting now explains the 403 and that /gen_keys and /remove_keys accept POST only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🟡 Merge after the to-dosGood hardening. evcc, curl and Home Assistant send no Impact on existing users: To-do for the author
Test before merging: On a Pi, open the dashboard directly and through your reverse proxy (HTTP), then click Generate and Remove. Also check that evcc still controls the car. Technical details
|
…rigins The Origin header is compared verbatim, so "http://nas.local:80" never matched and "http://nas.local/" was rejected as having a path. Entries are now lowercased and stripped of a trailing slash and default ports before they are trusted. The docs now also say that custom web pages or browser tools calling the API from another origin get 403 unless listed in trustedOrigins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why
The proxy has no authentication and listens on all interfaces, so it trusts the LAN. The practical risk is the browser: any web page opened by someone in the home network can send requests to
http://<proxy>:8080.<img src="http://proxy:8080/remove_keys?role=owner">deleted the key. The car then has to be re-paired with the NFC card./api/1/vehicles/<VIN>/command/door_unlockunlocks the car. A form POST needs no CORS preflight, and the VIN is visible through the windshield.text/template, so messages carrying user input (for example the VIN fromsend_key) were rendered unescaped. This also prepares for the richer dashboard requested in Feature Request: Enhanced Web Dashboard with Live Vehicle Status #168.What
http.CrossOriginProtection. Browser POSTs from other sites (Sec-Fetch-Site: cross-site, or anOriginthat doesn't matchHost) are rejected with 403. evcc, curl and Home Assistant send neither header and pass unchanged./gen_keysand/remove_keysaccept POST only. The dashboard forms are updated.html/template.commonDeferlogs a failed response write instead of callinglogging.Fatal, so a client that disconnects can no longer stop the process.MessageStackis safe for concurrent handlers.Breaking change
GET /gen_keysandGET /remove_keysnow return 405. They were never documented and are only used by the dashboard forms, which now send POST.POST /remove_keys?role=…still works for scripts.Release notes
Reverse proxies
Browsers send
Sec-Fetch-Siteonly to secure origins (HTTPS, localhost). Over HTTPS through a reverse proxy the browser reportssame-origin, andHostis not checked. Over plain HTTP,CrossOriginProtectionfalls back to comparingOriginwithHost. A proxy that rewritesHost(nginx default) then gets 403 on dashboard buttons.trustedOrigins=http://nas.local:8080exempts that origin (AddTrustedOrigin). Entries are normalized first (lowercase, no trailing slash, no default port), because browsers send theOriginin that form and it is compared verbatim. Invalid entries are logged and skipped. Documented indocs/environment_variables.mdand the README troubleshooting section.Not covered (follow-ups)
BleControlInstanceand BLE connections that bypass the command queue (separate PR).Testing
go vet ./...,go test -race ./...door_unlock), GET on key routes → 405, escaped dashboard messages.🤖 Generated with Claude Code