URL: https://linuxfr.org/forums/programmationweb/posts/signature-s3-et-comportement-du-navigateur-web Title: Signature S3 et comportement du navigateur Web Authors: JulienG Date: 2023年06月04日T13:56:57+02:00 License: CC By-SA Tags: aws, s3, bash et javascript Score: 2 Bonjour, Je suis bloqué depuis quelques hier sur quelque chose qui ne me semblait pas compliqué au démarrage : faire du S3 depuis le navigateur (sans se payer une bibliothèque obscure et/ou lourde). Le contexte : avoir la possibilité d'envoyer et récupérer du contenu S3 depuis une WebExtension (je devrais en parler bientôt sur LinuxFr). En soi le plus compliqué est [de générer l'entête `Authorization`](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationExamples). Je reste sur la v2 de l'authentification AWS, à destination d'un serveur local MinIO. Voici l'équivalent Bash et Javascript que j'utilise : ```bash # bash signature=`echo -en "PUT\n\n${type}\n${date}\n${chemin}" | openssl sha1 -hmac ${s3Secret} -binary | base64` ``` ```javascript // javascript function signer(message, secretKey) { const encoder = new TextEncoder(); const data = encoder.encode(message); const key = encoder.encode(secretKey); return crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']) .then( (key) => crypto.subtle.sign('HMAC', key, data) ) .then( (signature) => { return btoa( String.fromCharCode.apply( null, new Uint8Array(signature) ) ); } ); } ``` Les deux me fournissent une signature valide. Par acquis de conscience, __j'ai aussi testé en remettant la clé générée par Bash directement dans la requête de Firefox (avec la bonne date évidemment)__. Rien à faire : le serveur n'en veut pas. Firefox envoie la requête suivante : ``` PUT /tout/test-text.json HTTP/1.1 Host: localhost:9000 User-Agent: curl/7.81.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br authorization: AWS F1vfymAX4V4yp25DbX33:GbddtmO6/A2PGse8ySAwEPNn3vk= content-type: text/plain x-amz-date: 2023年6月04日 13:13:28 +0200 Content-Length: 11 Origin: moz-extension://183b3036-20b3-4bc3-82e2-46f80d95c2ca Connection: keep-alive Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Cache-Control: max-age=0 ``` Le message de retour est sans appel : la requête par le navigateur échoue sur une 403... ```xml SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your key and signing method.test-text.jsontout/tout/test-text.json17657120F9D3A9D3dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8 ``` Chose étrange, le navigateur me permet l'ajout de `x-amz-date` mais refuse semble-t-il (silencieusement) l'envoi de l'entête `Date` : ```javascript var headers = new Headers(); headers.set('User-Agent', 'curl/7.81.0'); headers.set('Host', 'localhost:9000'); headers.set('Date', date); // ça ne passe pas ? headers.set('X-Amz-Date', date); // ça passe headers.set( "Content-Type", contentType ); headers.set( "Expect", "100-continue" ); headers.set('Authorization', `AWS ${s3Key}:${signature}`); var contexteRequete = { method: 'PUT', headers: headers, mode: 'cors', // j'ai essayé aussi plusieurs valeurs suivant la doc du MDN, sans succès cache: 'no-cache', body: "Hello world" }; var requete = new Request( `http://${domain}/${bucket}/${filename}`, contexteRequete, ); fetch(requete,contexteRequete) .then(function(response) { console.log("réponse s3 ok", response.text().then( (c)=>console.log(c) )) }) .catch(function(erreur) { console.log("réponse s3 ko", erreur) }); ``` Quant aux journaux de CURL, je ne vois pas d'écart notable : ``` * Trying 127.0.0.1:9000... * Connected to localhost (127.0.0.1) port 9000 (#0)> PUT /tout/test-text.json HTTP/1.1> Host: localhost:9000> User-Agent: curl/7.81.0> Date: 2023年6月04日 13:13:28 +0200> Content-Type: text/plain> Authorization: AWS F1vfymAX4V4yp25DbX33:GbddtmO6/A2PGse8ySAwEPNn3vk=> Accept: */*> Accept-Language: en-US,en;q=0.5> Accept-Encoding: gzip, deflate, br> Origin: moz-extension://183b3036-20b3-4bc3-82e2-46f80d95c2ca> Connection: keep-alive> Sec-Fetch-Dest: empty> Sec-Fetch-Mode: cors> Sec-Fetch-Site: same-origin> Cache-Control: max-age=0> Content-Length: 9547> Expect: 100-continue> * Mark bundle as not supporting multiuse < HTTP/1.1 100 Continue HTTP/1.1 100 Continue * We are completely uploaded and fine * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK HTTP/1.1 200 OK (...) ``` J'ai pensé que l'entête `Date` serait la source du problème (au détriment de `x-amz-date`), mais si je retire celui-ci dans Bash, l'erreur est bien différente d'une 403 (il lui faut une date) : ```xml AccessDeniedAWS authentication requires a valid Date or x-amz-date header/tout/test-text.json ``` Point intéressant, la requête n'est pas valide par défaut de signature (403, `SignatureDoesNotMatch`) si je rajoute `x-amz-date` dans les entêtes CURL (avec ou sans `Date`) - ici le bout de script pour tester avec les entêtes de Firefox que je teste : ```bash curl -v -I -X PUT -T "${fileorigin}" \ -H "Host: ${domain}" \ -H "User-Agent: curl/7.81.0" \ -H "Date: ${dateValue}" \ -H "x-amz-date: ${dateValue}" \ -H "Content-Type: ${contentType}" \ -H "Authorization: AWS ${signature}" \ -H "Accept: */*" \ -H "Accept-Language: en-US,en;q=0.5" \ -H "Accept-Encoding: gzip, deflate, br" \ -H "Origin: moz-extension://183b3036-20b3-4bc3-82e2-46f80d95c2ca" \ -H "Connection: keep-alive" \ -H "Sec-Fetch-Dest: empty" \ -H "Sec-Fetch-Mode: cors" \ -H "Sec-Fetch-Site: same-origin" \ -H "Cache-Control: max-age=0" \ "http://${domain}/${bucket}/${filename}" ``` Notez bien que le contenu textuel qui sert à la signature (en plus du secret), est correct (sinon ça ne fonctionnerait jamais en Bash). Selon vous, quelle serait ma méprise ? Est-ce seulement cette question de l'entête `Date`, mais si oui, comment "forcer" son envoi par le navigateur ? _PS : ce sont des clés bidon évidemment._

AltStyle によって変換されたページ (->オリジナル) /