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

Ole*_*gas 146 javascript authentication amazon amazon-s3 amazon-web-services

我正在使用JavaScript,通过REST API实现从客户端计算机到Amazon S3的直接文件上传,没有任何服务器端代码.一切正常,但有一件事令我担心......

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

我怎么处理这个?这是一个问题吗?也许我可以将特定的私钥使用仅限于来自特定CORS Origin的REST API调用,仅限于PUT和POST方法,或者可能只将链接键连接到S3和特定存储桶?可能还有其他认证方法吗?

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

sec*_*ike 203

我想你想要的是使用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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

进出服务器的唯一数据是签名的URL.您的密钥在服务器上保密.

  • 请注意,这使用了Signature v2,很快将被v4取代:http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html (14认同)
  • 请非常肯定地`$ {文件名}`添加键名,所以上面的例子中,`用户/埃里克/ $ {文件名}`,而不是仅仅`用户/ eric`.如果`用户/ eric`是一个已经存在的文件夹,上载将失败默默(你甚至会被重定向到success_action_redirect)和上传的内容不会在那里.花了几个小时调试这个想法是一个权限问题. (8认同)

Joo*_*ler 37

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

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

也试试这段代码

只需更改Region,IdentityPoolId和您的存储桶名称即可

<!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>
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请检查 - Github


Bra*_*ncy 16

你说你想要一个"无服务器"的解决方案.但这意味着您无法将任何"您的"代码放入循环中.(注意:一旦您将代码提供给客户端,它现在就是"他们的"代码.)锁定CORS无济于事:人们可以轻松编写一个非基于Web的工具(或基于Web的代理)正确的CORS标头滥用您的系统.

最大的问题是你无法区分不同的用户.您不能允许一个用户列出/访问他的文件,但阻止其他用户这样做.如果您发现滥用行为,除了更改密钥外,您无能为力.(攻击者可能会再次获得该攻击者.)

您最好的选择是为您的javascript客户端创建一个带有密钥的"IAM用户".只给它一个桶的写访问权限.(但理想情况下,不要启用ListBucket操作,这会使攻击者更具吸引力.)

如果您有一台服务器(即使是每月20美元的简单微型实例),您也可以在监控/防止实时滥用的同时对服务器上的密钥进行签名.如果没有服务器,您可以做的最好的事情是在事后定期监控滥用情况.这就是我要做的事情:

1)定期旋转该IAM用户的密钥:每晚为该IAM用户生成一个新密钥,并替换最旧的密钥.由于有2个密钥,每个密钥有效期为2天.

2)启用S3日志记录,并每小时下载日志.设置"过多上传"和"过多下载"的提醒.您需要检查文件总大小和上传的文件数.并且您将要监控全局总数以及每IP地址总数(具有较低阈值).

这些检查可以"无服务器"完成,因为您可以在桌面上运行它们.(即S3完成所有的工作,这些过程就在那里,提醒您滥用您的S3存储的,这样你就不会得到一个巨人在月底AWS账单.)

  • 伙计,我忘记了Lambda之前的事情有多么复杂. (2认同)

Raj*_*evJ 10

在接受的答案中添加更多信息,您可以使用AWS Signature第4版参考我的博客,查看代码的运行版本.

将在此总结:

用户选择要上载的文件后,请执行以下操作:1.调用Web服务器以启动服务以生成所需的参数

  1. 在此服务中,调用AWS IAM服务以获得临时信誉

  2. 获得cred后,创建一个存储桶策略(base 64编码的字符串).然后使用临时秘密访问密钥对存储桶策略进行签名,以生成最终签名

  3. 将必要的参数发送回UI

  4. 收到后,创建一个html表单对象,设置所需的参数并发布它.

有关详细信息,请参阅 https://wordpress1763.wordpress.com/2016/10/03/browser-based-upload-aws-signature-version-4/

  • 我花了一整天的时间试图用Javascript来解决这个问题,这个答案告诉我如何使用XMLhttprequest来做到这一点.你很失望,我很惊讶.OP要求javascript并在推荐的答案中获得表单.好悲伤.谢谢你的回答! (5认同)