Skip to content

Commit

Permalink
http: return error when receiving too large header set
Browse files Browse the repository at this point in the history
To avoid abuse. The limit is set to 300 KB for the accumulated size of
all received HTTP headers for a single response. Incomplete research
suggests that Chrome uses a 256-300 KB limit, while Firefox allows up to
1MB.

Closes #11582
  • Loading branch information
bagder committed Aug 3, 2023
1 parent 944e219 commit 3ee79c1
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 20 deletions.
12 changes: 7 additions & 5 deletions lib/c-hyper.c
Expand Up @@ -182,8 +182,11 @@ static int hyper_each_header(void *userdata,
}
}

data->info.header_size += (curl_off_t)len;
data->req.headerbytecount += (curl_off_t)len;
result = Curl_bump_headersize(data, len, FALSE);
if(result) {
data->state.hresult = result;
return HYPER_ITER_BREAK;
}
return HYPER_ITER_CONTINUE;
}

Expand Down Expand Up @@ -313,9 +316,8 @@ static CURLcode status_line(struct Curl_easy *data,
if(result)
return result;
}
data->info.header_size += (curl_off_t)len;
data->req.headerbytecount += (curl_off_t)len;
return CURLE_OK;
result = Curl_bump_headersize(data, len, FALSE);
return result;
}

/*
Expand Down
4 changes: 3 additions & 1 deletion lib/cf-h1-proxy.c
Expand Up @@ -587,7 +587,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
return result;
}

data->info.header_size += (long)perline;
result = Curl_bump_headersize(data, perline, TRUE);
if(result)
return result;

/* Newlines are CRLF, so the CR is ignored as the line isn't
really terminated until the LF comes. Treat a following CR
Expand Down
34 changes: 30 additions & 4 deletions lib/http.c
Expand Up @@ -3920,6 +3920,29 @@ static CURLcode verify_header(struct Curl_easy *data)
return CURLE_OK;
}

CURLcode Curl_bump_headersize(struct Curl_easy *data,
size_t delta,
bool connect_only)
{
size_t bad = 0;
if(delta < MAX_HTTP_RESP_HEADER_SIZE) {
if(!connect_only)
data->req.headerbytecount += (unsigned int)delta;
data->info.header_size += (unsigned int)delta;
if(data->info.header_size > MAX_HTTP_RESP_HEADER_SIZE)
bad = data->info.header_size;
}
else
bad = data->info.header_size + delta;
if(bad) {
failf(data, "Too large response headers: %zu > %zu",
bad, MAX_HTTP_RESP_HEADER_SIZE);
return CURLE_RECV_ERROR;
}
return CURLE_OK;
}


/*
* Read any HTTP header lines from the server and pass them to the client app.
*/
Expand Down Expand Up @@ -4173,8 +4196,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result)
return result;

data->info.header_size += (long)headerlen;
data->req.headerbytecount += (long)headerlen;
result = Curl_bump_headersize(data, headerlen, FALSE);
if(result)
return result;

/*
* When all the headers have been parsed, see if we should give
Expand Down Expand Up @@ -4496,8 +4520,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result)
return result;

data->info.header_size += Curl_dyn_len(&data->state.headerb);
data->req.headerbytecount += Curl_dyn_len(&data->state.headerb);
result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb),
FALSE);
if(result)
return result;

Curl_dyn_reset(&data->state.headerb);
}
Expand Down
9 changes: 9 additions & 0 deletions lib/http.h
Expand Up @@ -64,6 +64,10 @@ extern const struct Curl_handler Curl_handler_wss;

struct dynhds;

CURLcode Curl_bump_headersize(struct Curl_easy *data,
size_t delta,
bool connect_only);

/* Header specific functions */
bool Curl_compareheader(const char *headerline, /* line to check */
const char *header, /* header keyword _with_ colon */
Expand Down Expand Up @@ -183,6 +187,11 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
#define EXPECT_100_THRESHOLD (1024*1024)
#endif

/* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers
combined that libcurl allows for a single HTTP response, any HTTP
version. This count includes CONNECT response headers. */
#define MAX_HTTP_RESP_HEADER_SIZE (300*1024)

#endif /* CURL_DISABLE_HTTP */

/****************************************************************************
Expand Down
4 changes: 3 additions & 1 deletion lib/pingpong.c
Expand Up @@ -341,7 +341,9 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
ssize_t clipamount = 0;
bool restart = FALSE;

data->req.headerbytecount += (long)gotbytes;
result = Curl_bump_headersize(data, gotbytes, FALSE);
if(result)
return result;

pp->nread_resp += gotbytes;
for(i = 0; i < gotbytes; ptr++, i++) {
Expand Down
17 changes: 8 additions & 9 deletions lib/urldata.h
Expand Up @@ -629,17 +629,16 @@ struct SingleRequest {
curl_off_t bytecount; /* total number of bytes read */
curl_off_t writebytecount; /* number of bytes written */

curl_off_t headerbytecount; /* only count received headers */
curl_off_t deductheadercount; /* this amount of bytes doesn't count when we
check if anything has been transferred at
the end of a connection. We use this
counter to make only a 100 reply (without a
following second response code) result in a
CURLE_GOT_NOTHING error code */

curl_off_t pendingheader; /* this many bytes left to send is actually
header and not body */
struct curltime start; /* transfer started at this time */
unsigned int headerbytecount; /* only count received headers */
unsigned int deductheadercount; /* this amount of bytes doesn't count when
we check if anything has been transferred
at the end of a connection. We use this
counter to make only a 100 reply (without
a following second response code) result
in a CURLE_GOT_NOTHING error code */
enum {
HEADER_NORMAL, /* no bad header at all */
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
Expand Down Expand Up @@ -1089,14 +1088,14 @@ struct PureInfo {
int httpversion; /* the http version number X.Y = X*10+Y */
time_t filetime; /* If requested, this is might get set. Set to -1 if the
time was unretrievable. */
curl_off_t header_size; /* size of read header(s) in bytes */
curl_off_t request_size; /* the amount of bytes sent in the request(s) */
unsigned long proxyauthavail; /* what proxy auth types were announced */
unsigned long httpauthavail; /* what host auth types were announced */
long numconnects; /* how many new connection did libcurl created */
char *contenttype; /* the content type of the object */
char *wouldredirect; /* URL this would've been redirected to if asked to */
curl_off_t retry_after; /* info from Retry-After: header */
unsigned int header_size; /* size of read header(s) in bytes */

/* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
and, 'conn_local_port' are copied over from the connectdata struct in
Expand Down

0 comments on commit 3ee79c1

Please sign in to comment.