2012-08-14 10 views
8

Tengo una aplicación que carga archivos de usuario a S3. Por el momento, la ACL para las carpetas y archivos está configurada en privado.Sirviendo con seguridad los archivos de Amazon S3

He creado una tabla db (llamados documentos) que almacena la siguiente información:

id 
user_id 
file_name (original file as specified by the user) 
hash_name (random hash used to save the file on amazon) 

Así, cuando un usuario desea descargar un archivo, compruebe en primer lugar en la tabla db que tengan acceso a archivo. Preferiría no tener primero el archivo descargado en mi servidor y luego enviarlo al usuario; me gustaría que ellos puedan obtener el archivo directamente de Amazon.

¿Está bien confiar en un hashname muy largo (haciendo que sea básicamente imposible para cualquier persona adivinar aleatoriamente un nombre de archivo)? En este caso, puedo configurar la ACL para que cada archivo sea de lectura pública.

O, ¿hay otras opciones que pueda utilizar para servir los archivos y mantenerlos en privado?

Respuesta

9

Recuerde, una vez que el enlace está disponible, nada impide que un usuario comparta ese enlace con otros. Por otra parte, nada impide que el usuario guarde el archivo en otro lugar y comparta un enlace a la copia del archivo.

El mejor enfoque depende de sus necesidades específicas.

Opción 1 - Descargar por tiempo limitado URL

Si procede a su escenario, también se pueden crear con vencimiento (por tiempo limitado) enlaces personalizados a los contenidos S3. Eso permitiría al usuario descargar contenido durante un tiempo limitado, después del cual tendrían que obtener un nuevo enlace.

http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html

Opción 2 - URL ofuscado

Si el valor de evitar la ejecución del archivo a través de su servidor web sobre el riesgo de que una URL, por oscura, podría ser compartida intencionalmente, a continuación, utilizar el disco -to-guess nombre del enlace. Esto permitiría que un enlace permanezca válido "para siempre", lo que significa que el enlace se puede compartir "para siempre".

Opción 3 - Descargar a través de su servidor de

Si usted está preocupado acerca de la relación que se comparte y ciertamente desea que los usuarios se autentican a través de su sitio web, entonces sirven el contenido a través de su sitio web después de la verificación de las credenciales de usuario.

Esta opción también permite que el enlace siga siendo válido "para siempre" pero requiere que el usuario inicie sesión (o tal vez solo tenga una cookie de autenticación en el navegador) para acceder al enlace.

+0

Así que, básicamente, no hay manera de mantener la ACL privada y todavía sirven en Amazon? ¿Qué hay de cambiar temporalmente la ACL a la lectura pública, luego servirla desde S3, y luego cambiar la ACL a privada? – JonoB

+0

Ver mi respuesta actualizada. Puede generar una URL pública de tiempo limitado sin abrir su ACL para el mundo. –

+0

@EricJ.¿puedo saber cómo hacer que sea un usuario autenticado para ver imágenes? Si usted sabe, entonces puede responder esta pregunta http://stackoverflow.com/questions/40168221/laravel-secure-amazon-s3-bucket-files.Gracias – iCoders

2

Solo quiero publicar la solución de PHP con código, si alguien tiene el mismo problema.

Aquí está el código utilicé:

$aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE'; 
$aws_secret_key = 'YourSecretKey12345'; 
$aws_bucket = 'bucket'; 
$file_path = 'directory/image.jpg'; 
$timeout = '+10 minutes'; 

// get the URL! 
$url = get_public_url($aws_access_key_id,$aws_secret_key,$aws_bucket,$file_path,$timeout); 

// print the URL! 
echo($url); 



function get_public_url($keyID, $s3Key, $bucket, $filepath, $timeout) 
{ 
    $expires = strtotime($timeout); 
    $stringToSign = "GET\n\n\n{$expires}\n/{$aws_bucket}/{$file_path}";  
    $signature = urlencode(hex2b64(hmacsha1($s3Key, utf8_encode($stringToSign)))); 

    $url = "https://{$bucket}.s3.amazonaws.com/{$file_path}?AWSAccessKeyId={$keyID}&Signature={$signature}&Expires={$expires}"; 
    return $url; 
} 

function hmacsha1($key,$data) 
{ 
    $blocksize=64; 
    $hashfunc='sha1'; 
    if (strlen($key)>$blocksize) 
     $key=pack('H*', $hashfunc($key)); 
    $key=str_pad($key,$blocksize,chr(0x00)); 
    $ipad=str_repeat(chr(0x36),$blocksize); 
    $opad=str_repeat(chr(0x5c),$blocksize); 
    $hmac = pack(
     'H*',$hashfunc(
      ($key^$opad).pack(
       'H*',$hashfunc(
        ($key^$ipad).$data 

        ) 
       ) 
      ) 
     ); 
    return bin2hex($hmac); 
} 

function hex2b64($str) 
{ 
    $raw = ''; 
    for ($i=0; $i < strlen($str); $i+=2) 
    { 
     $raw .= chr(hexdec(substr($str, $i, 2))); 
    } 
    return base64_encode($raw); 
} 
+0

Gran respuesta. Funcionó perfectamente Gracias Josue! –