Browse Source

[core] reject WS following header field-name (fixes #2985)

reject whitespace following request header field-name and before colon
Such whitespace is forbidden in RFC 7230 Section 3.2.4.

strict header parsing is enabled by default in lighttpd.  However,
if explicitly disabled in lighttpd.conf, lighttpd will continue to
accept (and re-format) such field-names before passing to any backend.
  UNSAFE: server.http-parseopts = ( "header-strict" => "disable" )
  This is NOT RECOMMENDED since doing so disables other protections
  provided by lighttpd strict http header parsing.

(thx fedormixalich)

x-ref:
  stricter request header parsing
  https://redmine.lighttpd.net/issues/2985
tags/lighttpd-1.4.55
Glenn Strauss 9 months ago
parent
commit
61f85d14ee
3 changed files with 15 additions and 15 deletions
  1. +13
    -0
      src/request.c
  2. +1
    -4
      src/t/test_request.c
  3. +1
    -11
      tests/request.t

+ 13
- 0
src/request.c View File

@@ -723,6 +723,19 @@ int http_request_parse(server *srv, connection *con, buffer *hdrs) {
switch(*cur) {
case ' ':
case '\t':
/* RFC7230 Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
* 3.2.4. Field Parsing
* [...]
* No whitespace is allowed between the header field-name and colon. In
* the past, differences in the handling of such whitespace have led to
* security vulnerabilities in request routing and response handling. A
* server MUST reject any received request message that contains
* whitespace between a header field-name and colon with a response code
* of 400 (Bad Request). A proxy MUST remove any such whitespace from a
* response message before forwarding the message downstream.
*/
if (http_header_strict)
return http_request_header_line_invalid(srv, 400, "invalid whitespace between field-name and colon -> 400");
/* skip every thing up to the : */
do { ++cur; } while (*cur == ' ' || *cur == '\t');
if (*cur != ':') {


+ 1
- 4
src/t/test_request.c View File

@@ -310,14 +310,11 @@ static void test_request_http_request_parse(server *srv, connection *con)
assert(buffer_is_equal_string(con->request.uri,
CONST_STR_LEN("/")));

run_http_request_parse(srv, con, __LINE__, 0,
run_http_request_parse(srv, con, __LINE__, 400,
"whitespace after key",
CONST_STR_LEN("GET / HTTP/1.0\r\n"
"ABC : foo\r\n"
"\r\n"));
ds = (data_string *)
array_get_element_klen(con->request.headers, CONST_STR_LEN("ABC"));
assert(ds && buffer_is_equal_string(ds->value, CONST_STR_LEN("foo")));

run_http_request_parse(srv, con, __LINE__, 400,
"whitespace within key",


+ 1
- 11
tests/request.t View File

@@ -8,7 +8,7 @@ BEGIN {

use strict;
use IO::Socket;
use Test::More tests => 52;
use Test::More tests => 51;
use LightyTest;

my $tf = LightyTest->new();
@@ -503,16 +503,6 @@ $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
ok($tf->handle_http($t) == 0, 'static file with forbidden pathinfo');


print "\nConnection header\n";
$t->{REQUEST} = ( <<EOF
GET /12345.txt HTTP/1.1
Connection : close
Host: 123.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
ok($tf->handle_http($t) == 0, 'Connection-header, spaces before ":"');

$t->{REQUEST} = ( <<EOF
GET /12345.txt HTTP/1.1
Connection: ,close


Loading…
Cancel
Save