mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-13 15:46:49 +03:00
Validate request method
Use the same validation as net/http. Fixes https://github.com/valyala/fasthttp/issues/1803
This commit is contained in:
@@ -10,3 +10,4 @@ const quotedArgShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01
|
||||
const quotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
||||
const validHeaderFieldByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00"
|
||||
const validHeaderValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
||||
const validMethodValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
@@ -146,6 +146,101 @@ func main() {
|
||||
return table
|
||||
}()
|
||||
|
||||
validMethodValueByteTable := [256]byte{
|
||||
/*
|
||||
Same as net/http
|
||||
|
||||
Method = "OPTIONS" ; Section 9.2
|
||||
| "GET" ; Section 9.3
|
||||
| "HEAD" ; Section 9.4
|
||||
| "POST" ; Section 9.5
|
||||
| "PUT" ; Section 9.6
|
||||
| "DELETE" ; Section 9.7
|
||||
| "TRACE" ; Section 9.8
|
||||
| "CONNECT" ; Section 9.9
|
||||
| extension-method
|
||||
extension-method = token
|
||||
token = 1*<any CHAR except CTLs or separators>
|
||||
*/
|
||||
'!': 1,
|
||||
'#': 1,
|
||||
'$': 1,
|
||||
'%': 1,
|
||||
'&': 1,
|
||||
'\'': 1,
|
||||
'*': 1,
|
||||
'+': 1,
|
||||
'-': 1,
|
||||
'.': 1,
|
||||
'0': 1,
|
||||
'1': 1,
|
||||
'2': 1,
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 1,
|
||||
'6': 1,
|
||||
'7': 1,
|
||||
'8': 1,
|
||||
'9': 1,
|
||||
'A': 1,
|
||||
'B': 1,
|
||||
'C': 1,
|
||||
'D': 1,
|
||||
'E': 1,
|
||||
'F': 1,
|
||||
'G': 1,
|
||||
'H': 1,
|
||||
'I': 1,
|
||||
'J': 1,
|
||||
'K': 1,
|
||||
'L': 1,
|
||||
'M': 1,
|
||||
'N': 1,
|
||||
'O': 1,
|
||||
'P': 1,
|
||||
'Q': 1,
|
||||
'R': 1,
|
||||
'S': 1,
|
||||
'T': 1,
|
||||
'U': 1,
|
||||
'W': 1,
|
||||
'V': 1,
|
||||
'X': 1,
|
||||
'Y': 1,
|
||||
'Z': 1,
|
||||
'^': 1,
|
||||
'_': 1,
|
||||
'`': 1,
|
||||
'a': 1,
|
||||
'b': 1,
|
||||
'c': 1,
|
||||
'd': 1,
|
||||
'e': 1,
|
||||
'f': 1,
|
||||
'g': 1,
|
||||
'h': 1,
|
||||
'i': 1,
|
||||
'j': 1,
|
||||
'k': 1,
|
||||
'l': 1,
|
||||
'm': 1,
|
||||
'n': 1,
|
||||
'o': 1,
|
||||
'p': 1,
|
||||
'q': 1,
|
||||
'r': 1,
|
||||
's': 1,
|
||||
't': 1,
|
||||
'u': 1,
|
||||
'v': 1,
|
||||
'w': 1,
|
||||
'x': 1,
|
||||
'y': 1,
|
||||
'z': 1,
|
||||
'|': 1,
|
||||
'~': 1,
|
||||
}
|
||||
|
||||
w := bytes.NewBufferString(pre)
|
||||
fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable)
|
||||
fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable)
|
||||
@@ -154,6 +249,7 @@ func main() {
|
||||
fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
|
||||
fmt.Fprintf(w, "const validHeaderFieldByteTable = %q\n", validHeaderFieldByteTable)
|
||||
fmt.Fprintf(w, "const validHeaderValueByteTable = %q\n", validHeaderValueByteTable)
|
||||
fmt.Fprintf(w, "const validMethodValueByteTable = %q\n", validMethodValueByteTable)
|
||||
|
||||
if err := os.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -2855,6 +2855,15 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
|
||||
return len(buf) - len(bNext), nil
|
||||
}
|
||||
|
||||
func isValidMethod(method []byte) bool {
|
||||
for _, ch := range method {
|
||||
if validMethodValueByteTable[ch] == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
|
||||
bNext := buf
|
||||
var b []byte
|
||||
@@ -2874,6 +2883,14 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
|
||||
return 0, fmt.Errorf("cannot find http request method in %q", buf)
|
||||
}
|
||||
h.method = append(h.method[:0], b[:n]...)
|
||||
|
||||
if !isValidMethod(h.method) {
|
||||
if h.secureErrorLogMessage {
|
||||
return 0, errors.New("unsupported http request method")
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported http request method %q in %q", h.method, buf)
|
||||
}
|
||||
|
||||
b = b[n+1:]
|
||||
|
||||
// parse requestURI
|
||||
|
||||
+3
-4
@@ -2668,10 +2668,6 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
|
||||
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456",
|
||||
-2, "/abc", "aa.com", "", "adv")
|
||||
|
||||
// invalid method
|
||||
testRequestHeaderReadSuccess(t, h, "POST /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\nmnbv",
|
||||
-2, "/foo/bar", "google.com", "", "")
|
||||
|
||||
// put request
|
||||
testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere",
|
||||
123, "/faa", "aaa.com", "", "aaa")
|
||||
@@ -2767,6 +2763,9 @@ func TestRequestHeaderReadError(t *testing.T) {
|
||||
|
||||
// Zero-length header
|
||||
testRequestHeaderReadError(t, h, "GET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
|
||||
|
||||
// Invalid method
|
||||
testRequestHeaderReadError(t, h, "G(ET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
|
||||
}
|
||||
|
||||
func TestRequestHeaderReadSecuredError(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user