Skip to content

Commit 27695fa

Browse files
committed
http/httpguts: reject leading and trailing spaces in field values
RFC9113 is extremely clear about this: > A field value MUST NOT start or end with an ASCII whitespace character > (ASCII SP or HTAB, 0x20 or 0x09). RFC9114 defers to RFC9110, which in turn provides a grammer that does not permit leading and/or trailing whitespace either.
1 parent 3e7a445 commit 27695fa

File tree

2 files changed

+63
-39
lines changed

2 files changed

+63
-39
lines changed

http/httpguts/httplex.go

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -262,48 +262,33 @@ var validHostByte = [256]bool{
262262
'~': true, // unreserved
263263
}
264264

265+
// validFieldValueChar reports whether v is an RFC9110 field-vchar, SP, or HTAB.
266+
func validFieldValueChar(v uint8) bool {
267+
if v < ' ' {
268+
return v == '\t'
269+
} else {
270+
return v != 0x7F
271+
}
272+
}
273+
265274
// ValidHeaderFieldValue reports whether v is a valid "field-value" according to
266-
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 :
267-
//
268-
// message-header = field-name ":" [ field-value ]
269-
// field-value = *( field-content | LWS )
270-
// field-content = <the OCTETs making up the field-value
271-
// and consisting of either *TEXT or combinations
272-
// of token, separators, and quoted-string>
273-
//
274-
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 :
275-
//
276-
// TEXT = <any OCTET except CTLs,
277-
// but including LWS>
278-
// LWS = [CRLF] 1*( SP | HT )
279-
// CTL = <any US-ASCII control character
280-
// (octets 0 - 31) and DEL (127)>
275+
// <https://rfc-editor.org/rfc/rfc9110#name-field-values>:
281276
//
282-
// RFC 7230 says:
283-
//
284-
// field-value = *( field-content / obs-fold )
285-
// obj-fold = N/A to http2, and deprecated
286-
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
287-
// field-vchar = VCHAR / obs-text
288-
// obs-text = %x80-FF
289-
// VCHAR = "any visible [USASCII] character"
290-
//
291-
// http2 further says: "Similarly, HTTP/2 allows header field values
292-
// that are not valid. While most of the values that can be encoded
293-
// will not alter header field parsing, carriage return (CR, ASCII
294-
// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII
295-
// 0x0) might be exploited by an attacker if they are translated
296-
// verbatim. Any request or response that contains a character not
297-
// permitted in a header field value MUST be treated as malformed
298-
// (Section 8.1.2.6). Valid characters are defined by the
299-
// field-content ABNF rule in Section 3.2 of [RFC7230]."
300-
//
301-
// This function does not (yet?) properly handle the rejection of
302-
// strings that begin or end with SP or HTAB.
277+
// field-value = *field-content
278+
// field-content = field-vchar
279+
// [ 1*( SP / HTAB / field-vchar ) field-vchar ]
280+
// field-vchar = VCHAR / obs-text
281+
// obs-text = %x80-FF
303282
func ValidHeaderFieldValue(v string) bool {
304-
for i := 0; i < len(v); i++ {
305-
b := v[i]
306-
if isCTL(b) && !isLWS(b) {
283+
l := len(v)
284+
if l == 0 {
285+
return true
286+
}
287+
if v[0] <= ' ' || v[l - 1] <= ' ' {
288+
return false
289+
}
290+
for i := 0; i < l; i++ {
291+
if !validFieldValueChar(v[i]) {
307292
return false
308293
}
309294
}

http/httpguts/httplex_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,45 @@ func TestValidHeaderFieldName(t *testing.T) {
129129
}
130130
}
131131

132+
func TestValidHeaderFieldValue(t *testing.T) {
133+
tests := []struct {
134+
in string
135+
want bool
136+
}{
137+
{"", true},
138+
{" junk", false},
139+
{"\tjunk", false},
140+
{"junk\t", false},
141+
{"junk ", false},
142+
{" ", false},
143+
{"\t", false},
144+
}
145+
for i := byte(0); true; i++ {
146+
bad := i < ' '
147+
if i == 0x7f {
148+
bad = true
149+
}
150+
if i == '\t' {
151+
bad = false
152+
}
153+
tests = append(tests,
154+
struct {
155+
in string
156+
want bool
157+
}{string([]byte{'a', i, 'b'}), !bad})
158+
if i == 255 {
159+
break
160+
}
161+
}
162+
163+
for _, tt := range tests {
164+
got := ValidHeaderFieldValue(tt.in)
165+
if tt.want != got {
166+
t.Errorf("ValidHeaderFieldValue(%q) = %t; want %t", tt.in, got, tt.want)
167+
}
168+
}
169+
}
170+
132171
func BenchmarkValidHeaderFieldName(b *testing.B) {
133172
names := []string{
134173
"",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy