8

我有一个将用户文件上传到 S3 的应用程序。目前,文件夹和文件的 ACL 设置为私有。

我创建了一个存储以下信息的数据库表(称为 docs):

id
user_id
file_name (original file as specified by the user)
hash_name (random hash used to save the file on amazon)

因此,当用户想要下载文件时,我首先在 db 表中检查他们有权访问文件。我不希望先将文件下载到我的服务器然后发送给用户——我希望他们能够直接从亚马逊获取文件。

依赖非常长的哈希名是否可以(使任何人基本上不可能随机猜测文件名)?在这种情况下,我可以将每个文件的 ACL 设置为公共读取。

或者,我可以使用其他选项来提供文件同时保持它们的私密性吗?

4

2 回答 2

10

请记住,一旦链接出现,没有什么能阻止用户与其他人共享该链接。再说一次,没有什么能阻止用户将文件保存在其他地方并共享指向文件副本的链接。

最佳方法取决于您的具体需求。

选项 1 - 限时下载 URL

如果适用于您的方案,您还可以创建指向 S3 内容的过期(限时)自定义链接。这将允许用户在有限的时间内下载内容,之后他们必须获得一个新的链接。

http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html

选项 2 - 混淆 URL

如果您重视避免通过您的 Web 服务器运行该文件,而不考虑有意共享一个 URL(无论多么晦涩)的风险,那么请使用难以猜测的链接名称。这将允许链接“永远”保持有效,这意味着该链接可以“永远”共享。

选项 3 - 通过您的服务器下载

如果您担心链接被共享,并且肯定希望用户通过您的网站进行身份验证,则在验证用户凭据后通过您的网站提供内容。

此选项还允许链接“永远”保持有效,但需要用户登录(或者可能只是在浏览器中有一个身份验证 cookie)才能访问该链接。

于 2012-08-14T16:27:54.400 回答
3

如果有人有同样的问题,我只想用代码发布 PHP 解决方案。

这是我使用的代码:

$aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE';
$aws_secret_key = 'YourSecretKey12345';
$aws_bucket = 'bucket';
$file_path = 'directory/image.jpg';
$timeout = '+10 minutes';

// get the URL!
$url = get_public_url($aws_access_key_id,$aws_secret_key,$aws_bucket,$file_path,$timeout);

// print the URL!
echo($url);



function get_public_url($keyID, $s3Key, $bucket, $filepath, $timeout)
{
    $expires = strtotime($timeout);
    $stringToSign = "GET\n\n\n{$expires}\n/{$aws_bucket}/{$file_path}";       
    $signature = urlencode(hex2b64(hmacsha1($s3Key, utf8_encode($stringToSign))));

    $url = "https://{$bucket}.s3.amazonaws.com/{$file_path}?AWSAccessKeyId={$keyID}&Signature={$signature}&Expires={$expires}";
    return $url;
}

function hmacsha1($key,$data)
{
    $blocksize=64;
    $hashfunc='sha1';
    if (strlen($key)>$blocksize)
        $key=pack('H*', $hashfunc($key));
    $key=str_pad($key,$blocksize,chr(0x00));
    $ipad=str_repeat(chr(0x36),$blocksize);
    $opad=str_repeat(chr(0x5c),$blocksize);
    $hmac = pack(
        'H*',$hashfunc(
            ($key^$opad).pack(
                'H*',$hashfunc(
                    ($key^$ipad).$data

                    )
                )
            )
        );
    return bin2hex($hmac);
}

function hex2b64($str)
{
  $raw = '';
  for ($i=0; $i < strlen($str); $i+=2)
  {
      $raw .= chr(hexdec(substr($str, $i, 2)));
  }
  return base64_encode($raw);
}
于 2013-10-23T23:01:38.620 回答