关于从iPhone应用程序和S3上传照片的架构和设计问题

TMC*_*TMC 48 iphone mobile amazon-s3 ios4 ios

我想允许iPhone应用程序的用户上传照片并使用Amazon S3.我有两种方法可以解决这个问题:

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

对于选项1,安全性很简单.我永远不必告诉iPhone我的S3秘密.缺点是所有内容都通过我们的服务器代理上传,这有点挫败了进入S3的目的.

对于选项2,理论上它更好,但问题是如何在不给我秘密的情况下将iPhone(或不同平台上的任何移动应用程序)写入我的S3存储桶?另外,我不确定这是否是一个好的设计,因为流程将是:iphone上传到S3,获取URL,然后告诉服务器URL是什么,以便它可以将它添加到我们的数据库以引用未来.但是,由于通信被分成两条腿(iphone-> S3 vs iPhone-> My-Server),因此它将其作为非原子操作而变得脆弱.

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

移动AWS开发工具包签署发送到Amazon Web Services(AWS)的API请求,以验证发出请求的AWS账户的身份.否则,恶意开发人员可以轻松地向其他开发人员的基础架构发出请求.请求使用AWS提供的AWS Access Key ID和Secret Access Key进行签名.秘密访问密钥类似于密码,保密是非常重要的.

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

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

有没有人对最佳建筑设计和流程有任何建议?

更新:我越深入研究,似乎有很多人建议使用HTTP POST方法和json策略文件,如下所述:http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev /index.html?UsingHTTPPOST.html.

有了这个,流程就像(1)iPhone向我的服务器发出请求,请求策略文件(2)服务器生成json策略文件并返回给客户端(3)iPhone将照片+ json策略的HTTP POST发送到S3 .我讨厌我以明显的kludgy方式使用HTTP POST,但它看起来更好,因为它根本不需要我的服务器来存储照片.

Adr*_*scu 10

我之前在AWS论坛上讨论了这个问题.正如我所说,从移动设备访问AWS的正确解决方案是使用AWS Identity and Access Management服务为每个用户生成临时的,有限权限的访问密钥.该服务非常棒,但它目前仍处于测试阶段,而且还不是移动SDK的一部分.我有一种感觉,一旦这个东西发布好,你会立即在移动SDK上看到它.

在此之前,为您的用户生成预先签名的URL,或者像其他人建议的那样通过您自己的服务器代理.预先签名的URL将允许您的用户临时GET或PUT到您的一个存储桶中的S3对象,而不实际拥有您的凭据(它们被哈希到签名中).你可以在这里阅读详细信息.

编辑:我已经使用IAM的预览测试版为这个问题实现了一个合适的解决方案.它可以在GitHub上找到,你可以在这里阅读它.


Lar*_*ipp 5

上传到您的服务器,然后发布到 S3。从架构的角度来看,您将希望从您的服务器执行此操作。在数据传输过程中可能会出现很多问题,您可以在服务器上更好地处理,如果您想存储有关发送到 S3 的图像的任何数据,您可能无论如何都会进行服务器端调用。

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

如果您担心可扩展性并且您将进行大量 S3 传输,我会考虑将您的服务器托管在 EC2 实例上。在两者之间传输数据是免费的(假设您将数据存储在以下数据中心)。

同一区域内的 Amazon EC2 和 Amazon S3 之间传输的数据或 Amazon EC2 北弗吉尼亚区域和 Amazon S3 美国标准区域之间传输的数据不收取数据传输费用。” Amazon Simple Storage Service (Amazon S3)

SO Amazon上有很多帖子- EC2 成本?(示例)关于使用 EC2 的利弊。


sha*_*ter 5

您可以使用REST API直接从iPhone上传到S3,并让服务器负责生成Authorization需要密钥的标头值部分.这样一来,你不要冒险暴露访问密钥给任何人越狱的iPhone,而你不把上传服务器上的文件负担.可以在"签名和验证REST请求"的"示例对象PUT"下找到要求的确切细节.我强烈建议您在继续之前阅读该文档.

以Python编写的以下代码生成Authorization从S3秘密访问密钥派生的标头值部分.你应该替换为自己的秘密访问密钥,并在虚拟主机的形式水桶名_S3_SECRET_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)
Run Code Online (Sandbox Code Playgroud)

使用文件名foo.txt和内容类型调用它会text/plain产生:

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

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

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

  • 服务器在一些名为的JSON对象中返回date_stringauth_string以上serverJson
  • 您的S3访问密钥(不是您的秘密,仅在服务器上)被命名 kS3AccessKey
  • 您的S3存储桶名称(设置为my-bucket-name上面)已命名kS3BucketName
  • 文件内容在NSData名为的对象中编组data
  • 发送到服务器的文件名是一个名为的字符串 filename
  • 发送到服务器的内容类型是一个名为的字符串 contentType

然后,您可以执行以下操作来创建NSURLRequest:

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]];
Run Code Online (Sandbox Code Playgroud)

接下来,您可以发出请求.如果您使用的优秀AFNetworking库,那么你可以包装request在一个AFXMLRequestOperation对象使用XMLDocumentRequestOperationWithRequest:success:failure:,然后调用它的start方法.不要忘记发布headers,request完成后.

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

"对于经过身份验证的请求,必须使用有效的时间戳(使用HTTP Date标头或x-amz-date备用).此外,经过身份验证的请求中包含的客户端时间戳必须在Amazon S3系统时间的15分钟内收到请求时.如果没有,请求将失败,并显示RequestTimeTooSkewed错误状态代码."

因此,而不是为了依靠具有正确的时间在客户端上获得成功的要求,依赖于服务器,它应该使用NTP(和守护像ntpd).

  • Shadowmatter是对的.直接上传到S3是更好的选择.他的代码效果很好.我[把我的叉子放在一个要点](https://gist.github.com/coudron/5258947),除了Objective-C代码之外还包含一些示例Python代码.这也是一个很好的方式来解决**Heroku的30秒请求超时**问题. (2认同)