Limit body size for all requests
- Dominant language
- Go
- Stars
- 15.9k
- Forks
- 873
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 6
Description
We should limit the body size for all requests. It's currently possible to send big messages to /message or big yaml configs to /plugin/{id}/config.
I guess a good default could be something like 10MB, but it should be configurable.
Email Report
**Summary**
Gotify Server's plugin configuration update feature accepts
attacker-controlled YAML bodies at ` POST /plugin/{id}/config`.
Confirmed in ` v2.9.1`, when ` registration=true` and at least one
installed plugin supports ` Configurer`, an unauthenticated remote
attacker can self-register and submit an oversized configuration body to
trigger severe memory pressure and persistent configuration storage
growth.
**Details**
`UpdateConfig` verifies plugin ownership and ` Configurer` support, but
then immediately calls ` io.ReadAll(ctx.Request.Body)` with no size
limit. The full request body is buffered before YAML parsing or
plugin-side validation, so any reachable caller can force memory
allocation proportional to the submitted body size. After validation,
the handler stores the original bytes in ` conf.Config`, which turns the
same endpoint into a persistence amplifier rather than only a transient
parse-time sink.
``` elementToProof
conf, err := c.DB.GetPluginConfByID(id)
...
if aborted := supportOrAbort(ctx, instance, compat.Configurer); aborted { return }
newconfBytes, err := io.ReadAll(ctx.Request.Body)
...
if err := yaml.Unmarshal(newconfBytes, newConf); err != nil { ... }
if err := instance.ValidateAndSetConfig(newConf); err != nil { ... }
conf.Config = newconfBytes
```
When ` registration=true`, ` POST /user` is reachable under
` authentication.Optional()` and allows unauthenticated non-admin
account creation. User creation triggers ` fireUserAdded`, which
initializes per-user instances for all installed plugins, and
` RequireClient` accepts Basic Auth directly. The attacker can then call
` GET /plugin` to enumerate a plugin instance whose ` capabilities`
include ` configurer`, fetch the current YAML from
` GET /plugin/{id}/config`, and reuse that ` id` against
` POST /plugin/{id}/config`.
Because the handler stores raw YAML bytes rather than a normalized
representation, the attacker can append large YAML comment blocks to an
otherwise valid configuration. ` yaml.Unmarshal` ignores comments, but
the oversized original document is still persisted as
` PluginConf.Config`. I verified the unbounded body read and raw-byte
persistence in ` api/plugin.go`, and the self-registration plus Basic
Auth reachability in ` api/user.go`, ` router/router.go`,
` auth/authentication.go`, and ` plugin/manager.go`. I did not identify
a fixed version from the current materials.
**PoC**
1. Ensure the target runs Gotify Server ` v2.9.1` with
` registration=true` and at least one installed plugin whose
` /plugin` entry lists ` configurer` in ` capabilities`.
2. Create a normal account:
``` elementToProof
POST /user
Content-Type: application/json
{"name":"pocuser","pass":"PocPassw0rd!"}
```
3. Use Basic Auth for that account to call ` GET /plugin`, select an
entry whose ` capabilities` array contains ` configurer`, then fetch
its current YAML from ` GET /plugin/{id}/config`.
4. Append a large number of YAML comment lines to the returned document
and submit it back:
``` elementToProof
POST /plugin/{id}/config
Authorization: Basic
Content-Type: application/x-yaml
# padding 000001
# padding 000002
...
```
5. Observe high memory pressure during the request. If the YAML remains
otherwise valid, a subsequent ` GET /plugin/{id}/config` returns the
enlarged document, confirming that the oversized raw body was
persisted.
**Impact**
Observed impact is a conditional unauthenticated network denial of
service in deployments that allow self-registration and have at least
one installed ` Configurer` plugin: a remote attacker can consume
process memory with a single oversized request and can repeatedly
enlarge persisted plugin configuration data. Where registration is
disabled, the same bug remains reachable to a low-privilege
authenticated user. I did not confirm confidentiality or integrity
impact from current evidence.
Contributor guide
Assessment
This issue has not been assessed yet.