Skip to content

Commit

Permalink
Restrict unknown scheme of HTTP/2 request (#9010)
Browse files Browse the repository at this point in the history
Strictly following RFC 3986 Section 3.1

```
scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
```
  • Loading branch information
masaori335 committed Aug 9, 2022
1 parent eaef5e8 commit c56f872
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 12 deletions.
42 changes: 31 additions & 11 deletions proxy/hdrs/URL.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,36 @@ validate_host_name(std::string_view addr)
return std::all_of(addr.begin(), addr.end(), &is_host_char);
}

/**
Checks if the (un-well-known) scheme is valid
RFC 3986 Section 3.1
These are the valid characters in a scheme:
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
return an error if there is another character in the scheme
*/
bool
validate_scheme(std::string_view scheme)
{
if (scheme.empty()) {
return false;
}

if (!ParseRules::is_alpha(scheme[0])) {
return false;
}

for (size_t i = 0; i < scheme.size(); ++i) {
const char &c = scheme[i];

if (!(ParseRules::is_alnum(c) != 0 || c == '+' || c == '-' || c == '.')) {
return false;
}
}

return true;
}

/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/

Expand Down Expand Up @@ -1133,19 +1163,9 @@ url_parse_scheme(HdrHeap *heap, URLImpl *url, const char **start, const char *en

if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) {
// Unknown scheme, validate the scheme

// RFC 3986 Section 3.1
// These are the valid characters in a scheme:
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
// return an error if there is another character in the scheme
if (!ParseRules::is_alpha(*scheme_start)) {
if (!validate_scheme({scheme_start, static_cast<size_t>(scheme_end - scheme_start)})) {
return PARSE_RESULT_ERROR;
}
for (cur = scheme_start + 1; cur < scheme_end; ++cur) {
if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) {
return PARSE_RESULT_ERROR;
}
}
}
url->set_scheme(heap, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p);
}
Expand Down
2 changes: 2 additions & 0 deletions proxy/hdrs/URL.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ extern int URL_LEN_MMST;

/* Public */
bool validate_host_name(std::string_view addr);
bool validate_scheme(std::string_view scheme);

void url_init();

URLImpl *url_create(HdrHeap *heap);
Expand Down
21 changes: 21 additions & 0 deletions proxy/hdrs/unit_tests/test_URL.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ TEST_CASE("ValidateURL", "[proxy][validurl]")
}
}

TEST_CASE("Validate Scheme", "[proxy][validscheme]")
{
static const struct {
std::string_view text;
bool valid;
} scheme_test_cases[] = {{"http", true}, {"https", true}, {"example", true}, {"example.", true},
{"example++", true}, {"example--.", true}, {"++example", false}, {"--example", false},
{".example", false}, {"example://", false}};

for (auto i : scheme_test_cases) {
// it's pretty hard to debug with
// CHECK(validate_scheme(i.text) == i.valid);

std::string_view text = i.text;
if (validate_scheme(text) != i.valid) {
std::printf("Validation of scheme: \"%s\", expected %s, but not\n", text.data(), (i.valid ? "true" : "false"));
CHECK(false);
}
}
}

namespace UrlImpl
{
bool url_is_strictly_compliant(const char *start, const char *end);
Expand Down
11 changes: 10 additions & 1 deletion proxy/http2/HTTP2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,17 @@ http2_convert_header_from_2_to_1_1(HTTPHdr *headers)
field != nullptr && field->value_is_valid(is_control_BIT | is_ws_BIT)) {
int scheme_len;
const char *scheme = field->value_get(&scheme_len);
const char *scheme_wks;

int scheme_wks_idx = hdrtoken_tokenize(scheme, scheme_len, &scheme_wks);

if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) {
// unkown scheme, validate the scheme
if (!validate_scheme({scheme, static_cast<size_t>(scheme_len)})) {
return PARSE_RESULT_ERROR;
}
}

int scheme_wks_idx = hdrtoken_tokenize(scheme, scheme_len);
headers->m_http->u.req.m_url_impl->set_scheme(headers->m_heap, scheme, scheme_wks_idx, scheme_len, true);

headers->field_delete(field);
Expand Down

0 comments on commit c56f872

Please sign in to comment.