gotify / gotify/server

Limit body size for all requests

Open
#950 5 comments 0 reactions 0 assignees View on GitHub
a:bug
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.