有关从iPhone应用程序和S3上传照片的build筑和devise问题

我希望允许iPhone应用的用户上传照片并使用Amazon S3。 有两种方法可以看到这个:

  1. 从iPhone上传到我的服务器,然后将其代理到Amazon S3。
  2. 从iPhone直接上传到S3

对于选项1,安全性很简单。 我永远不必告诉iPhone我的S3秘密。 不利的一面是所有的东西都是通过我们的服务器代理上传的,这种打破了去S3的目的。

对于选项2,从理论上来说它更好,但是问题是如何让iPhone(或其他平台上的任何移动应用程序)能够写入我的S3存储区而不会泄露我的秘密? 另外,我不确定这是不是一个好的devise,因为stream程将会是:iphone上传到S3,获取URL,然后告诉服务器URL是什么,这样它可以将它添加到我们的数据库中引用未来。 但是,由于通信分为两段(iphone-> S3 vs iPhone-> My-Server),所以它将其作为非primefaces操作保留下来。

我发现一些较旧的信息,使用基于浏览器的上传引用,但不确定如果这仍然是推荐的方法。 我希望有一个更好的解决scheme,我们可以使用REST API而不是依靠POST。 我也看到了AWS iOS Beta SDK ,但是他们的文档没有什么帮助,而且我发现亚马逊的文章同样没有帮助,因为它告诫你不要做什么,但是不会告诉你另一种方法:

移动AWS开发工具包签署发送到amazon web services(AWS)的API请求,以validation提出请求的AWS账户的身份。 否则,恶意开发者可能很容易向另一个开发者的基础架构发出请求。 这些请求使用AWS提供的AWS访问密钥ID和密钥访问密钥进行签名。 秘密访问密钥类似于密码,保密是非常重要的。

提示:您可以在AWS网站http://aws.amazon.com/security-credentials上查看所有AWS安全证书,包括访问密钥ID和机密访问密钥。

在源代码中embedded凭证对于软件(包括移动应用程序)来说是有问题的,因为恶意用户可以解编软件或查看源代码来检索秘密访问密钥。

有没有人对此有最好的build筑devise和stream程的任何build议?

更新:我深入了解这一点,似乎一堆人build议使用HTTP POST方法与JSON策略文件,如下所述: http ://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev /index.html?UHTTPPOST.html 。

(1)iPhone向我的服务器发出请求,请求策略文件(2)服务器生成json策略文件并返回给客户端(3)iPhone将照片+ json策略的HTTP POST发送到S3 。 我讨厌我以一种明显的糟糕的方式使用HTTP POST,但它似乎更好,因为它消除了我的服务器存储照片的需要。

我之前在AWS论坛上讨论过这个问题。 正如我所说,从移动设备访问AWS的正确解决scheme是使用AWS Identity and Access Management服务为每个用户生成临时的,有限权限的访问密钥。 该服务是伟大的,但它现在仍在testing阶段,它不是移动SDK的一部分。 我有一种感觉,一旦这个东西被释放好,你会马上看到在移动SDK上。

在此之前, 请为您的用户生成预先给定的URL ,或者像其他人所build议的那样通过您自己的服务器进行代理。 预先登记的URL将允许用户临时GET或PUT到您的一个桶中的S3对象,而不需要真正拥有您的凭据(它们被散列到签名中)。 你可以在这里阅读有关细节。

编辑 :我已经实施了这个问题的适当的解决scheme,使用IAM的预览testing版。 它在GitHub上可用,你可以在这里阅读 。

您可以使用REST API直接从iPhone上传到S3,并让服务器负责生成需要密钥的Authorization标头值部分。 这样,你不会冒险暴露访问密钥给任何一个越狱的iPhone,而你不把上传文件的负担在服务器上。 请求的具体细节可以在“签名和authenticationREST请求”的“Example Object PUT”下find。 我强烈build议在继续阅读之前阅读该文档。

以下用Python编写的代码生成从您的S3秘密访问密钥派生的Authorization标头值的一部分。 您应该分别使用虚拟主机窗体中的_S3_SECRET_S3_BUCKET_NAMEreplace您自己的秘密访问密钥和存储区名称 :

 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和content-type text/plain调用foo.txt产生:

 >>> get_upload_header_values('text/plain', 'foo.txt') ('Wed, 06 Feb 2013 00:57:45 +0000', 'EUSj3g70aEsEqSyPT/GojZmY8eI=') 

请注意,如果您运行此代码,返回的时间将会不同,因此编码的HMAC摘要将会不同。

现在,iPhone客户端只需使用返回的date和HMAC摘要向S3发出PUT请求。 假如说

  • 服务器在一些名为serverJson JSON对象中返回上面的date_stringserverJson
  • 您的S3访问密钥(不是您的秘密,只在服务器上)被命名为kS3AccessKey
  • 您的S3存储桶名称(设置为上面的my-bucket-name )被命名为kS3BucketName
  • 文件内容被编组在一个名为dataNSData对象中
  • 发送到服务器的filename是一个名为filename的string
  • 发送到服务器的内容types是名为contentType的string

然后您可以执行以下操作来创buildNSURLRequest

 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]]; 

接下来,您可以发出请求。 如果您使用的是优秀的AFNetworking库 ,那么可以使用XMLDocumentRequestOperationWithRequest:success:failure: AFXMLRequestOperationrequest包装在AFXMLRequestOperation对象中,然后调用它的start方法。 不要忘记完成后释放headersrequest

请注意,客户端从服务器获取Date标头的值。 这是因为,亚马逊在“时间戳要求”下描述:

“有效的时间戳(使用HTTP Date标题或x-amzdate替代)对于已authentication的请求是必需的。此外,已authentication请求中包含的客户端时间戳必须在Amazon S3系统时间的15分钟内当收到请求的时候,如果没有,请求将失败并且RequestTimeTooSkewed错误状态码。

所以,不要依赖于具有正确时间的客户端来使请求成功,而要依赖于应该使用NTP(以及像ntpd这样的守护进程)的服务器。

上传到您的服务器,然后发布到S3。 从架构的angular度来看,你会想从你的服务器上做到这一点。 在数据传输过程中有很多事情可能会出错,您可以在服务器上更好地处理数据,如果要存储有关发送到S3的映像的任何数据,您可能会有服务器端调用。

此外,您的秘密访问密钥存储在一个更安全的环境。 你自己。

如果你担心可扩展性,你将要做相当数量的S3传输,我会考虑托pipe你的服务器和EC2实例。 在两者之间传输数据是免费的(假设您将数据存储在以下数据中心中)。

对Amazon EC2和Amazon S3之间在同一地区内传输的数据或在亚马逊EC2北部弗吉尼亚地区和亚马逊S3美国标准地区之间传输的数据,不收取数据传输费用。“ Amazon Simple Storage Service(Amazon S3)

亚马逊 – EC2的成本有很多吗? (举例)了解使用EC2的优缺点。

我很困惑。 为什么亚马逊会出现w / ios sdk上传数据到s3然后告诉我们不要使用它( 在源代码中embedded凭证对于包括移动应用程序在内的软件是有问题的,因为恶意用户可以反编译软件或查看源代码代码来检索秘密访问密钥

他们可能已经提供了sdk的目的,也许应用程序可以允许validation个人s3帐户? 例如,让用户将文件存储在自己的(用户)存储区而不是提供者的应用程序? 我觉得将密钥与应用程序合并并分发的安全漏洞。 无论如何,任何人都可以(错误地)使用它们(无论如何,当它泄露时它是不可靠的)。 另一方面,保留服务器的function将保持你的密钥对用户透明,不是吗?