| # This test ensures that h2 requests with invalid pseudo-headers are properly |
| # rejected. Also, the host header must be ignored if authority is present. This |
| # is necessary to avoid http/2 desync attacks through haproxy as described here |
| # https://portswigger.net/research/http2 |
| |
| varnishtest "h2 desync attacks" |
| feature ignore_unknown_macro |
| |
| server s1 { |
| rxreq |
| expect req.http.host == "hostname" |
| txresp |
| } -start |
| |
| # haproxy frontend |
| haproxy hap -conf { |
| defaults |
| mode http |
| |
| listen feSrvH1 |
| bind "fd@${feSrvH1}" |
| http-request return status 200 |
| |
| listen feSrvH2 |
| bind "fd@${feSrvH2}" proto h2 |
| http-request return status 200 |
| |
| listen fe1 |
| bind "fd@${fe1}" proto h2 |
| server srv-hapSrv "${hap_feSrvH1_addr}:${hap_feSrvH1_port}" |
| |
| listen fe2 |
| bind "fd@${fe2}" proto h2 |
| server srv-hapSrv "${hap_feSrvH2_addr}:${hap_feSrvH2_port}" proto h2 |
| |
| listen fe3 |
| bind "fd@${fe3}" proto h2 |
| server s1 "${s1_addr}:${s1_port}" |
| } -start |
| |
| # valid request |
| client c1 -connect ${hap_fe1_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET" \ |
| -scheme "http" \ |
| -url "/" |
| rxresp |
| expect resp.status == 200 |
| } -run |
| } -run |
| |
| # valid request |
| client c2 -connect ${hap_fe2_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET" \ |
| -scheme "http" \ |
| -url "/" |
| rxresp |
| expect resp.status == 200 |
| } -run |
| } -run |
| |
| # invalid path |
| client c3-path -connect ${hap_fe1_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET" \ |
| -scheme "http" \ |
| -url "hello-world" |
| rxrst |
| } -run |
| } -run |
| |
| # invalid scheme |
| client c4-scheme -connect ${hap_fe1_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET" \ |
| -scheme "http://localhost/?" \ |
| -url "/" |
| rxresp |
| expect resp.status == 400 |
| } -run |
| } -run |
| |
| # invalid method |
| client c5-method -connect ${hap_fe2_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET?" \ |
| -scheme "http" \ |
| -url "/" |
| rxresp |
| expect resp.status == 400 |
| } -run |
| } -run |
| |
| # different authority and host headers |
| # in this case, host should be ignored in favor of the authority |
| client c6-host-authority -connect ${hap_fe3_sock} { |
| txpri |
| stream 0 { |
| txsettings |
| rxsettings |
| txsettings -ack |
| rxsettings |
| expect settings.ack == true |
| } -run |
| |
| stream 1 { |
| txreq \ |
| -method "GET" \ |
| -scheme "http" \ |
| -url "/" \ |
| -hdr ":authority" "hostname" \ |
| -hdr "host" "other_host" |
| } -run |
| } -run |
| |
| server s1 -wait |