Téléchargement direct de fichiers Amazon S3 à partir du navigateur client – divulgation de clé privée

Je met en œuvre un téléchargement direct de fichiers de la machine client vers Amazon S3 via REST API en utilisant uniquement JavaScript, sans code serveur. Tout fonctionne bien, mais une chose me préoccupe …

Lorsque j'envoie une demande à Amazon S3 REST API, je dois signer la demande et mettre une signature dans l'en-tête d' Authentication . Pour créer une signature, je dois utiliser ma clé secrète. Mais tout se passe d'un côté client, donc la clé secrète peut être facilement révélée à partir de la source de la page (même si j'obstrue / chiffrer mes sources).

Comment puis-je gérer cela? Et est-ce un problème du tout? Peut-être que je peux limiter l'utilisation de clés privées spécifiques uniquement aux appels de l'API REST à partir d'une origine CORS spécifique et uniquement aux méthodes PUT et POST ou peut-être à la clé de liaison uniquement pour le S3 et le seau spécifique? Peut-être y a-t-il d'autres méthodes d'authentification?

La solution «sans serveur» est idéale, mais je peux envisager d'implémenter un traitement sur le serveur, à l'exclusion de l'importation d'un fichier sur mon serveur puis de l'envoyer à S3.

Je pense que ce que vous voulez est Uploads basé sur le navigateur en utilisant POST.

Fondamentalement, vous avez besoin du code côté serveur, mais tout ce qu'il fait, c'est générer des stratégies signées. Une fois que le code côté client a la politique signée, il peut télécharger via POST directement sur S3 sans les données qui traversent votre serveur.

Voici les liens officiels de la doc.

Diagramme: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html

Exemple de code: http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

La politique signée entrerait dans votre html sous une forme comme celle-ci:

 <html> <head> ... <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> ... </head> <body> ... <form action="http://johnsmith.s3.amazonaws.com/" method="post" enctype="multipart/form-data"> Key to upload: <input type="input" name="key" value="user/eric/" /><br /> <input type="hidden" name="acl" value="public-read" /> <input type="hidden" name="success_action_redirect" value="http://johnsmith.s3.amazonaws.com/successful_upload.html" /> Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br /> <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" /> Tags for File: <input type="input" name="x-amz-meta-tag" value="" /><br /> <input type="hidden" name="AWSAccessKeyId" value="AKIAIOSFODNN7EXAMPLE" /> <input type="hidden" name="Policy" value="POLICY" /> <input type="hidden" name="Signature" value="SIGNATURE" /> File: <input type="file" name="file" /> <br /> <!-- The elements after this will be ignored --> <input type="submit" name="submit" value="Upload to Amazon S3" /> </form> ... </html> 

Notez que l'action FORM est en train d'envoyer le fichier directement sur S3 – pas via votre serveur.

Chaque fois que l'un de vos utilisateurs veut télécharger un fichier, vous créez la POLICY et la SIGNATURE sur votre serveur. Vous retournez la page au navigateur de l'utilisateur. L'utilisateur peut alors télécharger un fichier directement sur S3 sans passer par votre serveur.

Lorsque vous signez la politique, vous faites généralement que la politique expire après quelques minutes. Cela oblige vos utilisateurs à parler à votre serveur avant de télécharger. Cela vous permet de surveiller et de limiter les téléchargements si vous le désirez.

Les seules données vers ou depuis votre serveur sont les URL signées. Vos clés secrètes restent secrètes sur le serveur.

Vous pouvez le faire par AWS S3 Cognito, essayez ce lien ici:

http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-examples.html#Amazon_S3

Essayez également ce code

Il suffit de modifier Region, IdentityPoolId et votre nom de panier

 <!DOCTYPE html> <html> <head> <title>AWS S3 File Upload</title> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.12.min.js"></script> </head> <body> <input type="file" id="file-chooser" /> <button id="upload-button">Upload to S3</button> <div id="results"></div> <script type="text/javascript"> AWS.config.region = 'your-region'; // 1. Enter your region AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'your-IdentityPoolId' // 2. Enter your identity pool }); AWS.config.credentials.get(function(err) { if (err) alert(err); console.log(AWS.config.credentials); }); var bucketName = 'your-bucket'; // Enter your bucket name var bucket = new AWS.S3({ params: { Bucket: bucketName } }); var fileChooser = document.getElementById('file-chooser'); var button = document.getElementById('upload-button'); var results = document.getElementById('results'); button.addEventListener('click', function() { var file = fileChooser.files[0]; if (file) { results.innerHTML = ''; var objKey = 'testing/' + file.name; var params = { Key: objKey, ContentType: file.type, Body: file, ACL: 'public-read' }; bucket.putObject(params, function(err, data) { if (err) { results.innerHTML = 'ERROR: ' + err; } else { listObjs(); } }); } else { results.innerHTML = 'Nothing to upload.'; } }, false); function listObjs() { var prefix = 'testing'; bucket.listObjects({ Prefix: prefix }, function(err, data) { if (err) { results.innerHTML = 'ERROR: ' + err; } else { var objKeys = ""; data.Contents.forEach(function(obj) { objKeys += obj.Key + "<br>"; }); results.innerHTML = objKeys; } }); } </script> </body> </html> 

Vous dites que vous voulez une solution sans serveur. Mais cela signifie que vous n'avez aucune possibilité de mettre le code "votre" dans la boucle. (REMARQUE: Une fois que vous avez donné votre code à un client, c'est «leur» code maintenant.) Le verrouillage de CORS ne va pas aider: les gens peuvent facilement écrire un outil non basé sur le Web (ou un proxy basé sur le Web) qui ajoute L'en-tête CORS correct pour abuser de votre système.

