Skip to content

fix: bound unauthenticated stanza parsing#723

Closed
andydixon wants to merge 1 commit into
FiloSottile:mainfrom
andydixon:memexhaustion
Closed

fix: bound unauthenticated stanza parsing#723
andydixon wants to merge 1 commit into
FiloSottile:mainfrom
andydixon:memexhaustion

Conversation

@andydixon

Copy link
Copy Markdown

Age file headers are necessarily parsed before the header MAC is authenticated, because the recipient stanza is needed to recover the file key. Previously, stanza lines were read with unbounded ReadBytes calls, stanza bodies were appended without a total size limit, and Parse accumulated recipients until a footer appeared. An attacker who can supply an age file, detached header, or malicious plugin output could force the process to allocate large memory before cryptographic authentication fails.

The fixed code now defines explicit limits in internal/format/format.go:111: a 16 MiB maximum header, 64 KiB maximum line, and 16 MiB maximum stanza body. ReadStanza now uses bounded line reads at internal/format/format.go:181 and internal/format/format.go:201, rejects oversized bodies at internal/format/format.go:216, and Parse rejects oversized headers at internal/format/format.go:280 and internal/format/format.go:301.

Regression tests were added at internal/format/format_test.go:48, internal/format/format_test.go:55, and internal/format/format_test.go:70.

An attack schenario could look like this: A service uses this library to decrypt files uploaded by users. A low-privilege user submits a file beginning with a valid age intro and then supplies many megabytes or gigabytes of recipient stanza text before the unauthenticated footer. Before this fix, the process could grow memory while parsing the header, potentially causing container OOM, request failure, or service degradation.
Impact

Local or service-level denial of service. Confidentiality and cryptographic integrity are not directly bypassed, but availability can be affected wherever untrusted age files or untrusted plugins are processed.

Comment thread age.go
Comment on lines -156 to +158
rand.Read(fileKey)
if _, err := rand.Read(fileKey); err != nil {
return nil, fmt.Errorf("failed to generate file key: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto/rand.Read never returns an error (see https://pkg.go.dev/crypto/rand#Read). This change is not necessary.

Comment thread age.go
Comment on lines -167 to +171
rand.Read(nonce)
if _, err := rand.Read(nonce); err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto/rand.Read never returns an error (see https://pkg.go.dev/crypto/rand#Read). This change is not necessary.

Comment thread age.go
Comment on lines -182 to +188
rand.Read(fileKey)
if _, err := rand.Read(fileKey); err != nil {
return nil, fmt.Errorf("failed to generate file key: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto/rand.Read never returns an error (see https://pkg.go.dev/crypto/rand#Read). This change is not necessary.

Comment thread age.go
Comment on lines -194 to +202
rand.Read(nonce)
if _, err := rand.Read(nonce); err != nil {
return nil, fmt.Errorf("failed to generate nonce: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto/rand.Read never returns an error (see https://pkg.go.dev/crypto/rand#Read). This change is not necessary.

Comment thread plugin/client.go
Comment on lines -503 to +505
rand.Read(s.Body)
if _, err := rand.Read(s.Body); err != nil {
return false, fmt.Errorf("failed to generate grease body: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crypto/rand.Read never returns an error (see https://pkg.go.dev/crypto/rand#Read). This change is not necessary.

@andydixon andydixon closed this by deleting the head repository Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants