nodejs listobjects aws apiversion javascript authentication amazon-web-services amazon-s3 amazon

javascript - listobjects - Subida directa de archivos de Amazon S3 desde el navegador del cliente: divulgación de claves privadas



javascript aws (8)

Para crear una firma, debo usar mi clave secreta. Pero todo sucede en el lado del cliente, por lo tanto, la clave secreta puede revelarse fácilmente desde el origen de la página (incluso si oculto / encripto mis fuentes).

Aquí es donde has malentendido. La razón por la cual se utilizan firmas digitales es para que pueda verificar algo como correcto sin revelar su clave secreta. En este caso, la firma digital se usa para evitar que el usuario modifique la política que estableció para la publicación del formulario.

Las firmas digitales como la que se encuentra aquí se utilizan para la seguridad en toda la web. Si alguien (NSA?) Realmente pudiera romperlos, tendrían objetivos mucho más grandes que tu cubo S3 :)

Estoy implementando una carga directa de archivos desde la máquina cliente a Amazon S3 a través de la API REST usando solo JavaScript, sin ningún código del lado del servidor. Todo funciona bien pero una cosa me preocupa ...

Cuando envío una solicitud a la API REST de Amazon S3, debo firmar la solicitud y poner una firma en el encabezado Authentication . Para crear una firma, debo usar mi clave secreta. Pero todo sucede en el lado del cliente, por lo tanto, la clave secreta puede revelarse fácilmente desde el origen de la página (incluso si oculto / encripto mis fuentes).

¿Cómo puedo manejar esto? ¿Y es un problema en absoluto? ¿Tal vez puedo limitar el uso de la clave privada específica solo a las llamadas a la API REST desde un origen de CORS específico y solo a los métodos PUT y POST, o tal vez a la clave de enlace solo a S3 y a un depósito específico? ¿Puede haber otros métodos de autenticación?

La solución "sin servidor" es ideal, pero puedo considerar involucrar algún procesamiento en el servidor, excluyendo la carga de un archivo a mi servidor y luego enviarlo a S3.


Al agregar más información a la respuesta aceptada, puede consultar mi blog para ver una versión en ejecución del código, utilizando AWS Signature versión 4.

Resumiremos aquí:

Tan pronto como el usuario seleccione un archivo para ser cargado, haga lo siguiente: 1. Haga una llamada al servidor web para iniciar un servicio para generar los parámetros requeridos.

  1. En este servicio, realice una llamada al servicio AWS IAM para obtener crédito temporal

  2. Una vez que tenga la credibilidad, cree una política de depósito (cadena codificada en base 64). A continuación, firme la política de depósito con la clave de acceso secreto temporal para generar la firma final

  3. enviar los parámetros necesarios a la interfaz de usuario

  4. Una vez que se haya recibido, cree un objeto de formulario html, configure los parámetros necesarios y POST.

Para obtener información detallada, consulte https://wordpress1763.wordpress.com/2016/10/03/browser-based-upload-aws-signature-version-4/


Creo que lo que quieres son cargas basadas en el navegador usando POST.

Básicamente, necesitas un código del lado del servidor, pero todo lo que hace es generar políticas firmadas. Una vez que el código del lado del cliente tiene la política firmada, puede cargar usando POST directamente a S3 sin que los datos pasen por su servidor.

Aquí están los enlaces oficiales de doc:

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

Código de ejemplo: http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

La política firmada irá en su html en una forma como esta:

<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>

Observe que la acción FORM está enviando el archivo directamente a S3 , no a través de su servidor.

Cada vez que uno de tus usuarios quiera subir un archivo, deberías crear la POLICY y SIGNATURE en tu servidor. Usted devuelve la página al navegador del usuario. El usuario puede subir un archivo directamente a S3 sin pasar por su servidor.

Cuando firma la política, normalmente hace que la política caduque después de unos minutos. Esto obliga a los usuarios a hablar con su servidor antes de cargarlo. Esto le permite controlar y limitar las cargas si lo desea.

La única información que llega o sale de su servidor son las URL firmadas. Tus claves secretas permanecen secretas en el servidor.


Estás diciendo que quieres una solución "sin servidor". Pero eso significa que no tienes la capacidad de poner cualquier "tu" código en el circuito. (NOTA: una vez que le das tu código a un cliente, es "su" código ahora). Bloquear CORS no va a ayudar: las personas pueden escribir fácilmente una herramienta no basada en la web (o un proxy basado en web) que agregue el encabezado CORS correcto para abusar de su sistema.

