putObject via presigned url on encrypted S3 bucket returns 签名不匹配

putObject via presigned url on encrypted S3 bucket returns signature does not match

我有无服务器 aws lambda,它将通过预先签名的 url get/put object 到加密的 S3 存储桶。 getObject 完美地工作。一旦我加密存储桶,putObject 就会生成 SignatureDoesNotMatch 错误,我不明白为什么。我玩过 headers 之类的,但仍然无法正常工作。以下代码/政策:

λ

    const generatepresignedurl = (req, res, callback) => {

    var fileurls = [];
    const body = JSON.parse(req.body);
    const theBucket = body['theBucket'];
    const theKey = body['theKey'];
    const theContentType = body['theContentType'];
    const theOperation = body['theOperation'];

    /*setting the presigned url expiry time in seconds, also check if user making the request is an authorized user
     for your app (this will be specific to your app’s auth mechanism so i am skipping it)*/
    const signedUrlExpireSeconds = 60 * 60;

    if (theOperation == 'getObject') {
        var params = {
            Bucket: theBucket,
            Key: theKey,
            Expires: signedUrlExpireSeconds
        };
    } else {
        var params = {
            Bucket: theBucket,
            Key: theKey,
            Expires: signedUrlExpireSeconds,
            ACL: 'bucket-owner-full-control',
            ContentType: theContentType,
            ServerSideEncryption: 'AES256'
        };
    }

    s3.getSignedUrl(theOperation, params, function (err, url) {
        if (err) {
            console.log('Error Getting Presigned URL from AWS S3');
            // callback(null, ({ success: false, message: 'Pre-Signed URL error', urls: fileurls }));
            callback(null, {error: err});
        }
        else {
            fileurls[0] = url;
            console.log('Presigned URL: ', fileurls[0]);
            callback(null, { success: true, message: 'AWS SDK S3 Pre-signed urls generated successfully.', urls: fileurls });
        }
    });

}

调用代码在这里:

生成预签名 Url

Function callStandAloneAWSService(lambda As String, request As String, contentType As String) As String

    Dim httpserver As New MSXML2.XMLHTTP

    With httpserver

        Dim theURL As String
        theURL = AWS_WEBSERVICE_URL_DEV

        .Open "POST", theURL & lambda 'variable that contains generatepresignedurl

        .setRequestHeader "Content-type", contentType

        .send request

        Do: DoEvents: Loop Until .readyState = 4 'make sure we are ready to recieve response

        callStandAloneAWSService = .responseText

    End With

End Function

上传到PreSigned URL(原题没有serversidencryptionheader)

Function uploadToPreSignedURL(url As String, whichFile As String, contentType) As Boolean

    'code to create binaryFile

    Dim httpserver As New MSXML2.XMLHTTP

    With httpserver

        .Open "POST", url
        .setRequestHeader "Content-type", "text/plain" 'contentType
        .send binaryFile
        Do: DoEvents: Loop Until .readyState = 4 'make sure we are ready to recieve response

        If Len(.responseText) = 0 Then

            uploadToPreSignedURL = True

        Else

            'extract error message from xml and write to report mail

        End If

    End With

End Function

存储桶策略

{
    "Version": "2012-10-17",
    "Id": "S3PolicyId1",
    "Statement": [
        {
            "Sid": "DenyIncorrectEncryptionHeader",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::mybiggetybucket/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption": "AES256"
                }
            }
        },
        {
            "Sid": "DenyUnEncryptedObjectUploads",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::mybiggetybucket/*",
            "Condition": {
                "Null": {
                    "s3:x-amz-server-side-encryption": "true"
                }
            }
        }
    ]
}

FWIW,我可以通过 aws cli 运行 并让它运行:

aws s3api put-object --bucket mybiggetybucket --key test.json --body package-lock.json --server-side-encryption "AES256"

引用 Server Side Encryption 文档:

You can't enforce SSE-S3 encryption on objects that are uploaded using presigned URLs. You can specify server-side encryption only with the AWS Management Console or an HTTP request header. For more information, see Specifying Conditions in a Policy.

我能够得到类似的东西。我需要使用 PUT 而不是 POST,并且我需要提供 x-amz-server-side-encryption:AES256 作为 header,如下所示:

const axios = require('axios');
const AWS = require('aws-sdk');

const s3 = new AWS.S3();

const params = {
  Bucket: 'mybucket',
  Key: 'myfolder/myfile.txt',
  Expires: 60 * 60,
  ACL: 'bucket-owner-full-control',
  ContentType: 'text/plain',
  ServerSideEncryption: 'AES256',
};

const axiosConfig = {
  headers: {
    'Content-Type': 'text/plain',
    'x-amz-server-side-encryption': 'AES256',
  },
};

const uploadTextFile = (presignedurl) => {
  axios.put(presignedurl, 'some text here', axiosConfig).then((res) => {
    console.log('File uploaded');
  }).catch((error) => {
    console.error(error);
  });
};

s3.getSignedUrl('putObject', params, (err, url) => {
  if (err) {
    console.error(err);
  } else {
    console.log('Pre-signed URL:', url);
    uploadTextFile(url);
  }
});