Am'm de punere în aplicare un fișier direct incarca de la calculator client pentru Amazon S3, prin intermediul REST API folosind doar JavaScript, fără nici un cod server-side. Toate funcționează bine, dar un singur lucru mă îngrijorează pe mine...
Când am trimite o cerere la Amazon S3 REST API, am nevoie pentru a semna cererea și de a pune o semnătură în "Autentificare" antet. Pentru a crea o semnătură, trebuie să-mi folosesc cheia secretă. Dar toate lucrurile se întâmplă pe partea de client, deci, cheia secretă pot fi ușor de arătat la pagina sursa (chiar dacă am obfuscate/cripta sursele mele).
Cum pot rezolva asta? Și este o problemă la toate? Poate pot limita cod privat specific de utilizare numai pentru REST API apeluri de la un anumit CORS Origine și de a PUNE doar și POST metode sau poate link-ul de cheie doar S3 și specifice găleată? Poate există alte metode de autentificare?
"fără server" soluție este ideală, dar pot să ia în considerare implicarea unele serverside de prelucrare, cu excepția încărcarea unui fișier pe server și apoi trimite la S3.
Cred că ceea ce vrei este Bazat pe Browser de Încărcări Folosind POST.
Practic, ai nevoie de cod server-side, dar tot nu este de a genera semnat politici. Odată ce client-side cod a semnat politică, se poate incarca folosind POSTA direct la S3 fără datele trec prin server.
Aici's oficială doc link-uri:
Diagrama: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html
Exemplu de cod: http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html
Semnat politica ar merge în html într-o formă de genul asta:
<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>
Notificare FORMĂ de acțiune este de a trimite fișierul direct la S3 - nu prin server.
De fiecare dată când unul dintre utilizatori vrea să încărcați un fișier, veți crea POLITICĂ " și " SEMNĂTURA de pe server. Vă întoarceți pagina pentru utilizator's browser-ul. Utilizatorul poate apoi să încărcați un fișier direct la S3, fără a trece prin server.
Atunci când vă conectați politica, de obicei face politica expiră după câteva minute. Aceasta obligă utilizatorii să vorbesc cu server-ul dvs. înainte de a încărca. Acest lucru vă permite să monitorizeze și să limiteze încărcări, dacă doriți.
Singurele date de gând să sau de la server-ul dvs. este semnat de Url-uri. Secretul cheile rămân secrete pe server.
Puteți face acest lucru prin AWS S3 Cognito încercați acest link aici :
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-examples.html#Amazon_S3
Încercați, de asemenea, acest cod
Doar schimba Regiunea, IdentityPoolId și găleata numele
<!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>
Te're să spui că vrei un "fără server" soluție. Dar asta înseamnă că nu au capacitatea de a pune orice de "dvs." codul în buclă. (NOTĂ: Odată ce ați da codul de la un client, l's "lor" codul acum.) Blocare în jos CORS nu este de gând să vă ajute: Oamenii pot scrie cu ușurință un non-instrument bazat pe web (sau web-based proxy), care adaugă corectă CORS antet să abuzeze de sistemul dumneavoastră.
Problema mare este că puteți't diferenția între diferiți utilizatori. Puteți't permite unui utilizator pentru a lista/accesa fișierele sale, dar a împiedica pe alții de la a face așa. Dacă detectați abuz, nu este nimic care le puteți face despre el, cu excepția schimba cheia. (Care atacatorul poate, probabil, doar obține din nou.)
Cel mai bun pariu este de a crea un "IAM utilizator" cu o cheie pentru client javascript. Doar da acces de scriere la doar o găleată. (dar, în mod ideal, nu permite ListBucket funcționare, care va face mai atractive pentru atacatori.)
Daca ai avea un server (chiar și un simplu micro exemplu de la $20/luna), ai putea semna tastele de pe server-ul dvs. în timp ce de monitorizare/de prevenire a abuzurilor în timp real. Fără un server, tot ce puteți face este de a monitoriza periodic pentru abuz în după-the-faptul. Aici's ce-as face:
rotiți periodic cheile pentru că IAM utilizator: în Fiecare noapte, de a genera o nouă cheie pentru care IAM utilizator, și de a înlocui cele mai vechi cheie. Deoarece există 2 chei, fiecare cheie va fi valabil timp de 2 zile.
permite S3 logare, și de a descărca busteni la fiecare oră. Setați alerte pe "prea multe imagini" și "prea multe descărcări". Veți dori să verificați ambele total dimensiunea fișierului și numărul de fișiere încărcate. Și veți dori să monitorizeze atât global totaluri, și, de asemenea, per-adresa IP total (cu un prag mai mic).
Aceste controale se poate face "fără server" pentru a le putea rula pe desktop-ul tau. (de exemplu, S3 nu toate de lucru, aceste procese acolo doar pentru a alerta pentru abuz de găleată S3, astfel încât să don't obține un uriaș AWS proiect de lege la sfârșitul lunii.)
Adăugând mai multe informații la răspunsul acceptat, puteți face referire la blog-ul meu pentru a vedea o versiune de rulare de cod, folosind AWS Semnătura versiunea 4.
Voi rezuma aici:
De îndată ce utilizatorul selectează un fișier pentru a fi încărcate fotografii, face următoarele:
Fac un apel la serverul de web pentru a iniția un serviciu pentru a genera necesare params
În acest serviciu, face un apel la AWS IAM serviciu pentru a obține temporar cred
Odată ce ai credibilitate, de a crea o găleată comună (base 64 codificate string). Apoi semn găleată cu politica temporară secret cheie de acces pentru a genera final semnătura
trimite parametrii necesari înapoi la UI
Odată ce acest lucru este primit, de a crea un formular html object, setați necesare params și POST-l.
Pentru informații detaliate, vă rugăm să consultați https://wordpress1763.wordpress.com/2016/10/03/browser-based-upload-aws-signature-version-4/
Pentru a crea o semnătură, trebuie să-mi folosesc cheia secretă. Dar toate lucrurile se întâmplă pe partea de client, deci, cheia secretă pot fi ușor de arătat de la sursa paginii (chiar dacă am obfuscate/cripta sursele mele).
Acest lucru este în cazul în care ai înțeles greșit. Motivul semnăturile digitale sunt utilizate este astfel încât să puteți verifica ceva la fel de corect, fără a dezvălui secretul tău cheie. În acest caz, semnătura digitală este folosit pentru a preveni utilizatorul de modificarea politicii ați setat-o pentru formularul de post.
Semnături digitale, cum ar fi cea de aici sunt folosite de securitate în jurul web. Dacă cineva (NSA?) într-adevăr au fost capabili de a renunța la ele, ar fi mult mai obiective decât găleată S3 :)
Mi-au dat un cod simplu pentru a încărca fișiere de Javascript browser-ul pentru a AWS S3 și lista toate fișierele din găleată S3.
Etape:
<?xml version="1.0" encoding="UTF-8"?>
var fileChooser = document.getElementById('fișier-selector'); var butonul = document.getElementById('incarca-buton'); var rezultat = document.getElementById('rezultatele');
funcția de încărcare() { fișier var = fileChooser.fișiere[0]; console.log(fișier.nume);
dacă (fișier) { rezultate.innerHTML = ''; var params = { Cheie: n + '.pdf', ContentType: fișierul.tip, Corp: fișier }; găleată.incarca(params, funcția(err, date) { rezultate.innerHTML = err ? 'EROARE!' : 'ÎNCĂRCATE.'; }); } else { rezultate.innerHTML = 'Nimic nu a încărca.'; } }
</script> <input type="fișier" id="fișier-selector" /> <input type="butonul" onclick="încărcați()" value="Încărcați la S3">Dacă tu nu't au orice server side cod, de securitate depinde de securitate de acces la cod JavaScript pe partea de client (de exemplu, toată lumea care are codul-ar incarca ceva).
Deci, aș recomanda, pur și simplu a crea un special găleată S3 care este publică inscriptibile (dar nu poate fi citit), astfel încât să don't nevoie de nici semnat componente pe partea de client.
Găleată numele (un GUID de exemplu) va fi singura ta apărare împotriva malware încărcări (dar un potențial atacator nu poate folosi galeata pentru transferul de date, pentru că este scris numai pentru el)
Aici este cum de a genera un document de politică folosind nod și fără server
"use strict";
const uniqid = require('uniqid');
const crypto = require('crypto');
class Token {
/**
* @param {Object} config SSM Parameter store JSON config
*/
constructor(config) {
// Ensure some required properties are set in the SSM configuration object
this.constructor._validateConfig(config);
this.region = config.region; // AWS region e.g. us-west-2
this.bucket = config.bucket; // Bucket name only
this.bucketAcl = config.bucketAcl; // Bucket access policy [private, public-read]
this.accessKey = config.accessKey; // Access key
this.secretKey = config.secretKey; // Access key secret
// Create a really unique videoKey, with folder prefix
this.key = uniqid() + uniqid.process();
// The policy requires the date to be this format e.g. 20181109
const date = new Date().toISOString();
this.dateString = date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2);
// The number of minutes the policy will need to be used by before it expires
this.policyExpireMinutes = 15;
// HMAC encryption algorithm used to encrypt everything in the request
this.encryptionAlgorithm = 'sha256';
// Client uses encryption algorithm key while making request to S3
this.clientEncryptionAlgorithm = 'AWS4-HMAC-SHA256';
}
/**
* Returns the parameters that FE will use to directly upload to s3
*
* @returns {Object}
*/
getS3FormParameters() {
const credentialPath = this._amazonCredentialPath();
const policy = this._s3UploadPolicy(credentialPath);
const policyBase64 = new Buffer(JSON.stringify(policy)).toString('base64');
const signature = this._s3UploadSignature(policyBase64);
return {
'key': this.key,
'acl': this.bucketAcl,
'success_action_status': '201',
'policy': policyBase64,
'endpoint': "https://" + this.bucket + ".s3-accelerate.amazonaws.com",
'x-amz-algorithm': this.clientEncryptionAlgorithm,
'x-amz-credential': credentialPath,
'x-amz-date': this.dateString + 'T000000Z',
'x-amz-signature': signature
}
}
/**
* Ensure all required properties are set in SSM Parameter Store Config
*
* @param {Object} config
* @private
*/
static _validateConfig(config) {
if (!config.hasOwnProperty('bucket')) {
throw "'bucket' is required in SSM Parameter Store Config";
}
if (!config.hasOwnProperty('region')) {
throw "'region' is required in SSM Parameter Store Config";
}
if (!config.hasOwnProperty('accessKey')) {
throw "'accessKey' is required in SSM Parameter Store Config";
}
if (!config.hasOwnProperty('secretKey')) {
throw "'secretKey' is required in SSM Parameter Store Config";
}
}
/**
* Create a special string called a credentials path used in constructing an upload policy
*
* @returns {String}
* @private
*/
_amazonCredentialPath() {
return this.accessKey + '/' + this.dateString + '/' + this.region + '/s3/aws4_request';
}
/**
* Create an upload policy
*
* @param {String} credentialPath
*
* @returns {{expiration: string, conditions: *[]}}
* @private
*/
_s3UploadPolicy(credentialPath) {
return {
expiration: this._getPolicyExpirationISODate(),
conditions: [
{bucket: this.bucket},
{key: this.key},
{acl: this.bucketAcl},
{success_action_status: "201"},
{'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
{'x-amz-credential': credentialPath},
{'x-amz-date': this.dateString + 'T000000Z'}
],
}
}
/**
* ISO formatted date string of when the policy will expire
*
* @returns {String}
* @private
*/
_getPolicyExpirationISODate() {
return new Date((new Date).getTime() + (this.policyExpireMinutes * 60 * 1000)).toISOString();
}
/**
* HMAC encode a string by a given key
*
* @param {String} key
* @param {String} string
*
* @returns {String}
* @private
*/
_encryptHmac(key, string) {
const hmac = crypto.createHmac(
this.encryptionAlgorithm, key
);
hmac.end(string);
return hmac.read();
}
/**
* Create an upload signature from provided params
* https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html#signing-request-intro
*
* @param policyBase64
*
* @returns {String}
* @private
*/
_s3UploadSignature(policyBase64) {
const dateKey = this._encryptHmac('AWS4' + this.secretKey, this.dateString);
const dateRegionKey = this._encryptHmac(dateKey, this.region);
const dateRegionServiceKey = this._encryptHmac(dateRegionKey, 's3');
const signingKey = this._encryptHmac(dateRegionServiceKey, 'aws4_request');
return this._encryptHmac(signingKey, policyBase64).toString('hex');
}
}
module.exports = Token;
Configurația obiect folosit este stocat în MUS Parametru Magazin și se pare că acest
{
"bucket": "my-bucket-name",
"region": "us-west-2",
"bucketAcl": "private",
"accessKey": "MY_ACCESS_KEY",
"secretKey": "MY_SECRET_ACCESS_KEY",
}
Dacă sunteți dispus să utilizeze un serviciu 3rd party, auth0.com acceptă această integrare. La auth0 schimburi de servicii un 3rd petrecere SSO serviciu de autentificare pentru un AWS sesiune temporară token va permisiuni limitate.
A se vedea: https://github.com/auth0-samples/auth0-s3-sample/ și auth0 de documentare.