El gran problema es que no se puede diferenciar entre los diferentes usuarios. No puede permitir que un usuario liste / acceda a sus archivos, pero impide que otros lo hagan. Si detecta un abuso, no hay nada que pueda hacer al respecto excepto cambiar la clave. (Que el atacante puede supuestamente obtener nuevamente).

Su mejor opción es crear un "usuario de IAM" con una clave para su cliente de JavaScript. Solo déle acceso de escritura a solo un cubo. (pero idealmente, no habilite la operación ListBucket, que lo hará más atractivo para los atacantes).

Si tuviera un servidor (incluso una microinstancia simple a $ 20 / mes), podría firmar las claves en su servidor mientras controla / previene el abuso en tiempo real. Sin un servidor, lo mejor que puede hacer es monitorear periódicamente el abuso después de los hechos. Esto es lo que haría:

1) rotar periódicamente las teclas para ese usuario de IAM: todas las noches, genere una nueva clave para ese usuario de IAM y reemplace la clave más antigua. Como hay 2 claves, cada clave será válida por 2 días.

2) habilite el registro S3 y descargue los registros cada hora. Establezca alertas en "demasiadas cargas" y "demasiadas descargas". Deberá verificar tanto el tamaño total del archivo como la cantidad de archivos cargados. Y querrá monitorear tanto los totales globales como también los totales de direcciones por IP (con un umbral más bajo).

Estas comprobaciones se pueden hacer "sin servidor" porque puede ejecutarlas en su escritorio. (es decir, S3 hace todo el trabajo, estos procesos están ahí para alertarlo sobre el uso indebido de su segmento S3, por lo que no obtendrá una cuenta AWS gigante a fin de mes).


He proporcionado un código simple para cargar archivos desde el navegador Javascript a AWS S3 y una lista de todos los archivos en el depósito S3.

Pasos:

  1. Para saber cómo crear Create IdentityPoolId http://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html

    1. Vaya a la página de consola de S3 y abra la configuración de cors desde las propiedades del depósito y escriba el siguiente código XML en eso.

      <?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedMethod>HEAD</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>

    2. Crea un archivo HTML que contenga el siguiente código, cambia las credenciales, abre el archivo en el navegador y disfruta.

      <script type="text/javascript"> AWS.config.region = ''ap-north-1''; // Region AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: ''ap-north-1:qb4g-cn3esdf7wbdw'', }); var bucket = new AWS.S3({ params: { Bucket: ''MyBucket'' } }); var fileChooser = document.getElementById(''file-chooser''); var button = document.getElementById(''upload-button''); var results = document.getElementById(''results''); function upload() { var file = fileChooser.files[0]; console.log(file.name); if (file) { results.innerHTML = ''''; var params = { Key: n + ''.pdf'', ContentType: file.type, Body: file }; bucket.upload(params, function(err, data) { results.innerHTML = err ? ''ERROR!'' : ''UPLOADED.''; }); } else { results.innerHTML = ''Nothing to upload.''; } } </script> <body> <input type="file" id="file-chooser" /> <input type="button" onclick="upload()" value="Upload to S3"> <div id="results"></div> </body>


Puede hacerlo por AWS S3 Cognito intente este enlace aquí:

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

Pruebe también este código

Simplemente cambie Region, IdentityPoolId y su nombre de cubo

<!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>

Para más detalles, por favor verifique - Github

Si está dispuesto a utilizar un servicio de terceros, auth0.com es compatible con esta integración. El servicio auth0 intercambia una autenticación de servicio SSO de terceros para un token de sesión temporal de AWS que limitará los permisos.

Ver: https://github.com/auth0-samples/auth0-s3-sample/
y la documentación de auth0.


Si no tiene ningún código del lado del servidor, la seguridad depende de la seguridad del acceso a su código JavaScript en el lado del cliente (es decir, todos los que tengan el código podrían cargar algo).

Así que lo recomendaría, simplemente crear un contenedor S3 especial que sea de escritura pública (pero no legible), por lo que no necesita ningún componente firmado en el lado del cliente.

El nombre del contenedor (un GUID, por ejemplo) será su única defensa contra las cargas maliciosas (pero un posible atacante no podría usar su depósito para transferir datos, porque es solo para él)