我想允许iPhone应用的用户上传照片并使用Amazon S3。我有两种方法可以解决这个问题:
对于选项1,安全性很简单。我永远不必告诉iPhone我的S3秘密。缺点是所有内容都通过我们的服务器代理上传,这有点挫败了进入S3的目的。
对于选项2,理论上它更好,但问题是如何在不给我秘密的情况下将iPhone(或不同平台上的任何移动应用程序)写入我的S3存储桶?另外,我不确定这是否是一个好的设计,因为流程将是:iphone上传到S3,获取URL,然后告诉服务器URL是什么,以便它可以将它添加到我们的数据库以引用未来。但是,由于通信被分成两条腿(iphone-> S3 vs iPhone-> My-Server),因此它将其作为非原子操作而变得脆弱。
我发现了一些使用Browser-based Uploads using POST引用的旧信息,但不确定这是否仍然是推荐的方法。我希望有一个更好的解决方案,我们可以使用REST API而不是依赖POST。我也看到了AWS iOS Beta SDK,但他们的文档没有多大帮助,我发现Amazon article同样无益,因为它会警告你不要做什么,但是没有告诉你另一种方法:
移动AWS软件开发工具包对API进行签名 发送到Amazon Web Services的请求 (AWS)为了验证 制作的AWS账户的身份 请求。否则,是恶意的 开发人员可以轻松提出请求 到另一个开发人员的基础设施。 请求使用AWS签署 访问密钥ID和秘密访问密钥 AWS提供的。秘密通道 密钥类似于密码,它 保守秘密非常重要。
提示:您可以查看所有AWS 安全凭证,包括Access 密钥ID和秘密访问密钥,在 AWS网站在 http://aws.amazon.com/security-credentials
在源代码中嵌入凭据 对软件有问题,包括 移动应用,因为恶意 用户可以解压缩软件或 查看源代码以检索 秘密访问密钥。
有没有人对最佳建筑设计和流程有任何建议?
更新:我越深入研究,似乎有很多人建议将HTTP POST方法与json策略文件一起使用,如下所述:http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?UsingHTTPPOST.html。
有了这个,流程就像(1)iPhone向我的服务器发出请求,请求策略文件(2)服务器生成json策略文件并返回给客户端(3)iPhone执行HTTP POST照片+ json S3的政策。我讨厌我以一种显而易见的方式使用HTTP POST,但它看起来更好,因为它根本不需要我的服务器来存储照片。
答案 0 :(得分:10)
我之前讨论过这个问题on the AWS forums。正如我所说,从移动设备访问AWS的正确解决方案是使用AWS Identity and Access Management服务为每个用户生成临时的,有限权限的访问密钥。该服务非常棒,但它目前仍处于测试阶段,而且还不是移动SDK的一部分。我有一种感觉,一旦这个东西发布好了,你会立即在移动SDK上看到它。
在此之前,generate presigned URLs为您的用户提供服务,或通过您自己的服务器代理,就像其他人所建议的那样。预先签名的URL将允许您的用户临时GET或PUT到您的一个存储桶中的S3对象,而不实际拥有您的凭据(它们被哈希到签名中)。您可以阅读详细信息here。
编辑:我已经使用IAM的预览测试版实现了针对此问题的正确解决方案。它是GitHub上的available,你可以read about it here。
答案 1 :(得分:5)
您可以使用REST API直接从iPhone上传到S3,并让服务器负责生成需要密钥的Authorization
标头值的一部分。这样,您就不会冒险将访问密钥暴露给拥有越狱iPhone的任何人,而您不会将文件上载的负担放在服务器上。可以在"Example Object PUT" of "Signing and Authenticating REST Requests"下找到要求的确切详细信息。我强烈建议阅读该文档,然后再继续。
以下代码(用Python编写)生成从{3}}标头值中派生自S3秘密访问密钥的部分。您应该分别用下面的Authorization
和_S3_SECRET
替换您自己的秘密访问密钥和bucket name in virtual host form:
_S3_BUCKET_NAME
使用文件名import base64
from datetime import datetime
import hmac
import sha
# Replace these values.
_S3_SECRET = "my-s3-secret"
_S3_BUCKET_NAME = "my-bucket-name"
def get_upload_header_values(content_type, filename):
now = datetime.utcnow()
date_string = now.strftime("%a, %d %b %Y %H:%M:%S +0000")
full_pathname = '/%s/%s' % (_S3_BUCKET_NAME, filename)
string_to_sign = "PUT\n\n%s\n%s\n%s" % (
content_type, date_string, full_pathname)
h = hmac.new(_S3_SECRET, string_to_sign, sha)
auth_string = base64.encodestring(h.digest()).strip()
return (date_string, auth_string)
和内容类型foo.txt
调用此选项会产生:
text/plain
请注意,如果您运行此代码,则返回的时间将不同,因此编码的HMAC摘要将不同。
现在,iPhone客户端只需使用返回的日期和HMAC摘要向S3发出PUT请求。假设
>>> get_upload_header_values('text/plain', 'foo.txt')
('Wed, 06 Feb 2013 00:57:45 +0000', 'EUSj3g70aEsEqSyPT/GojZmY8eI=')
date_string
和auth_string
serverJson
kS3AccessKey
)名为my-bucket-name
kS3BucketName
NSData
对象中进行编组
data
filename
然后,您可以执行以下操作来创建contentType
:
NSURLRequest
接下来,您可以发出请求。如果您使用了优秀的AFNetworking library,则可以使用XMLDocumentRequestOperationWithRequest:success:failure:
将NSString *serverDate = [serverJson objectForKey:@"date"]
NSString *serverHmacDigest = [serverJson objectForKey:@"hmacDigest"]
// Create the headers.
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
[headers setObject:contentType forKey:@"Content-Type"];
NSString *host = [NSString stringWithFormat:@"%@.s3.amazonaws.com", kS3BucketName]
[headers setObject:host forKey:@"Host"];
[headers setObject:serverDate forKey:@"Date"];
NSString *authorization = [NSString stringWithFormat:@"AWS %@:%@", kS3AccessKey, serverHmacDigest];
[headers setObject:authorization forKey:@"Authorization"];
// Create the request.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:data];
[request setHTTPMethod:@"PUT"];
NSString *postUrl = [NSString stringWithFormat:@"http://%@.s3.amazonaws.com/%@", kS3BucketName, filename];
[request setURL:[NSURL URLWithString:postUrl]];
包裹在request
对象中,然后调用其AFXMLRequestOperation
方法。完成后不要忘记发布start
和headers
。
请注意,客户端从服务器获取request
标头的值。这是因为正如亚马逊在"Time Stamp Requirement"下所描述的那样:
“对于经过身份验证的请求,必须使用有效的时间戳(使用HTTP日期标头或x-amz-date备用方案)。此外,经过身份验证的请求中包含的客户端时间戳必须在亚马逊的15分钟内收到请求时的S3系统时间。如果没有,请求将因RequestTimeTooSkewed错误状态代码而失败。“
因此,不依赖于具有正确时间的客户端以使请求成功,而是依赖服务器,该服务器应该使用NTP(以及Date
之类的守护进程。)
答案 2 :(得分:4)
上传到您的服务器,然后发布到S3。从架构的角度来看,您需要从服务器执行此操作。在数据传输过程中可能出现许多问题,您可以在服务器上更好地处理,如果您想要存储有关您发送给S3的图像的任何数据,您可能会进行服务器端呼叫。
另外,您的秘密访问密钥存储在更安全的环境中。你自己的。
如果您担心可扩展性并且您将要进行大量的S3传输,我会考虑托管您的服务器和EC2实例。在两者之间传输数据是免费的(假设您将数据存储在以下数据中心)。
对于在同一地区内的Amazon EC2和Amazon S3之间传输的数据,或者对于Amazon EC2 Northern Virginia Region和Amazon S3 US Standard Region之间传输的数据,不收取数据传输费用。“Amazon Simple Storage Service (Amazon S3)
关于使用EC2的利弊,SO Amazon - EC2 cost?(示例)上有很多帖子。
答案 3 :(得分:0)
答案 4 :(得分:0)
他们可能提供了sdk,目的是申请可能允许对个人s3帐户进行身份验证?例如,一个应用程序,允许用户将文件存储在他们自己(用户)的存储桶而不是提供商?我觉得将密钥与应用程序合并并分发它存在安全漏洞。任何人都可以(错误)使用它们一旦钥匙被揭示(当你放弃它时它永远不会安全)。另一方面,保留服务器保留的功能将使您的密钥对用户透明,不是吗?