fleet/server/contexts/token/token.go
jacobshandling 0eb8d432bf
Safely split incoming request headers, remove support for token presence in request body (#39427)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issues:**
- Prevents unbounded split length exploits similar to
https://nvd.nist.gov/vuln/detail/CVE-2025-30204
- Also removes parsing of request body for token, see
https://github.com/fleetdm/fleet/issues/39659
- @iansltx I figured since this PR updates the code blocks in question,
makes sense to [remove the body parsing
here](https://github.com/fleetdm/fleet/pull/39427/changes#diff-83b0d73af21e81cf2c5ed4448718d0760543699fe6e36e401372467befea29edL30-L33),
and clean up the [related dead
code](c1e3e89b5f/frontend/services/entities/installers.ts (L13))
in a follow-up

See https://fleetdm.slack.com/archives/C019WG4GH0A/p1770322925865209

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
2026-02-18 08:50:04 -08:00

67 lines
2 KiB
Go

// Package token enables setting and reading
// authentication token contexts
package token
import (
"context"
"net/http"
"strings"
)
type key int
const (
tokenKey key = 0
tokenDelimiter = " "
)
// Token is the concrete type that represents Fleet session tokens
type Token string
// FromHTTPRequest extracts an Authorization
// from an HTTP request if present.
func FromHTTPRequest(r *http.Request) Token {
authHeader := r.Header.Get("Authorization")
headerCouple, ok := parseHeaderLimited(authHeader)
if ok && strings.ToUpper(headerCouple[0]) == "BEARER" {
// If the Authorization header is present and properly formatted, return the token.
// Preserve case-insensitivity for "Bearer" prefix while case-sensitivity for the token value
return Token(headerCouple[1])
}
return ""
}
// NewContext returns a new context carrying the Authorization Bearer token.
func NewContext(ctx context.Context, token Token) context.Context {
if token == "" {
return ctx
}
return context.WithValue(ctx, tokenKey, token)
}
// FromContext extracts the Authorization Bearer token if present.
func FromContext(ctx context.Context) (Token, bool) {
token, ok := ctx.Value(tokenKey).(Token)
return token, ok
}
// parseHeaderLimited splits an authHeader into exactly two parts or nil, and returns an ok boolean indicating whether the passed in authHeader did have exactly 2 parts. If it did not, nil and false are returned.
func parseHeaderLimited(authHeader string) ([]string, bool) {
parts := make([]string, 2)
pre, remain, foundDelimiter := strings.Cut(authHeader, tokenDelimiter)
if !foundDelimiter {
return nil, false
}
parts[0] = pre
// Ensure the token value is the last part of the string and there are no more
// delimiters. This avoids an issue where malicious input could contain additional delimiters
// causing unnecessary overhead parsing tokens.
post, _, unexpectedDelimiterFound := strings.Cut(remain, tokenDelimiter)
if unexpectedDelimiterFound {
// more than 2 parts
return nil, false
}
parts[1] = post
return parts, true
}