繁体   English   中英

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

[英]Architectural and design question about uploading photos from iPhone app and S3

我想允许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,但它看起来更好,因为它根本不需要我的服务器来存储照片。

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

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

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

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

以Python编写的以下代码生成从S3秘密访问密钥派生的Authorization标头值的一部分。 您应该分别在下面的_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)

使用文件名foo.txt和content-type text/plain调用此方法会产生:

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

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

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

  • 服务器返回date_stringauth_string在一些JSON对象命名为上述serverJson
  • 您的S3访问密钥(不是您的秘密,仅在服务器上)名为kS3AccessKey
  • 您的S3存储桶名称(上面设置为my-bucket-name )名为kS3BucketName
  • 文件内容在名为dataNSData对象中进行编组
  • 发送到服务器的filename是名为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]];

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

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

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

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

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

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

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

对于在同一地区内的Amazon EC2和Amazon S3之间传输的数据,或者对于Amazon EC2 Northern Virginia Region和Amazon S3美国标准区域之间传输的数据,不收取数据传输费用。“ Amazon Simple Storage Service(Amazon S3)

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

我糊涂了。 亚马逊为什么会出现ios sdk将数据上传到s3然后告诉我们不要使用它( 在源代码中嵌入凭据对软件有问题,包括移动应用程序,因为恶意用户可以对软件进行反编译或查看源代码用于检索秘密访问密钥的代码 )???

他们可能提供了sdk,以便申请可能允许对个人s3帐户进行身份验证? 例如,一个应用程序,允许用户将文件存储在他们自己(用户)的存储桶而不是提供商? 我觉得将密钥与应用程序合并并分发它存在安全漏洞。 任何人都可以(错误)使用它们一旦钥匙被揭示(当你放弃它时它永远不会安全)。 另一方面,保留服务器保留的功能将使您的密钥对用户透明,不是吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM