@@ -509,6 +509,10 @@ impl ProxyServer {
509509
510510 update_header ( headers, & X_FORWARDED_FOR , & new_x_forwarded_for) ;
511511
512+ // Strip any caller-provided attestation metadata before injecting authenticated values.
513+ headers. remove ( ATTESTATION_TYPE_HEADER ) ;
514+ headers. remove ( MEASUREMENT_HEADER ) ;
515+
512516 // If we have measurements, from the remote peer, add them to the request header
513517 let measurements = measurements. clone ( ) ;
514518
@@ -782,6 +786,8 @@ impl ProxyClient {
782786 Ok ( mut resp) => {
783787 debug!( "[proxy-client] Read response from proxy-server: {resp:?}" ) ;
784788 let headers = resp. headers_mut( ) ;
789+ headers. remove( MEASUREMENT_HEADER ) ;
790+
785791 if let Some ( measurements) = measurements. clone( ) {
786792 match measurements. to_header_format( ) {
787793 Ok ( header_value) => {
@@ -1225,6 +1231,56 @@ mod tests {
12251231 assert ! ( headers. get( MEASUREMENT_HEADER ) . is_none( ) ) ;
12261232 }
12271233
1234+ /// Test service that echoes attestation-related request headers as JSON.
1235+ async fn request_header_echo_service ( ) -> SocketAddr {
1236+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
1237+ let addr = listener. local_addr ( ) . unwrap ( ) ;
1238+
1239+ let app = axum:: Router :: new ( ) . route (
1240+ "/" ,
1241+ axum:: routing:: get ( |headers : http:: HeaderMap | async move {
1242+ axum:: Json ( serde_json:: json!( {
1243+ "measurement" : headers
1244+ . get( MEASUREMENT_HEADER )
1245+ . and_then( |v| v. to_str( ) . ok( ) ) ,
1246+ "attestation_type" : headers
1247+ . get( ATTESTATION_TYPE_HEADER )
1248+ . and_then( |v| v. to_str( ) . ok( ) ) ,
1249+ } ) )
1250+ } ) ,
1251+ ) ;
1252+
1253+ tokio:: spawn ( async move {
1254+ axum:: serve ( listener, app) . await . unwrap ( ) ;
1255+ } ) ;
1256+
1257+ addr
1258+ }
1259+
1260+ /// Test service that deliberately returns a spoofed measurement header.
1261+ async fn spoofed_response_measurement_service ( ) -> SocketAddr {
1262+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
1263+ let addr = listener. local_addr ( ) . unwrap ( ) ;
1264+
1265+ let app = axum:: Router :: new ( ) . route (
1266+ "/" ,
1267+ axum:: routing:: get ( || async move {
1268+ let mut response = http:: Response :: new ( "ok" . to_string ( ) ) ;
1269+ response. headers_mut ( ) . insert (
1270+ MEASUREMENT_HEADER ,
1271+ HeaderValue :: from_static ( "{\" spoofed\" :\" value\" }" ) ,
1272+ ) ;
1273+ response
1274+ } ) ,
1275+ ) ;
1276+
1277+ tokio:: spawn ( async move {
1278+ axum:: serve ( listener, app) . await . unwrap ( ) ;
1279+ } ) ;
1280+
1281+ addr
1282+ }
1283+
12281284 #[ test]
12291285 fn proxy_alpn_protocols_prefer_http2 ( ) {
12301286 let mut protocols = Vec :: new ( ) ;
@@ -1597,6 +1653,68 @@ mod tests {
15971653 assert_eq ! ( res_body, "No measurements" ) ;
15981654 }
15991655
1656+ #[ tokio:: test( flavor = "multi_thread" ) ]
1657+ async fn http_proxy_strips_spoofed_request_attestation_headers ( ) {
1658+ let target_addr = request_header_echo_service ( ) . await ;
1659+
1660+ let ( server_cert_chain, server_private_key) =
1661+ generate_certificate_chain_for_host ( "localhost" ) ;
1662+ let ( server_config, client_config) =
1663+ generate_tls_config ( server_cert_chain. clone ( ) , server_private_key) ;
1664+
1665+ let proxy_server = ProxyServer :: new (
1666+ Some ( OuterTlsConfig {
1667+ listen_addr : "127.0.0.1:0" ,
1668+ tls : OuterTlsMode :: Preconfigured {
1669+ server_config,
1670+ certificate_name : certificate_identity_from_chain ( & server_cert_chain) . unwrap ( ) ,
1671+ } ,
1672+ } ) ,
1673+ Some ( "127.0.0.1:0" ) ,
1674+ target_addr. to_string ( ) ,
1675+ AttestationGenerator :: with_no_attestation ( ) ,
1676+ AttestationVerifier :: mock ( ) ,
1677+ false ,
1678+ )
1679+ . await
1680+ . unwrap ( ) ;
1681+
1682+ let proxy_addr = proxy_server. local_addr ( ) . unwrap ( ) ;
1683+
1684+ tokio:: spawn ( async move {
1685+ proxy_server. accept ( ) . await . unwrap ( ) ;
1686+ } ) ;
1687+
1688+ let proxy_client = ProxyClient :: new_with_tls_config (
1689+ client_config,
1690+ "127.0.0.1:0" ,
1691+ format ! ( "localhost:{}" , proxy_addr. port( ) ) ,
1692+ AttestationGenerator :: with_no_attestation ( ) ,
1693+ AttestationVerifier :: expect_none ( ) ,
1694+ None ,
1695+ )
1696+ . await
1697+ . unwrap ( ) ;
1698+
1699+ let proxy_client_addr = proxy_client. local_addr ( ) . unwrap ( ) ;
1700+
1701+ tokio:: spawn ( async move {
1702+ proxy_client. accept ( ) . await . unwrap ( ) ;
1703+ } ) ;
1704+
1705+ let res = reqwest:: Client :: new ( )
1706+ . get ( format ! ( "http://{}" , proxy_client_addr) )
1707+ . header ( MEASUREMENT_HEADER , "{\" spoofed\" :\" request\" }" )
1708+ . header ( ATTESTATION_TYPE_HEADER , "dcap-tdx" )
1709+ . send ( )
1710+ . await
1711+ . unwrap ( ) ;
1712+
1713+ let echoed: serde_json:: Value = res. json ( ) . await . unwrap ( ) ;
1714+ assert ! ( echoed[ "measurement" ] . is_null( ) ) ;
1715+ assert ! ( echoed[ "attestation_type" ] . is_null( ) ) ;
1716+ }
1717+
16001718 // Server has mock DCAP, client has mock DCAP and client auth
16011719 #[ tokio:: test( flavor = "multi_thread" ) ]
16021720 async fn http_proxy_mutual_attestation ( ) {
@@ -2044,6 +2162,64 @@ mod tests {
20442162 assert_eq ! ( request_count. load( Ordering :: SeqCst ) , 2 ) ;
20452163 }
20462164
2165+ #[ tokio:: test( flavor = "multi_thread" ) ]
2166+ async fn http_proxy_strips_spoofed_response_measurement_header ( ) {
2167+ let target_addr = spoofed_response_measurement_service ( ) . await ;
2168+
2169+ let ( server_cert_chain, server_private_key) =
2170+ generate_certificate_chain_for_host ( "localhost" ) ;
2171+ let ( server_config, client_config) =
2172+ generate_tls_config ( server_cert_chain. clone ( ) , server_private_key) ;
2173+
2174+ let proxy_server = ProxyServer :: new (
2175+ Some ( OuterTlsConfig {
2176+ listen_addr : "127.0.0.1:0" ,
2177+ tls : OuterTlsMode :: Preconfigured {
2178+ server_config,
2179+ certificate_name : certificate_identity_from_chain ( & server_cert_chain) . unwrap ( ) ,
2180+ } ,
2181+ } ) ,
2182+ Some ( "127.0.0.1:0" ) ,
2183+ target_addr. to_string ( ) ,
2184+ AttestationGenerator :: with_no_attestation ( ) ,
2185+ AttestationVerifier :: expect_none ( ) ,
2186+ false ,
2187+ )
2188+ . await
2189+ . unwrap ( ) ;
2190+
2191+ let proxy_addr = proxy_server. local_addr ( ) . unwrap ( ) ;
2192+
2193+ tokio:: spawn ( async move {
2194+ proxy_server. accept ( ) . await . unwrap ( ) ;
2195+ } ) ;
2196+
2197+ let proxy_client = ProxyClient :: new_with_tls_config (
2198+ client_config,
2199+ "127.0.0.1:0" ,
2200+ format ! ( "localhost:{}" , proxy_addr. port( ) ) ,
2201+ AttestationGenerator :: with_no_attestation ( ) ,
2202+ AttestationVerifier :: expect_none ( ) ,
2203+ None ,
2204+ )
2205+ . await
2206+ . unwrap ( ) ;
2207+
2208+ let proxy_client_addr = proxy_client. local_addr ( ) . unwrap ( ) ;
2209+
2210+ tokio:: spawn ( async move {
2211+ proxy_client. accept ( ) . await . unwrap ( ) ;
2212+ } ) ;
2213+
2214+ let res = reqwest:: get ( format ! ( "http://{}" , proxy_client_addr) )
2215+ . await
2216+ . unwrap ( ) ;
2217+
2218+ assert_attestation_type_header ( res. headers ( ) , "none" ) ;
2219+ assert_no_measurements_header ( res. headers ( ) ) ;
2220+ assert_eq ! ( res. text( ) . await . unwrap( ) , "ok" ) ;
2221+ }
2222+
20472223 // Use HTTP 1.1
20482224 #[ tokio:: test( flavor = "multi_thread" ) ]
20492225 async fn http_proxy_with_http1 ( ) {
0 commit comments