add cookie sanitization according to RFC 6265

pull/3379/head
Anshul Sinha 2025-03-29 21:16:16 +05:30
parent e90fe8afbc
commit 5ecb9af8e5
2 changed files with 55 additions and 1 deletions

44
ctx.go
View File

@ -23,6 +23,7 @@ import (
"text/template"
"time"
"github.com/gofiber/fiber/v3/log"
"github.com/gofiber/utils/v2"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
@ -450,7 +451,48 @@ func (c *DefaultCtx) Cookie(cookie *Cookie) {
// The returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func (c *DefaultCtx) Cookies(key string, defaultValue ...string) string {
return defaultString(c.app.getString(c.fasthttp.Request.Header.Cookie(key)), defaultValue)
value := c.app.getString(c.fasthttp.Request.Header.Cookie(key))
return defaultString(c.sanitizeCookieValue(value), defaultValue)
}
// sanitizeCookieValue sanitizes a cookie value according to RFC 6265.
// It removes invalid characters from the cookie value, similar to how
// Go's standard library handles cookie values.
func (c *DefaultCtx) sanitizeCookieValue(v string) string {
var result strings.Builder
result.Grow(len(v))
invalidChars := make(map[byte]struct{})
for i := 0; i < len(v); i++ {
b := v[i]
if c.validCookieValueByte(b) {
result.WriteByte(b)
} else {
invalidChars[b] = struct{}{}
}
}
if len(invalidChars) > 0 {
var chars []string
for b := range invalidChars {
chars = append(chars, fmt.Sprintf("'%c'", b))
}
log.Warn("invalid byte(s) %s in Cookie.Value; dropping invalid bytes",
strings.Join(chars, ", "))
return result.String()
}
return v
}
// validCookieValueByte reports whether b is a valid byte in a cookie value.
// Per RFC 6265 section 4.1.1, cookie values must be ASCII
// and may not contain control characters, whitespace, double quotes,
// commas, semicolons, or backslashes.
func (c *DefaultCtx) validCookieValueByte(b byte) bool {
return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
// Note: commas are deliberately allowed
// See https://golang.org/issue/7243 for the discussion.
}
// Download transfers the file from path as an attachment.

View File

@ -1035,6 +1035,18 @@ func Test_Ctx_Cookies(t *testing.T) {
c.Request().Header.Set("Cookie", "john=doe")
require.Equal(t, "doe", c.Req().Cookies("john"))
require.Equal(t, "default", c.Req().Cookies("unknown", "default"))
c.Request().Header.Set("Cookie", "special=value,with,commas") // commas are allowed
require.Equal(t, "value,with,commas", c.Req().Cookies("special"))
c.Request().Header.Set("Cookie", "quotes=value\"with\"quotes")
require.Equal(t, "valuewithquotes", c.Req().Cookies("quotes"))
c.Request().Header.Set("Cookie", "semicolons=value;with;semicolons")
require.Equal(t, "valuewithsemicolons", c.Req().Cookies("semicolons"))
c.Request().Header.Set("Cookie", "backslash=value\\with\\backslash")
require.Equal(t, "valuewithbackslash", c.Req().Cookies("backslash"))
}
// go test -run Test_Ctx_Format