安全地从Amazon S3提供文件

时间:2012-08-14 16:21:50

标签: php amazon-s3

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

我创建了一个存储以下信息的db表(称为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表中检查他们是否有权访问文件。我不希望首先将文件下载到我的服务器然后发送给用户 - 我希望他们能够直接从亚马逊获取文件。

依靠非常长的hashname是否可以(任何人都无法随机猜出文件名)?在这种情况下,我可以将每个文件的ACL设置为public-read。

或者,我可以使用其他选项来提供文件,同时保密吗?

2 个答案:

答案 0 :(得分:9)

请记住,一旦链接在那里,没有什么可以阻止用户与他人共享该链接。然后,没有什么能阻止用户将文件保存到别处并共享指向文件副本的链接。

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

选项1 - 限时下载网址

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

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

选项2 - 模糊的网址

如果您重视通过Web服务器运行文件的价值超过了可能会故意共享URL(无论多么模糊)的风险,那么请使用难以猜测的链接名称。这将允许链接“永远”保持有效,这意味着链接可以“永久”共享。

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

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

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

答案 1 :(得分:2)

我只想发布带有代码的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);
}