首页 > 解决方案 > 访问 s3 资源的最安全方式是什么?

问题描述

问题标题很广泛,但我的问题不是。我只想澄清我的方法。我有一个阻止公共访问的 s3 存储桶。存储桶策略设置为 http-referer。这就是它的外观。

{
    "Version": "2008-10-17",
    "Id": "http referer policy example",
    "Statement": [
        {
            "Sid": "Allow get requests referred by www.mysite.com and mysite.com",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::storage/*",
            "Condition": {
                "StringLike": {
                    "aws:Referer": [
                        "https://www.example.com/*",
                        "https://example.com/*",
                    ]
                }
            }
        }
    ]
}

如果我的前端(在我的 tld 上)尝试通过 URL 访问 s3 资源,我仍然会收到错误消息。(网址示例 - https://storage.s3.amazonaws.com/path/to/my/file.png)。

我放弃了直接访问 s3 URL 的方法,并决定在我的 TLD 上构建一个后端实用程序,该实用程序将获取有问题的 s3 资源并将其发送回前端。所以 URL 看起来像这样,https://<tld>/fetch-s3-resource/path/to/file.png.

我想知道这种方法是否正确,或者是否有更好的方法。在我看来,即使设置 http-referer 策略也没有任何意义,因为任何人都可以通过手动将 http-referer 设置为我的 TLD 来调用我的存储桶。

更新- 我发现了应该允许用户通过 URL 公开访问资源的签名 URL。这应该可以解决我的问题,但我仍然将公共访问设置为“关闭”,而且我真的不知道要切换哪个开关以允许具有签名 URL 的用户能够访问该资源。

这是 s3 签名 URL 的示例。这是文档的链接

import logging
import boto3
from botocore.exceptions import ClientError


def create_presigned_url(bucket_name, object_name, expiration=3600):
    """Generate a presigned URL to share an S3 object

    :param bucket_name: string
    :param object_name: string
    :param expiration: Time in seconds for the presigned URL to remain valid
    :return: Presigned URL as string. If error, returns None.
    """

    # Generate a presigned URL for the S3 object
    s3_client = boto3.client('s3')
    try:
        response = s3_client.generate_presigned_url('get_object',
                                                    Params={'Bucket': bucket_name,
                                                            'Key': object_name},
                                                    ExpiresIn=expiration)
    except ClientError as e:
        logging.error(e)
        return None

    # The response contains the presigned URL
    return response

更新更新- 由于问题还不够清楚,而且我似乎对我的“松散”语言采取了一些自由,让我澄清一些事情。

1 - 我实际上想做什么?我想以这样一种方式保持我的 s3 存储桶的安全,只有具有“我”生成的预签名 URL 的用户才能访问那里的任何资源。2 - 当我问“我的方法”是否更好或者是否有任何其他方法时,我的意思是什么?我想知道是否有一种“本机”/aws 提供的方式来访问存储桶,而无需编写一个后端端点来获取资源并将其扔回前端。3 - 我如何衡量一种方法与另一种方法?我认为这一点很明显,如果您的框架提供了一个身份验证流程*,您就不要尝试从头开始编写身份验证流程*。这个逻辑在这里也适用,如果有办法访问 AWS 列出的对象,那么我可能不应该

标签: amazon-web-servicessecurityamazon-s3

解决方案


预签名的 URL 出现了。您可以阻止所有公共访问,但仍然能够生成签名 URL 并将其提供给前端。我已经在我的问题中链接了官方文档,这是我最终得到的最后一段代码。

def create_presigned_url(bucket_name, bucket_key, expiration=3600, signature_version='s3v4'):
    """Generate a presigned URL for the S3 object
    :param bucket_name: string
    :param bucket_key: string
    :param expiration: Time in seconds for the presigned URL to remain valid
    :param signature_version: string
    :return: Presigned URL as string. If error, returns None.
    """
    s3_client = boto3.client('s3',
                             aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
                             aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
                             config=Config(signature_version=signature_version),
                             region_name='us-east-1'
                             )   
    try:
        response = s3_client.generate_presigned_url('get_object',
                                                    Params={'Bucket': bucket_name,
                                                            'Key': bucket_key},
                                                    ExpiresIn=expiration)
    except ClientError as e:
        logging.error(e)
        return None
    # The response contains the pre-signed URL
    return response

推荐阅读