secuencia - Pregunta arquitectónica y de diseño sobre la carga de fotos desde la aplicación de iPhone y S3
porque las fotos de mi iphone no cargan (5)
¿Podrían haber proporcionado el SDK con el propósito de que tal vez la aplicación podría permitir la autenticación a cuentas S3 individuales? por ejemplo, una aplicación que permite a los usuarios almacenar archivos en su propio contenedor (del usuario) en lugar de en el proveedor. Siento un error de seguridad al fusionar las claves con la aplicación y distribuirla. cualquiera puede (mal) usarlos una vez que las llaves se revelen de todos modos (nunca es seguro cuando lo estás regalando). por otro lado, mantener la funcionalidad reservada al servidor mantendrá sus claves transparentes para el usuario, ¿no es así?
Quiero permitir que los usuarios de una aplicación de iPhone carguen fotos y usen Amazon S3. Hay 2 formas en que veo que se trata de esto:
- Cargue desde el iPhone a mi servidor, que lo envía a Amazon S3.
- Cargar desde iPhone directamente a S3
Para la opción 1, la seguridad es sencilla. Nunca tengo que decirle al iPhone mi secreto S3. Lo malo es que todo se envía a través de nuestro servidor para cargar archivos, lo que en cierto modo frustra el propósito de ir a S3.
Para la opción 2, en teoría es mejor, pero el problema es cómo habilitar el iPhone (o cualquier aplicación móvil en una plataforma diferente) para escribir en mi cubo S3 sin darle mi secreto. Además, no estoy seguro de si este es un buen diseño o no, porque el flujo sería: iphone carga en S3, obtiene la URL, luego le dice al servidor cuál es la URL para que pueda agregarla a nuestra base de datos para hacer referencia en el futuro. Sin embargo, dado que la comunicación está separada en 2 patas (iphone-> S3 frente a iPhone-> Mi-Servidor) la deja frágil como una operación no atómica.
He encontrado información más antigua que hace referencia al uso de cargas basadas en el navegador mediante POST, pero no estoy seguro de si ese sigue siendo el enfoque recomendado. Espero una solución mejor en la que podamos usar las API REST en lugar de confiar en POST. También he visto AWS iOS Beta SDK , pero sus documentos no me ayudaron demasiado y encontré un artículo de Amazon que tampoco me ayudó, ya que te advierte sobre lo que no debes hacer, pero no te dice un enfoque alternativo:
Los SDK móviles de AWS firman las solicitudes API enviadas a Amazon Web Services (AWS) para validar la identidad de la cuenta AWS que realiza la solicitud. De lo contrario, un desarrollador malintencionado podría realizar fácilmente solicitudes a la infraestructura de otro desarrollador. Las solicitudes se firman utilizando una identificación de clave de acceso de AWS y una clave de acceso secreta que proporciona AWS. La clave de acceso secreta es similar a una contraseña, y es extremadamente importante mantenerse en secreto.
Consejo: Puede ver todas sus credenciales de seguridad de AWS, incluidas la clave de acceso y la clave secreta de acceso, en el sitio web de AWS en http://aws.amazon.com/security-credentials .
Incrustar credenciales en el código fuente es problemático para el software, incluidas las aplicaciones móviles, porque los usuarios malintencionados pueden descompilar el software o ver el código fuente para recuperar la clave secreta de acceso.
¿Alguien tiene algún consejo sobre el mejor diseño arquitectónico y flujo para esto?
Actualización: Mientras más profundizo en esto, parece que muchos usuarios recomiendan usar el método HTTP POST con el archivo de políticas json como se describe aquí: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev /index.html?UsingHTTPPOST.html .
Con esto, el flujo sería algo así como (1) iPhone solicita a mi servidor, solicitando el archivo de política (2) el servidor genera el archivo de política json y lo devuelve al cliente (3) iPhone hace HTTP POST de la política de foto + json a S3 . Odio que esté usando HTTP POST de una manera aparentemente kludgy pero parece ser mejor porque elimina la necesidad de que mi servidor almacene la foto.
Cargue en su servidor y luego publíquelo en S3. Desde el punto de vista de la arquitectura, querrás hacerlo desde tu servidor. Hay muchas cosas que podrían salir mal durante la transferencia de datos que puede manejar mejor en el servidor y si desea almacenar cualquier información sobre la imagen que está enviando a S3, es probable que tenga una llamada al lado del servidor.
Además, su clave de acceso secreta se almacena en un entorno más seguro. Tu propio.
Si le preocupa la escalabilidad y va a realizar un número considerable de transferencias S3, consideraría alojar su servidor y la instancia EC2. La transferencia de datos entre los dos es gratuita (dado que usted almacena sus datos en los siguientes centros de datos).
No hay un cargo de transferencia de datos para los datos transferidos entre Amazon EC2 y Amazon S3 dentro de la misma región o para los datos transferidos entre Amazon EC2 Northern Virginia Region y Amazon S3 US Standard Region. " Amazon Simple Storage Service (Amazon S3)
Hay muchas publicaciones aquí en SO Amazon - EC2 cost? (ejemplo) sobre los pros y los contras del uso de EC2.
Estoy confundido. ¿Por qué Amazon vendría con el ios sdk para subir datos a s3 y luego nos dicen que no lo usemos? ( Incrustar credenciales en el código fuente es problemático para el software, incluidas las aplicaciones móviles, porque los usuarios malintencionados pueden descompilar el software o ver la fuente código para recuperar la clave secreta de acceso ) ???
Ya he discutido este tema en los foros de AWS . Como dije allí, la solución adecuada para acceder a AWS desde un dispositivo móvil es usar el servicio de Administración de acceso e identidad de AWS para generar claves de acceso de privilegio limitado temporales para cada usuario. El servicio es excelente, pero todavía está en versión beta y todavía no forma parte del SDK móvil. Tengo la sensación de que una vez que esto se publique definitivamente, lo verás en el SDK móvil inmediatamente después.
Hasta entonces, genere URL preseleccionadas para sus usuarios, o proxy a través de su propio servidor como otros han sugerido. La URL prescrita les permitirá a sus usuarios OBTENER O PONER temporalmente en un objeto S3 en uno de sus depósitos sin tener realmente sus credenciales (están codificadas en hash en la firma). Puedes leer sobre los detalles here .
EDITAR : He implementado una solución adecuada para este problema, utilizando la versión preliminar beta de IAM. Está available en GitHub, y puedes leerlo aquí .
Puede cargar directamente desde su iPhone a S3 usando la API REST, y hacer que el servidor sea responsable de generar la parte del valor del encabezado de Authorization
que requiere la clave secreta. De esta forma, no se arriesga a exponer la clave de acceso a ninguna persona que tenga un iPhone liberado, mientras que usted no tiene que cargar el archivo en el servidor. Los detalles exactos de la solicitud se pueden encontrar en "Objeto de ejemplo PUT" de "Firmar y autenticar solicitudes de REST" . Recomiendo leer ese documento antes de seguir adelante.
El siguiente código, escrito en Python, genera la parte del valor del encabezado de Authorization
que se deriva de su clave de acceso secreto S3. Debe sustituir su clave de acceso secreta y nombre de depósito en forma de host virtual por _S3_SECRET
y _S3_BUCKET_NAME
continuación, respectivamente:
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)
Llamando esto con el nombre de archivo foo.txt
y el tipo de contenido text/plain
yields:
>>> get_upload_header_values(''text/plain'', ''foo.txt'')
(''Wed, 06 Feb 2013 00:57:45 +0000'', ''EUSj3g70aEsEqSyPT/GojZmY8eI='')
Tenga en cuenta que si ejecuta este código, el tiempo devuelto será diferente, por lo que el resumen HMAC codificado será diferente.
Ahora, el cliente de iPhone solo tiene que emitir una solicitud PUT a S3 utilizando la fecha devuelta y el resumen de HMAC. Asumiendo que
- el servidor devuelve
date_string
yauth_string
arriba en algún objeto JSON llamadoserverJson
- su clave de acceso S3 (no es su secreto, que está solo en el servidor) se llama
kS3AccessKey
- su nombre de depósito S3 (establecido en
my-bucket-name
anterior) se llamakS3BucketName
- los contenidos del archivo se organizan en un objeto
NSData
llamadodata
- el nombre de archivo que se envió al servidor es una cadena llamada
filename
- el tipo de contenido que se envió al servidor es una cadena llamada
contentType
Luego puede hacer lo siguiente para crear 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]];
A continuación, puede emitir la solicitud. Si está utilizando la excelente biblioteca de AFNetworking , puede envolver la request
en un objeto XMLDocumentRequestOperationWithRequest:success:failure:
utilizando XMLDocumentRequestOperationWithRequest:success:failure:
y luego invocar su método de start
. No te olvides de soltar los headers
y request
cuando termines.
Tenga en cuenta que el cliente obtuvo el valor del encabezado Date
del servidor. Esto se debe a que, como describe Amazon en "Requisito de sello de tiempo" :
"Una marca de tiempo válida (utilizando el encabezado Fecha HTTP o una alternativa x-amz-date) es obligatoria para las solicitudes autenticadas. Además, la marca de tiempo del cliente incluida con una solicitud autenticada debe estar dentro de los 15 minutos de la hora del sistema Amazon S3 cuando se recibe la solicitud. De lo contrario, la solicitud fallará con el código de estado de error RequestTimeTooSkewed ".
Entonces, en lugar de confiar en que el cliente tenga la hora correcta para que la solicitud tenga éxito, confíe en el servidor, que debería usar NTP (y un daemon como ntpd
).