diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c1389a7463..35349116c0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,12 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" + [[aws-sdk-rust]] + message = "Automatically exclude X-Ray trace ID headers and authorization headers from SigV4 canonical request calculations." + references = ["smithy-rs#2815"] + meta = { "breaking" = false, "tada" = false, "bug" = true } + author = "relevantsam" + [[aws-sdk-rust]] message = "Add accessors to Builders" references = ["smithy-rs#2791"] diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs index f7c2692a37..221463ada2 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs @@ -774,14 +774,46 @@ mod tests { assert_eq!(creq.values.signed_headers().as_str(), "host;x-amz-date"); } - // It should exclude user-agent and x-amz-user-agent headers from presigning + // It should exclude authorization, user-agent, x-amzn-trace-id headers from presigning + #[test] + fn non_presigning_header_exclusion() { + let request = http::Request::builder() + .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") + .header("content-type", "application/xml") + .header("content-length", "0") + .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") + .header("x-amz-user-agent", "test-user-agent") + .body("") + .unwrap(); + let request = SignableRequest::from(&request); + + let settings = SigningSettings { + signature_location: SignatureLocation::Headers, + ..Default::default() + }; + + let signing_params = signing_params(settings); + let canonical = CanonicalRequest::from(&request, &signing_params).unwrap(); + + let values = canonical.values.as_headers().unwrap(); + assert_eq!( + "content-length;content-type;host;x-amz-date;x-amz-user-agent", + values.signed_headers.as_str() + ); + } + + // It should exclude authorization, user-agent, x-amz-user-agent, x-amzn-trace-id headers from presigning #[test] fn presigning_header_exclusion() { let request = http::Request::builder() .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") .header("content-type", "application/xml") .header("content-length", "0") .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") .header("x-amz-user-agent", "test-user-agent") .body("") .unwrap(); diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs index bac85281df..501cd5c775 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use http::header::{HeaderName, USER_AGENT}; +use http::header::{HeaderName, AUTHORIZATION, USER_AGENT}; use std::time::Duration; /// HTTP signing parameters pub type SigningParams<'a> = crate::SigningParams<'a, SigningSettings>; +const HEADER_NAME_X_RAY_TRACE_ID: &str = "x-amzn-trace-id"; + /// HTTP-specific signing settings #[derive(Debug, PartialEq)] #[non_exhaustive] @@ -97,15 +99,30 @@ pub enum SessionTokenMode { impl Default for SigningSettings { fn default() -> Self { - // The user agent header should not be signed because it may be altered by proxies - const EXCLUDED_HEADERS: [HeaderName; 1] = [USER_AGENT]; - + // Headers that are potentially altered by proxies or as a part of standard service operations. + // Reference: + // Go SDK: + // Java SDK: + // JS SDK: + // There is no single source of truth for these available, so this uses the minimum common set of the excluded options. + // Instantiate this every time, because SigningSettings takes a Vec (which cannot be const); + let excluded_headers = Some( + [ + // This header is calculated as part of the signing process, so if it's present, discard it + AUTHORIZATION, + // Changes when sent by proxy + USER_AGENT, + // Changes based on the request from the client + HeaderName::from_static(HEADER_NAME_X_RAY_TRACE_ID), + ] + .to_vec(), + ); Self { percent_encoding_mode: PercentEncodingMode::Double, payload_checksum_kind: PayloadChecksumKind::NoHeader, signature_location: SignatureLocation::Headers, expires_in: None, - excluded_headers: Some(EXCLUDED_HEADERS.to_vec()), + excluded_headers, uri_path_normalization_mode: UriPathNormalizationMode::Enabled, session_token_mode: SessionTokenMode::Include, }