Closed nitisht closed 6 years ago
x-amz-contentsha256
in signed headers
http://s3.amazonaws.com/aws-sdk-php-5a0aeaaedaad0/obj1?X-Amz-Content-Sha256=invalid-sha256&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=**REDACTED**%2F20171114%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20171114T130808Z&X-Amz-SignedHeaders=host&X-Amz-Expires=1200&X-Amz-Signature=**REDACTED**
<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client; use Aws\Credentials; use Aws\Exception\AwsException; use GuzzleHttp\Psr7; use GuzzleHttp\Client;
$s3Client = new S3Client([ 'credentials' => [ 'key' => 'accessKey', 'secret' => 'secretKey', ], 'scheme' => 'http', 'use_path_style_endpoint' => true, 'region' => 'us-east-1', 'version' => '2006-03-01' ]); $bucket = 'mybucket'; $object = 'myobject'; $sha256 = "invalid-sha256";
$cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => $bucket, 'Key' => $object, 'ContentSHA256' => $sha256, ]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes'); $presignedUrl = (string) $request->getUri(); echo $presignedUrl;
$client = new Client(['debug' => true]); $body = "Hello, World"; $res = $client->request('PUT', $presignedUrl, ['body' => $body]); echo 'statusCode: ' . $res->getStatusCode();
?>
### Resolution
1. aws-sdk-php mint test
This test doesn't capture a [valid use-case](http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html), nor is it a negative test-case. `ContentSha256` is ignored in a presigned PUT request. To quote from AWS S3 docs,
>You don't include a payload hash in the Canonical Request, because when you create a presigned URL, you don't know the payload content because the URL is used to upload an arbitrary payload. Instead, you use a constant string UNSIGNED-PAYLOAD.
2. minio-server
Minio server should not validate content-sha256 for presigned requests.
We should change the test to incorporate this perhaps
package main() {
//creds := credentials.NewStaticCredentials("USWUXHGYZQYFYFFIT3RE", "MOJRH0mkL1IPauahWITSVvyDrQbEEIwljvmxdq03", "")
creds := credentials.NewStaticCredentials(os.Getenv("ACCESS_KEY"), os.Getenv("SECRET_KEY"), "")
newSession := session.New()
s3Config := &aws.Config{
Credentials: creds,
//Endpoint: aws.String("http://localhost:9000"),
Endpoint: aws.String("https://s3.amazonaws.com"),
Region: aws.String("us-east-1"),
//DisableSSL: aws.Bool(true),
//S3ForcePathStyle: aws.Bool(true),
}
// Create an S3 service object in the default region.
s3Client := s3.New(newSession, s3Config)
req, _ := s3Client.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String("vadmeste"),
Key: aws.String("object"),
ContentType: aws.String("application/octet-stream"),
})
req.HTTPRequest.Header.Set("Content-Md5", "82bb413746aee42f89dea2b59614f9ef")
url, err := req.Presign(24 * time.Hour)
if err != nil {
panic(err)
}
rreq, err := http.NewRequest("PUT", url, bytes.NewReader([]byte("")))
rreq.Header.Add("Content-Type", "application/octet-stream")
rreq.Header.Set("Content-Md5", "82bb413746aee42f89dea2b59614f9ef")
log.Println("REQUEST PUT ", url)
for k, v := range rreq.Header {
log.Printf("%v: %v", k, v)
}
resp, err := http.DefaultClient.Do(rreq)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
panic(err)
}
log.Println("AMZ RESPONSE")
for k, v := range resp.Header {
log.Printf("%v: %v", k, v)
}
log.Printf("\n%v", string(body))
}
$ go run put-object-presign-content-type.go
2017/11/14 10:14:17 REQUEST PUT https://vadmeste.s3.amazonaws.com/object?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJY2EP7JTV2SUOH4Q%2F20171114%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20171114T181417Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=content-md5%3Bcontent-type%3Bhost&X-Amz-Signature=1c1dec7ce9531106bd437d8850589182ff9ae3b561d654b43133a732130d73e4
2017/11/14 10:14:17 Content-Md5: [82bb413746aee42f89dea2b59614f9ef]
2017/11/14 10:14:17 Content-Type: [application/octet-stream]
2017/11/14 10:14:18 AMZ RESPONSE
2017/11/14 10:14:18 Server: [AmazonS3]
2017/11/14 10:14:18 X-Amz-Request-Id: [2F8B8FA82C2BBECB]
2017/11/14 10:14:18 X-Amz-Id-2: [C8t+C+nOXAaALT+uFgarF6q+ALJJUP8Xz1l0PPCK92vtHWdZRvZd+dwGzd5DZcywS24DTNVwyg0=]
2017/11/14 10:14:18 Content-Type: [application/xml]
2017/11/14 10:14:18 Date: [Tue, 14 Nov 2017 18:14:18 GMT]
2017/11/14 10:14:18
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>InvalidDigest</Code><Message>The Content-MD5 you specified was invalid.</Message><Content-MD5>82bb413746aee42f89dea2b59614f9ef</Content-MD5><RequestId>2F8B8FA82C2BBECB</RequestId><HostId>C8t+C+nOXAaALT+uFgarF6q+ALJJUP8Xz1l0PPCK92vtHWdZRvZd+dwGzd5DZcywS24DTNVwyg0=</HostId></Error>
aws-sdk-php mint test
This test does capture a valid yet uncommon use-case where the presigned URL provider wishes to control the content to match the given sha256 sum. But aws-sdk-php doesn't include the x-amz-content-sha256 in signed headers. i.e, not including X-Amz-Content-Sha256
in presigned request header doesn't fail with SignatureDoesNotMatch
. I.e, when this test is run on Minio server/gateway it would return different error code than when run on AWS S3. So, it makes sense to add an equivalent test using aws-sdk-go and remove this test.
minio-server
Minio server should return ErrContentSHA256Mismatch if an invalid hex value is set in X-Amz-Content-Sha256
header.
This PR https://github.com/minio/mint/pull/222 is needed as well
Mint test on latest rc failed. Reproducible with both Server and Gateway modes
Mint logs:
Server logs: