从客户端浏览器直接上传 Amazon S3 文件 - 私有密钥泄露

我正在仅使用 JavaScript 通过 REST API 实现从客户端计算机到 Amazon S3 的直接文件上传,而无需任何服务器端代码。一切都很好,但有一件事让我担心...

当我向 Amazon S3 REST API 发送请求时,我需要对请求进行签名并将签名放入标头中。要创建签名,我必须使用我的密钥。但是所有事情都发生在客户端,因此,密钥可以很容易地从页面源代码中显示(即使我对源代码进行模糊/加密)。Authentication

我该如何处理?这到底是个问题吗?也许我可以将特定的私钥使用限制为仅来自特定 CORS 源的 REST API 调用,并且仅限于 PUT 和 POST 方法,或者可能仅将密钥链接到 S3 和特定存储桶?可能还有另外一种认证方法?

“无服务器”解决方案是理想的,但我可以考虑涉及一些服务器端处理,不包括将文件上传到我的服务器,然后发送到S3。


答案 1

我认为你想要的是使用POST的基于浏览器的上传。

基本上,您确实需要服务器端代码,但它所做的只是生成签名策略。客户端代码具有签名策略后,可以使用 POST 直接上传到 S3,而无需数据通过服务器。

这是官方文档链接:

图:http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html

示例代码:http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

签名的策略将以如下形式进入您的 html:

<html>
  <head>
    ...
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    ...
  </head>
  <body>
  ...
  <form action="http://johnsmith.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
    Key to upload: <input type="input" name="key" value="user/eric/" /><br />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="success_action_redirect" value="http://johnsmith.s3.amazonaws.com/successful_upload.html" />
    Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br />
    <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
    Tags for File: <input type="input" name="x-amz-meta-tag" value="" /><br />
    <input type="hidden" name="AWSAccessKeyId" value="AKIAIOSFODNN7EXAMPLE" />
    <input type="hidden" name="Policy" value="POLICY" />
    <input type="hidden" name="Signature" value="SIGNATURE" />
    File: <input type="file" name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>
  ...
</html>

请注意,FORM 操作是将文件直接发送到 S3,而不是通过您的服务器。

每当您的某个用户想要上传文件时,您都会在服务器上创建 和。将页面返回到用户的浏览器。然后,用户可以将文件直接上传到 S3,而无需通过您的服务器。POLICYSIGNATURE

对策略进行签名时,通常会使策略在几分钟后过期。这将强制您的用户在上传之前与您的服务器通信。这样,您就可以根据需要监控和限制上传。

进入或传出服务器的唯一数据是签名 URL。您的密钥在服务器上保持机密。


答案 2

您可以通过 AWS S3 Cognito 执行此操作,请尝试以下链接:

http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-examples.html#Amazon_S3

也试试这个代码

只需更改区域、身份池 ID 和您的存储桶名称

<!DOCTYPE html>
<html>

<head>
    <title>AWS S3 File Upload</title>
    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.12.min.js"></script>
</head>

<body>
    <input type="file" id="file-chooser" />
    <button id="upload-button">Upload to S3</button>
    <div id="results"></div>
    <script type="text/javascript">
    AWS.config.region = 'your-region'; // 1. Enter your region

    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: 'your-IdentityPoolId' // 2. Enter your identity pool
    });

    AWS.config.credentials.get(function(err) {
        if (err) alert(err);
        console.log(AWS.config.credentials);
    });

    var bucketName = 'your-bucket'; // Enter your bucket name
    var bucket = new AWS.S3({
        params: {
            Bucket: bucketName
        }
    });

    var fileChooser = document.getElementById('file-chooser');
    var button = document.getElementById('upload-button');
    var results = document.getElementById('results');
    button.addEventListener('click', function() {

        var file = fileChooser.files[0];

        if (file) {

            results.innerHTML = '';
            var objKey = 'testing/' + file.name;
            var params = {
                Key: objKey,
                ContentType: file.type,
                Body: file,
                ACL: 'public-read'
            };

            bucket.putObject(params, function(err, data) {
                if (err) {
                    results.innerHTML = 'ERROR: ' + err;
                } else {
                    listObjs();
                }
            });
        } else {
            results.innerHTML = 'Nothing to upload.';
        }
    }, false);
    function listObjs() {
        var prefix = 'testing';
        bucket.listObjects({
            Prefix: prefix
        }, function(err, data) {
            if (err) {
                results.innerHTML = 'ERROR: ' + err;
            } else {
                var objKeys = "";
                data.Contents.forEach(function(obj) {
                    objKeys += obj.Key + "<br>";
                });
                results.innerHTML = objKeys;
            }
        });
    }
    </script>
</body>

</html>
有关更多详细信息,请检查 - Github