44use Aws \Credentials \CredentialsInterface ;
55use AWS \CRT \Auth \Signable ;
66use AWS \CRT \Auth \SignatureType ;
7- use AWS \CRT \Auth \SignedBodyHeaderType ;
87use AWS \CRT \Auth \Signing ;
98use AWS \CRT \Auth \SigningAlgorithm ;
109use AWS \CRT \Auth \SigningConfigAWS ;
@@ -49,7 +48,6 @@ protected function getHeaderBlacklist()
4948 {
5049 return [
5150 'cache-control ' => true ,
52- 'content-type ' => true ,
5351 'content-length ' => true ,
5452 'expect ' => true ,
5553 'max-forwards ' => true ,
@@ -67,13 +65,31 @@ protected function getHeaderBlacklist()
6765 'from ' => true ,
6866 'referer ' => true ,
6967 'user-agent ' => true ,
70- 'X-Amz-User-Agent ' => true ,
7168 'x-amzn-trace-id ' => true ,
7269 'aws-sdk-invocation-id ' => true ,
7370 'aws-sdk-retry ' => true ,
7471 ];
7572 }
7673
74+ /**
75+ * Headers that must be excluded from presigned URLs (in addition to
76+ * the regular header blacklist). These headers are excluded because the
77+ * consumer of the presigned URL cannot reliably reproduce them at the
78+ * time the URL is used:
79+ * - content-type: caller-determined at upload time
80+ * - x-amz-user-agent: specific to the SDK that generated the URL
81+ *
82+ * @return array
83+ */
84+ protected function getPresignHeaderDenyList ()
85+ {
86+ return [
87+ 'content-type ' => true ,
88+ 'x-amz-user-agent ' => true ,
89+ ];
90+ }
91+
92+
7793 /**
7894 * @param string $service Service name to use when signing
7995 * @param string $region Region name to use when signing
@@ -397,15 +413,16 @@ private function convertExpires($expiresTimestamp, $startTimestamp)
397413
398414 private function moveHeadersToQuery (array $ parsedRequest )
399415 {
400- //x-amz-user-agent shouldn't be put in a query param
401- unset( $ parsedRequest [ ' headers ' ][ ' X-Amz-User-Agent ' ]) ;
416+ $ presignDenyList = $ this -> getPresignHeaderDenyList ();
417+ $ blacklist = $ this -> getHeaderBlacklist () + $ presignDenyList ;
402418
403419 foreach ($ parsedRequest ['headers ' ] as $ name => $ header ) {
404420 $ lname = strtolower ($ name );
405- if (substr ($ lname , 0 , 5 ) == 'x-amz ' ) {
421+ // Move x-amz-* headers into the query string, but skip those that
422+ // must not appear in presigned URLs (e.g. x-amz-user-agent).
423+ if (substr ($ lname , 0 , 5 ) == 'x-amz ' && !isset ($ presignDenyList [$ lname ])) {
406424 $ parsedRequest ['query ' ][$ name ] = $ header ;
407425 }
408- $ blacklist = $ this ->getHeaderBlacklist ();
409426 if (isset ($ blacklist [$ lname ])
410427 || $ lname === strtolower (self ::AMZ_CONTENT_SHA256_HEADER )
411428 ) {
@@ -416,6 +433,7 @@ private function moveHeadersToQuery(array $parsedRequest)
416433 return $ parsedRequest ;
417434 }
418435
436+
419437 private function parseRequest (RequestInterface $ request )
420438 {
421439 // Clean up any previously set headers.
@@ -479,7 +497,7 @@ private function removeIllegalV4aHeaders(&$request)
479497 'aws-sdk-invocation-id ' ,
480498 'aws-sdk-retry ' ,
481499 'x-amz-region-set ' ,
482- 'transfer-encoding '
500+ 'transfer-encoding ' ,
483501 ];
484502 $ storedHeaders = [];
485503
@@ -569,13 +587,15 @@ protected function presignWithV4a(
569587 ]);
570588
571589 $ this ->removeIllegalV4aHeaders ($ request );
572- foreach ($ this ->getHeaderBlacklist () as $ headerName => $ headerValue ) {
590+ $ denyList = $ this ->getHeaderBlacklist () + $ this ->getPresignHeaderDenyList ();
591+ foreach ($ denyList as $ headerName => $ headerValue ) {
573592 if ($ request ->hasHeader ($ headerName )) {
574593 $ request = $ request ->withoutHeader ($ headerName );
575594 }
576595 }
577596
578597 $ http_request = $ this ->CRTRequestFromGuzzleRequest ($ request );
598+
579599 Signing::signRequestAws (
580600 Signable::fromHttpRequest ($ http_request ),
581601 $ signingConfig , function ($ signing_result , $ error_code ) use (&$ http_request ) {
0 commit comments