http: return error when receiving too large header set
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
This commit is contained in:
parent
944e219f10
commit
3ee79c1674
@ -182,8 +182,11 @@ static int hyper_each_header(void *userdata,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->info.header_size += (curl_off_t)len;
|
result = Curl_bump_headersize(data, len, FALSE);
|
||||||
data->req.headerbytecount += (curl_off_t)len;
|
if(result) {
|
||||||
|
data->state.hresult = result;
|
||||||
|
return HYPER_ITER_BREAK;
|
||||||
|
}
|
||||||
return HYPER_ITER_CONTINUE;
|
return HYPER_ITER_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,9 +316,8 @@ static CURLcode status_line(struct Curl_easy *data,
|
|||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
data->info.header_size += (curl_off_t)len;
|
result = Curl_bump_headersize(data, len, FALSE);
|
||||||
data->req.headerbytecount += (curl_off_t)len;
|
return result;
|
||||||
return CURLE_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -587,7 +587,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
|||||||
return result;
|
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
|
/* Newlines are CRLF, so the CR is ignored as the line isn't
|
||||||
really terminated until the LF comes. Treat a following CR
|
really terminated until the LF comes. Treat a following CR
|
||||||
|
|||||||
34
lib/http.c
34
lib/http.c
@ -3920,6 +3920,29 @@ static CURLcode verify_header(struct Curl_easy *data)
|
|||||||
return CURLE_OK;
|
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.
|
* Read any HTTP header lines from the server and pass them to the client app.
|
||||||
*/
|
*/
|
||||||
@ -4173,8 +4196,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
data->info.header_size += (long)headerlen;
|
result = Curl_bump_headersize(data, headerlen, FALSE);
|
||||||
data->req.headerbytecount += (long)headerlen;
|
if(result)
|
||||||
|
return result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When all the headers have been parsed, see if we should give
|
* When all the headers have been parsed, see if we should give
|
||||||
@ -4496,8 +4520,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
data->info.header_size += Curl_dyn_len(&data->state.headerb);
|
result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb),
|
||||||
data->req.headerbytecount += Curl_dyn_len(&data->state.headerb);
|
FALSE);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
|
||||||
Curl_dyn_reset(&data->state.headerb);
|
Curl_dyn_reset(&data->state.headerb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,10 @@ extern const struct Curl_handler Curl_handler_wss;
|
|||||||
|
|
||||||
struct dynhds;
|
struct dynhds;
|
||||||
|
|
||||||
|
CURLcode Curl_bump_headersize(struct Curl_easy *data,
|
||||||
|
size_t delta,
|
||||||
|
bool connect_only);
|
||||||
|
|
||||||
/* Header specific functions */
|
/* Header specific functions */
|
||||||
bool Curl_compareheader(const char *headerline, /* line to check */
|
bool Curl_compareheader(const char *headerline, /* line to check */
|
||||||
const char *header, /* header keyword _with_ colon */
|
const char *header, /* header keyword _with_ colon */
|
||||||
@ -183,6 +187,11 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
|
|||||||
#define EXPECT_100_THRESHOLD (1024*1024)
|
#define EXPECT_100_THRESHOLD (1024*1024)
|
||||||
#endif
|
#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 */
|
#endif /* CURL_DISABLE_HTTP */
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
|||||||
@ -341,7 +341,9 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
|
|||||||
ssize_t clipamount = 0;
|
ssize_t clipamount = 0;
|
||||||
bool restart = FALSE;
|
bool restart = FALSE;
|
||||||
|
|
||||||
data->req.headerbytecount += (long)gotbytes;
|
result = Curl_bump_headersize(data, gotbytes, FALSE);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
|
||||||
pp->nread_resp += gotbytes;
|
pp->nread_resp += gotbytes;
|
||||||
for(i = 0; i < gotbytes; ptr++, i++) {
|
for(i = 0; i < gotbytes; ptr++, i++) {
|
||||||
|
|||||||
@ -629,17 +629,16 @@ struct SingleRequest {
|
|||||||
curl_off_t bytecount; /* total number of bytes read */
|
curl_off_t bytecount; /* total number of bytes read */
|
||||||
curl_off_t writebytecount; /* number of bytes written */
|
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
|
curl_off_t pendingheader; /* this many bytes left to send is actually
|
||||||
header and not body */
|
header and not body */
|
||||||
struct curltime start; /* transfer started at this time */
|
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 {
|
enum {
|
||||||
HEADER_NORMAL, /* no bad header at all */
|
HEADER_NORMAL, /* no bad header at all */
|
||||||
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
|
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
|
||||||
@ -1089,7 +1088,6 @@ struct PureInfo {
|
|||||||
int httpversion; /* the http version number X.Y = X*10+Y */
|
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_t filetime; /* If requested, this is might get set. Set to -1 if the
|
||||||
time was unretrievable. */
|
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) */
|
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 proxyauthavail; /* what proxy auth types were announced */
|
||||||
unsigned long httpauthavail; /* what host auth types were announced */
|
unsigned long httpauthavail; /* what host auth types were announced */
|
||||||
@ -1097,6 +1095,7 @@ struct PureInfo {
|
|||||||
char *contenttype; /* the content type of the object */
|
char *contenttype; /* the content type of the object */
|
||||||
char *wouldredirect; /* URL this would've been redirected to if asked to */
|
char *wouldredirect; /* URL this would've been redirected to if asked to */
|
||||||
curl_off_t retry_after; /* info from Retry-After: header */
|
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'
|
/* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
|
||||||
and, 'conn_local_port' are copied over from the connectdata struct in
|
and, 'conn_local_port' are copied over from the connectdata struct in
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user