-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sanitize Content-Type for multipart/form-data requests #1139
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2114,6 +2114,58 @@ tfw_http_set_loc_hdrs(TfwHttpMsg *hm, TfwHttpReq *req) | |
return 0; | ||
} | ||
|
||
/** | ||
* Compose Content-Type header field from scratch. | ||
* | ||
* A POST-request with multipart/form-data payload need a boundary, which is | ||
* supplied by a parameter in the Content-Type header field. There are strict | ||
* instructions on how to parse that parameter (see RFC 7231 and RFC 7230), but | ||
* application servers parse it in a non-standard way. For example, PHP checks | ||
* whenever parameter name contains substring "boundary", and thus happily takes | ||
* "xxboundaryxx". Such quirks are used to bypass web application firewalls. | ||
* | ||
* To make evasions harder, this function composes value of the Content-Type | ||
* field from the parsed data. All parameters other than "boundary" are dropped. | ||
*/ | ||
static int | ||
tfw_http_recreate_content_type_multipart_hdr(TfwHttpReq *req) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR has a good comment, why the header is recreated from parsed value. Let's keep it with the function, since the backend compatibility issue is not a trivial case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment to the function. Text is mostly from the pull request descriptions, with references to the pull request removed. |
||
{ | ||
TfwStr replacement = { | ||
.ptr = (TfwStr []) { | ||
TFW_STR_FROM("Content-Type"), | ||
TFW_STR_FROM(": "), | ||
TFW_STR_FROM("multipart/form-data; boundary="), | ||
req->multipart_boundary_raw, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved back to the code version that expected Since this data is then used to recreate |
||
}, | ||
.flags = 4 << TFW_STR_CN_SHIFT, | ||
}; | ||
TfwStr *c = replacement.ptr; | ||
|
||
BUG_ON(!TFW_STR_PLAIN(&req->multipart_boundary_raw)); | ||
replacement.len = c[0].len + c[1].len + c[2].len + c[3].len; | ||
return tfw_http_msg_hdr_xfrm_str((TfwHttpMsg *)req, &replacement, | ||
TFW_HTTP_HDR_CONTENT_TYPE, false); | ||
} | ||
|
||
static bool | ||
tfw_http_should_validate_post_req(TfwHttpReq *req) | ||
{ | ||
if (req->location && req->location->validate_post_req) | ||
return true; | ||
|
||
if (!req->vhost) | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic of this function (as well as the whole
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, if |
||
|
||
if (req->vhost->loc_dflt && req->vhost->loc_dflt->validate_post_req) | ||
return true; | ||
|
||
if (req->vhost->vhost_dflt && | ||
req->vhost->vhost_dflt->loc_dflt->validate_post_req) | ||
return true; | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Adjust the request before proxying it to real server. | ||
*/ | ||
|
@@ -2143,6 +2195,15 @@ tfw_http_adjust_req(TfwHttpReq *req) | |
if (r < 0) | ||
return r; | ||
|
||
if (req->method == TFW_HTTP_METH_POST && | ||
test_bit(TFW_HTTP_B_CT_MULTIPART, req->flags) && | ||
tfw_http_should_validate_post_req(req)) | ||
{ | ||
r = tfw_http_recreate_content_type_multipart_hdr(req); | ||
if (r) | ||
return r; | ||
} | ||
|
||
return tfw_http_set_hdr_connection(hm, BIT(TFW_HTTP_B_CONN_KA)); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -229,6 +229,10 @@ enum { | |
TFW_HTTP_B_CONN_EXTRA, | ||
/* Chunked transfer encoding. */ | ||
TFW_HTTP_B_CHUNKED, | ||
/* Media type is multipart/form-data. */ | ||
TFW_HTTP_B_CT_MULTIPART, | ||
/* Multipart/form-data request have boundary parameter. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in #1154. Thanks! |
||
TFW_HTTP_B_CT_MULTIPART_HAS_BOUNDARY, | ||
/* Singular header presents more than once. */ | ||
TFW_HTTP_B_FIELD_DUPENTRY, | ||
|
||
|
@@ -369,6 +373,8 @@ typedef struct { | |
* @host - host in URI, may differ from Host header; | ||
* @uri_path - path + query + fragment from URI (RFC3986.3); | ||
* @mark - special hash mark for redirects handling in session module; | ||
* @multipart_boundary_raw - multipart boundary as is, maybe with escaped chars; | ||
* @multipart_boundary - decoded multipart boundary; | ||
* @fwd_list - member in the queue of forwarded/backlogged requests; | ||
* @nip_list - member in the queue of non-idempotent requests; | ||
* @method - HTTP request method, one of GET/PORT/HEAD/etc; | ||
|
@@ -394,6 +400,8 @@ struct tfw_http_req_t { | |
TfwStr host; | ||
TfwStr uri_path; | ||
TfwStr mark; | ||
TfwStr multipart_boundary_raw; | ||
TfwStr multipart_boundary; | ||
struct list_head fwd_list; | ||
struct list_head nip_list; | ||
unsigned char method; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra asterisk after
directives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that was a "possessive 's". Since "directives" is plural, "'s" reduces to a sole apostrophe.