Le gros problème est que vous ne pouvez pas différencier les différents utilisateurs. Vous ne pouvez pas permettre à un utilisateur d'inscrire ou d'accéder à ses fichiers, mais d'empêcher d'autres personnes de le faire. Si vous détectez un abus, il n'y a rien que vous pouvez faire à ce sujet, sauf changer la clé. (Ce que l'attaquant peut sans doute revenir à nouveau).

Votre meilleur pari est de créer un "utilisateur IAM" avec une clé pour votre client javascript. Donnez-lui seulement un accès écrit à un seul seau. (Mais idéalement, n'activez pas l'opération ListBucket, ce qui rendra plus attrayant pour les attaquants.)

Si vous aviez un serveur (même une micro instance simple à 20 $ par mois), vous pouvez signer les clés sur votre serveur tout en surveillant / empêchant les abus en temps réel. Sans serveur, le meilleur que vous pouvez faire est de surveiller périodiquement les abus après le fait. Voici ce que je ferais:

1) tourner périodiquement les clés pour cet utilisateur IAM: chaque nuit, générer une nouvelle clé pour cet utilisateur IAM et remplacer la clé la plus ancienne. Comme il y a 2 touches, chaque touche sera valable pendant 2 jours.

2) activer la journalisation S3, et télécharger les journaux toutes les heures. Définissez les alertes sur "trop ​​d'uploads" et "trop ​​de téléchargements". Vous voudrez vérifier la taille totale du fichier et le nombre de fichiers téléchargés. Et vous voudrez surveiller à la fois les totaux globaux, et aussi les totaux d'adresse par IP (avec un seuil inférieur).

Ces contrôles peuvent être effectués sans serveur car vous pouvez les exécuter sur votre bureau. (C.-à-d. S3 fait tout le travail, ces processus juste là pour vous alerter d'abuser de votre seau S3 afin que vous ne receviez pas un billet AWS géant en fin de mois).

Pour créer une signature, je dois utiliser ma clé secrète. Mais tout se passe d'un côté client, donc la clé secrète peut être facilement révélée à partir de la source de la page (même si j'obstrue / chiffrer mes sources).

C'est là que vous avez mal compris. La seule raison pour laquelle les signatures numériques sont utilisées est que vous pouvez vérifier quelque chose comme correct sans révéler votre clé secrète. Dans ce cas, la signature numérique est utilisée pour empêcher l'utilisateur de modifier la politique définie pour la publication du formulaire.

Les signatures numériques telles que celles ici sont utilisées pour la sécurité sur le Web. Si quelqu'un (NSA?) Était vraiment capable de les casser, ils auraient des cibles beaucoup plus grandes que votre seau S3 🙂

En ajoutant plus d'informations à la réponse acceptée, vous pouvez consulter mon blog pour voir une version en cours d'exécution du code, en utilisant AWS Signature version 4.

Résumerez ici:

Dès que l'utilisateur sélectionne un fichier à télécharger, procédez comme suit: 1. Appelez le serveur Web pour lancer un service pour générer les paramètres requis

  1. Dans ce service, appelez le service AWS IAM pour obtenir un crédit temporaire

  2. Une fois que vous avez la crédibilité, créez une politique de décompte (chaîne codée base 64). Ensuite, inscrivez la politique de décompte avec la clé d'accès secret temporaire pour générer la signature finale

  3. Renvoyer les paramètres nécessaires à l'interface utilisateur

  4. Une fois que cela est reçu, créez un objet formulaire html, définissez les paramètres requis et POST-le.

Pour des informations détaillées, veuillez consulter https://wordpress1763.wordpress.com/2016/10/03/browser-based-upload-aws-signature-version-4/

Si vous n'avez pas de code côté serveur, votre sécurité dépend de la sécurité de l'accès à votre code JavaScript du côté client (c.-à-d. Que tous ceux qui ont le code pourraient télécharger quelque chose).

Je recommande donc simplement de créer un seau S3 spécial qui est accessible en écriture publique (mais pas lisible), donc vous n'avez pas besoin de composants signés côté client.

Le nom du seau (un GUID par exemple) sera votre seule défense contre les téléchargements malveillants (mais un attaquant potentiel ne pourrait pas utiliser votre seau pour transférer des données, car il ne lui est écrit que)

Si vous souhaitez utiliser un service tiers, auth0.com prend en charge cette intégration. Le service auth0 échange une authentification de service SSO tiers pour un jeton de session temporaire AWS limitera les autorisations.

Voir: https://github.com/auth0-samples/auth0-s3-sample/
Et la documentation auth0.