|
| 1 | +Relaxed header value character parsing |
| 2 | +======================================= |
| 3 | + |
| 4 | +Relaxed parsing mode: accepts unusual characters (like control chars) |
| 5 | +but still rejects specifally dangerous ones (NULL, CR, LF) that could enable |
| 6 | +smuggling attacks. |
| 7 | + |
| 8 | +## Control char in header value (relaxed) |
| 9 | + |
| 10 | +Control characters like form feed should be accepted in relaxed mode. |
| 11 | + |
| 12 | +<!-- meta={"type": "request-lenient-header-value-relaxed"} --> |
| 13 | +```http |
| 14 | +GET /url HTTP/1.1 |
| 15 | +Header1: hello\fworld |
| 16 | +
|
| 17 | +
|
| 18 | +``` |
| 19 | + |
| 20 | +```log |
| 21 | +off=0 message begin |
| 22 | +off=0 len=3 span[method]="GET" |
| 23 | +off=3 method complete |
| 24 | +off=4 len=4 span[url]="/url" |
| 25 | +off=9 url complete |
| 26 | +off=9 len=4 span[protocol]="HTTP" |
| 27 | +off=13 protocol complete |
| 28 | +off=14 len=3 span[version]="1.1" |
| 29 | +off=17 version complete |
| 30 | +off=19 len=7 span[header_field]="Header1" |
| 31 | +off=27 header_field complete |
| 32 | +off=28 len=11 span[header_value]="hello\fworld" |
| 33 | +off=41 header_value complete |
| 34 | +off=43 headers complete method=1 v=1/1 flags=0 content_length=0 |
| 35 | +off=43 message complete |
| 36 | +``` |
| 37 | + |
| 38 | +## Control char in header value (strict) |
| 39 | + |
| 40 | +Control characters should be rejected in strict mode. |
| 41 | + |
| 42 | +<!-- meta={"type": "request"} --> |
| 43 | +```http |
| 44 | +GET /url HTTP/1.1 |
| 45 | +Header1: hello\fworld |
| 46 | +
|
| 47 | +
|
| 48 | +``` |
| 49 | + |
| 50 | +```log |
| 51 | +off=0 message begin |
| 52 | +off=0 len=3 span[method]="GET" |
| 53 | +off=3 method complete |
| 54 | +off=4 len=4 span[url]="/url" |
| 55 | +off=9 url complete |
| 56 | +off=9 len=4 span[protocol]="HTTP" |
| 57 | +off=13 protocol complete |
| 58 | +off=14 len=3 span[version]="1.1" |
| 59 | +off=17 version complete |
| 60 | +off=19 len=7 span[header_field]="Header1" |
| 61 | +off=27 header_field complete |
| 62 | +off=28 len=5 span[header_value]="hello" |
| 63 | +off=33 error code=10 reason="Invalid header value char" |
| 64 | +``` |
| 65 | + |
| 66 | +## LF in header value should be rejected even with relaxed flag |
| 67 | + |
| 68 | +Invalid newlines could enable smuggling and must still be rejected. |
| 69 | + |
| 70 | +<!-- meta={"type": "request-lenient-header-value-relaxed"} --> |
| 71 | +```http |
| 72 | +POST / HTTP/1.1 |
| 73 | +Host: localhost:5000 |
| 74 | +x:\nTransfer-Encoding: chunked |
| 75 | +
|
| 76 | +1 |
| 77 | +A |
| 78 | +0 |
| 79 | +
|
| 80 | +``` |
| 81 | + |
| 82 | +```log |
| 83 | +off=0 message begin |
| 84 | +off=0 len=4 span[method]="POST" |
| 85 | +off=4 method complete |
| 86 | +off=5 len=1 span[url]="/" |
| 87 | +off=7 url complete |
| 88 | +off=7 len=4 span[protocol]="HTTP" |
| 89 | +off=11 protocol complete |
| 90 | +off=12 len=3 span[version]="1.1" |
| 91 | +off=15 version complete |
| 92 | +off=17 len=4 span[header_field]="Host" |
| 93 | +off=22 header_field complete |
| 94 | +off=23 len=14 span[header_value]="localhost:5000" |
| 95 | +off=39 header_value complete |
| 96 | +off=39 len=1 span[header_field]="x" |
| 97 | +off=41 header_field complete |
| 98 | +off=42 error code=10 reason="Invalid header value char" |
| 99 | +``` |
| 100 | + |
| 101 | +## CR without LF in header value should be rejected even with relaxed flag |
| 102 | + |
| 103 | +Invalid newlines could enable smuggling and must still be rejected. |
| 104 | + |
| 105 | +<!-- meta={"type": "request-lenient-header-value-relaxed"} --> |
| 106 | +```http |
| 107 | +POST / HTTP/1.1 |
| 108 | +Host: localhost:5000 |
| 109 | +x:\rTransfer-Encoding: chunked |
| 110 | +
|
| 111 | +1 |
| 112 | +A |
| 113 | +0 |
| 114 | +
|
| 115 | +``` |
| 116 | + |
| 117 | +```log |
| 118 | +off=0 message begin |
| 119 | +off=0 len=4 span[method]="POST" |
| 120 | +off=4 method complete |
| 121 | +off=5 len=1 span[url]="/" |
| 122 | +off=7 url complete |
| 123 | +off=7 len=4 span[protocol]="HTTP" |
| 124 | +off=11 protocol complete |
| 125 | +off=12 len=3 span[version]="1.1" |
| 126 | +off=15 version complete |
| 127 | +off=17 len=4 span[header_field]="Host" |
| 128 | +off=22 header_field complete |
| 129 | +off=23 len=14 span[header_value]="localhost:5000" |
| 130 | +off=39 header_value complete |
| 131 | +off=39 len=1 span[header_field]="x" |
| 132 | +off=41 header_field complete |
| 133 | +off=42 error code=2 reason="Expected LF after CR" |
| 134 | +``` |
| 135 | + |
| 136 | +## Space after start line must still fail |
| 137 | + |
| 138 | +Unlike LENIENT_HEADERS, this flag should NOT allow space after start line. |
| 139 | + |
| 140 | +<!-- meta={"type": "request-lenient-header-value-relaxed"} --> |
| 141 | +```http |
| 142 | +GET /url HTTP/1.1 |
| 143 | + Header1: value |
| 144 | +
|
| 145 | +``` |
| 146 | + |
| 147 | +```log |
| 148 | +off=0 message begin |
| 149 | +off=0 len=3 span[method]="GET" |
| 150 | +off=3 method complete |
| 151 | +off=4 len=4 span[url]="/url" |
| 152 | +off=9 url complete |
| 153 | +off=9 len=4 span[protocol]="HTTP" |
| 154 | +off=13 protocol complete |
| 155 | +off=14 len=3 span[version]="1.1" |
| 156 | +off=17 version complete |
| 157 | +off=20 error code=30 reason="Unexpected space after start line" |
| 158 | +``` |
0 commit comments