2323
2424#include " ts/ink_defs.h"
2525#include " ts/ink_platform.h"
26- #include " ts/TsBuffer.h"
2726#include " ts/ink_inet.h"
2827#include < assert.h>
2928#include < stdio.h>
@@ -161,6 +160,15 @@ is_digit(char c)
161160 return ((c <= ' 9' ) && (c >= ' 0' ));
162161}
163162
163+ // test to see if a character is a valid character for a host in a URI according to
164+ // RFC 3986 and RFC 1034
165+ inline static int
166+ is_host_char (char c)
167+ {
168+ return (ParseRules::is_alnum (c) || (c == ' -' ) || (c == ' .' ) || (c == ' [' ) || (c == ' ]' ) || (c == ' _' ) || (c == ' :' ) ||
169+ (c == ' ~' ) || (c == ' %' ));
170+ }
171+
164172/* **********************************************************************
165173 * *
166174 * M A I N C O D E *
@@ -1107,6 +1115,18 @@ http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const
11071115 return mime_parser_parse (&parser->m_mime_parser , heap, hh->m_fields_impl , start, end, must_copy_strings, eof);
11081116}
11091117
1118+ // Checks if `addr` is a valid FQDN string
1119+ bool
1120+ validate_host_name (ts::ConstBuffer addr)
1121+ {
1122+ while (addr) {
1123+ if (!(is_host_char (*addr)))
1124+ return false ;
1125+ ++addr;
1126+ }
1127+ return true ;
1128+ }
1129+
11101130MIMEParseResult
11111131validate_hdr_host (HTTPHdrImpl *hh)
11121132{
@@ -1124,9 +1144,11 @@ validate_hdr_host(HTTPHdrImpl *hh)
11241144 if (port.size () > 5 )
11251145 return PARSE_ERROR;
11261146 int port_i = ink_atoi (port.data (), port.size ());
1127- if (port. size () > 5 || port_i >= 65536 || port_i <= 0 )
1147+ if (port_i >= 65536 || port_i <= 0 )
11281148 return PARSE_ERROR;
11291149 }
1150+ if (!validate_host_name (addr))
1151+ return PARSE_ERROR;
11301152 while (rest && PARSE_DONE == ret) {
11311153 if (!ParseRules::is_ws (*rest))
11321154 return PARSE_ERROR;
@@ -2188,3 +2210,45 @@ HTTPInfo::push_frag_offset(FragOffset offset)
21882210
21892211 m_alt->m_frag_offsets [m_alt->m_frag_offset_count ++] = offset;
21902212}
2213+
2214+
2215+ /* -------------------------------------------------------------------------
2216+ * Regression tests
2217+ -------------------------------------------------------------------------*/
2218+ #if TS_HAS_TESTS
2219+ #include " ts/TestBox.h"
2220+
2221+ const static struct {
2222+ const char *const text;
2223+ bool valid;
2224+ } http_validate_hdr_field_test_case[] = {{" yahoo" , true },
2225+ {" yahoo.com" , true },
2226+ {" yahoo.wow.com" , true },
2227+ {" yahoo.wow.much.amaze.com" , true },
2228+ {" 209.131.52.50" , true },
2229+ {" 192.168.0.1" , true },
2230+ {" localhost" , true },
2231+ {" 3ffe:1900:4545:3:200:f8ff:fe21:67cf" , true },
2232+ {" fe80:0:0:0:200:f8ff:fe21:67cf" , true },
2233+ {" fe80::200:f8ff:fe21:67cf" , true },
2234+ {" <svg onload=alert(1)>" , false }, // Sample host header XSS attack
2235+ {" jlads;f8-9349*(D&F*D(234jD*(FSD*(VKLJ#(*$@()#$)))))" , false },
2236+ {" \"\t\n " , false },
2237+ {" !@#$%^ &*(*&^%$#@#$%^&*(*&^%$#))" , false },
2238+ {" :):(:O!!!!!!" , false }};
2239+
2240+ REGRESSION_TEST (VALIDATE_HDR_FIELD)(RegressionTest *t, int /* level ATS_UNUSED */ , int *pstatus)
2241+ {
2242+ TestBox box (t, pstatus);
2243+ box = REGRESSION_TEST_PASSED;
2244+
2245+ for (unsigned int i = 0 ; i < sizeof (http_validate_hdr_field_test_case) / sizeof (http_validate_hdr_field_test_case[0 ]); ++i) {
2246+ const char *const txt = http_validate_hdr_field_test_case[i].text ;
2247+ ts::ConstBuffer tmp = ts::ConstBuffer (txt, strlen (txt));
2248+ box.check (validate_host_name (tmp) == http_validate_hdr_field_test_case[i].valid ,
2249+ " Validation of FQDN (host) header: \" %s\" , expected %s, but not" , txt,
2250+ (http_validate_hdr_field_test_case[i].valid ? " true" : " false" ));
2251+ }
2252+ }
2253+
2254+ #endif // TS_HAS_TESTS
0 commit comments