2012-08-14 9 views
8

Ich habe eine App, die Benutzerdateien auf S3 hochlädt. Im Moment ist die ACL für die Ordner und Dateien auf privat gesetzt.Sicheres Versenden von Dateien von Amazon S3

Ich habe eine DB-Tabelle erstellt (so genannte docs), die die folgenden Informationen speichert:

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

Also, wenn ein Benutzer eine Datei herunterladen will, ich zum ersten Mal in der db-Tabelle überprüfen, dass sie Zugang zu Datei. Ich würde es vorziehen, wenn die Datei nicht zuerst auf meinen Server heruntergeladen und dann an den Benutzer gesendet wird - ich möchte, dass sie die Datei direkt von Amazon abrufen können.

Ist es in Ordnung, sich auf einen sehr langen Hashnamen zu verlassen (was es im Prinzip unmöglich macht, einen Dateinamen zufällig zu erraten)? In diesem Fall kann ich die ACL für jede Datei auf public-read setzen.

Oder gibt es andere Optionen, die ich verwenden kann, um die Dateien zu bedienen, während sie privat bleiben?

Antwort

9

Denken Sie daran, sobald ein Link existiert, verhindert nichts, dass ein Benutzer diesen Link mit anderen teilen kann. Andererseits hindert nichts den Benutzer daran, die Datei anderswo zu speichern und einen Link zu der Kopie der Datei zu teilen.

Der beste Ansatz hängt von Ihren spezifischen Anforderungen ab.

Option 1 - Zeitlich begrenzte Download-URL

Wenn für Ihr Szenario, können Sie auch auf die S3 Inhalt abläuft (zeitlich begrenzte) benutzerdefinierten Links erstellen. Das würde es dem Benutzer erlauben, Inhalte für eine begrenzte Zeit herunterzuladen, wonach sie einen neuen Link erhalten müssten.

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

Option 2 - Obfuscated URL

Wenn Sie die Datei über Ihren Web-Server über das Risiko laufen legen Wert zu vermeiden, dass eine URL jedoch unklar, könnte absichtlich geteilt wird, dann die harte verwenden Verknüpfungsname. Dies würde ermöglichen, dass eine Verbindung "für immer" gültig bleibt, was bedeutet, dass die Verbindung "für immer" geteilt werden kann.

Option 3 - Laden Sie über Ihren Server

Wenn Sie über den Link sind geteilt werden und sicherlich möchten, dass Benutzer über Ihre Website authentifizieren, dann den Inhalt über Ihre Website dienen nach Benutzeranmeldeinformationen zu überprüfen.

Diese Option ermöglicht auch, dass der Link "für immer" gültig bleibt, aber der Benutzer sich anmelden muss (oder vielleicht nur einen Authentifizierungscookie im Browser hat), um auf den Link zuzugreifen.

+0

Also, im Grunde gibt es keine Möglichkeit, die ACL privat zu halten und sie immer noch von amazon dienen? Wie wäre es, die ACL vorübergehend in Public-Read zu ändern, sie dann von S3 aus zu bedienen und dann die ACL wieder in den Private-Modus zu ändern? – JonoB

+0

Siehe meine aktualisierte Antwort. Sie können eine zeitlich begrenzte öffentliche URL generieren, ohne Ihre ACL für die Welt zu öffnen. –

+0

@EricJ.kann ich wissen, wie man es authentifizierten Benutzer macht, um Bilder anzusehen. Wenn Sie dann wissen, können Sie für diese Frage antworten http://stackoverflow.com/questions/40168221/laravel-secure-amazon-s3-bucket-files.Thank Sie – iCoders

2

Ich möchte nur die PHP-Lösung mit Code, wenn jemand das gleiche Problem hat.

Hier ist der Code, den ich verwenden:

$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

Gute Antwort. Arbeitete perfekt. Danke Josue! –