Cap request body size and add baseline security headers
Part of a security-hardening pass (see wiki for the full backlog). decodeJSON had no size limit at all, so every JSON endpoint -- including the two unauthenticated ones (bootstrap, the Alertmanager webhook) -- would buffer an attacker-supplied body of unbounded size before it was even validated. decodeJSON now wraps the body in http.MaxBytesReader at a 1 MiB default; the webhook gets its own 8 MiB cap via decodeJSONLimit, since a real Alertmanager batch can be bigger than an ordinary API body. Also adds a securityHeaders middleware, applied globally: nosniff on every response (previously only the static site got it), and HSTS (180-day max-age, conservative on purpose) whenever cookieSecure's signal says the browser is on HTTPS. Checked the chart/gateway config first -- neither sets HSTS anywhere, so this was a real gap. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -72,8 +72,25 @@ func respond(w http.ResponseWriter, status int, v any) {
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
// maxBodyBytes caps an ordinary JSON request body. 1 MiB is far more than any
|
||||
// endpoint below needs — it exists so an unauthenticated caller (signup,
|
||||
// login, bootstrap) can't make the server buffer an arbitrarily large body
|
||||
// before the request is even validated.
|
||||
const maxBodyBytes = 1 << 20
|
||||
|
||||
func decodeJSON(r *http.Request, v any) error {
|
||||
return decodeJSONLimit(r, v, maxBodyBytes)
|
||||
}
|
||||
|
||||
// decodeJSONLimit is decodeJSON with an explicit cap, for the one endpoint
|
||||
// (the Alertmanager webhook, see maxWebhookBodyBytes) whose real payloads can
|
||||
// legitimately be larger than maxBodyBytes.
|
||||
func decodeJSONLimit(r *http.Request, v any, limit int64) error {
|
||||
defer r.Body.Close()
|
||||
// w is nil: there is no ResponseWriter here to disable keep-alive with,
|
||||
// which net/http documents as fine — the limit is still enforced, the
|
||||
// connection just isn't closed early on a request that blows past it.
|
||||
r.Body = http.MaxBytesReader(nil, r.Body, limit)
|
||||
return json.NewDecoder(r.Body).Decode(v)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user