Crittografia e decrittografia delle stringhe JavaScript?


153

Sono interessato a creare una piccola app per uso personale che crittograferà e decrittograferà le informazioni sul lato client utilizzando JavaScript. Le informazioni crittografate verranno archiviate in un database su un server, ma mai la versione decrittografata.

Non deve essere super duper sicuro, ma vorrei usare un algoritmo attualmente ininterrotto.

Idealmente sarei in grado di fare qualcosa del genere

var gibberish = encrypt(string, salt, key);

per generare la stringa codificata e qualcosa di simile

var sensical = decrypt(gibberish, key);

per decodificarlo in seguito.

Finora l'ho visto: http://bitwiseshiftleft.github.io/sjcl/

Qualche altra biblioteca che dovrei guardare?


2
Dai un'occhiata alla crittografia Javascript AES
kevinji


10
Alcune terminologie qui sono disattivate, ecco una semplice versione 1. I sali vengono aggiunti alle informazioni (di solito le password) che vengono sottoposte a hash. Il loro scopo è quello di rendere l'hash diverso da quello che sarebbe senza il sale. Questo è utile perché crea pre-generati hash se il tuo database viene violato e le password degli utenti con hash vengono rilasciate. 2. L'hashing è un'operazione a senso unico che traduce l'input in output. Non può essere facilmente annullato o annullato. 3. La codifica non è crittografia. base64_encode, UrlEncode, ecc
des

Risposte:


160

 var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
//U2FsdGVkX18ZUVvShFSES21qHsQEqZXMxQ9zgHy+bu0=

var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
//4d657373616765


document.getElementById("demo1").innerHTML = encrypted;
document.getElementById("demo2").innerHTML = decrypted;
document.getElementById("demo3").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
Full working sample actually is:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" integrity="sha256-/H4YS+7aYb9kJ5OKhFYPUjSJdrtV6AeyJOtTkw6X72o=" crossorigin="anonymous"></script>

<br><br>
<label>encrypted</label>
<div id="demo1"></div>
<br>

<label>decrypted</label>
<div id="demo2"></div>

<br>
<label>Actual Message</label>
<div id="demo3"></div>


8
Encrypted è in realtà un oggetto, ma puoi chiamare crittoted.toString () per ottenere la stringa. Sarai in grado di decifrare quella stringa in seguito: jsbin.com/kofiqokoku/1
Tomas Kirda

9
Ma come possiamo proteggere la passphrase segreta?
duykhoa,

9
Sembra che crypto js sia un progetto archiviato. C'è un clone su github: github.com/sytelus/CryptoJS ma non è stato aggiornato da due anni. È ancora l'opzione migliore per la crittografia js?
syonip,

2
Vorrei andare con questo: github.com/brix/crypto-js è anche disponibile via NPM
Tomas Kirda il

1
@stom dipende da te come e dove lo memorizzi. Non so se esiste un modo veramente sicuro per archiviarlo in un browser. Richiederli dal server e archiviarli in memoria.
Tomas Kirda,

62

Che ne dici di CryptoJS ?

È una libreria crittografica solida, con molte funzionalità. Implementa hash, HMAC, PBKDF2 e cifre. In questo caso, le cifre sono ciò di cui hai bisogno. Dai un'occhiata alla domanda di avvio rapido sulla home page del progetto.

Potresti fare qualcosa di simile con l'AES:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>

<script>
    var encryptedAES = CryptoJS.AES.encrypt("Message", "My Secret Passphrase");
    var decryptedBytes = CryptoJS.AES.decrypt(encryptedAES, "My Secret Passphrase");
    var plaintext = decryptedBytes.toString(CryptoJS.enc.Utf8);
</script>

Per quanto riguarda la sicurezza, al momento della mia scrittura, si ritiene che l'algoritmo AES sia ininterrotto

Modificare :

Sembra che l'URL online non sia attivo e puoi utilizzare i file scaricati per la crittografia dal link indicato di seguito e posizionare i rispettivi file nella cartella principale dell'applicazione.

https://code.google.com/archive/p/crypto-js/downloads

o usato altri CDN come https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js


Qual è la differenza tra rollup e componenti nella cartella 3.1.2?
Kanagavelu Sugumar

Dopo aver giocato un po 'i componenti sono le parti separate. Dovrai sapere quali componenti prendere (e in quale ordine) per farlo funzionare. I file di rollup contengono tutto il necessario per farlo funzionare con un solo riferimento di script (molto meglio dato che il lavoro è già fatto).
Shahar Eldad,

2
Ma come possiamo proteggere la passphrase segreta?
shaijut

@shaijut No. Non lo salvi nemmeno da nessuna parte tranne che nella RAM durante la crittografia / decrittografia del testo in chiaro. La passphrase deve essere memorizzata solo nel cervello dell'utente (o in un gestore di password)
slebetman

39

Ho creato un programma di cifratura / decodifica di testo insicuro ma semplice. Nessuna dipendenza con nessuna libreria esterna.

Queste sono le funzioni

const cipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const byteHex = n => ("0" + Number(n).toString(16)).substr(-2);
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);

    return text => text.split('')
        .map(textToChars)
        .map(applySaltToChar)
        .map(byteHex)
        .join('');
}

const decipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
    return encoded => encoded.match(/.{1,2}/g)
        .map(hex => parseInt(hex, 16))
        .map(applySaltToChar)
        .map(charCode => String.fromCharCode(charCode))
        .join('');
}

E puoi usarli come segue:

// To create a cipher
const myCipher = cipher('mySecretSalt')

//Then cipher any text:
myCipher('the secret string')   // --> "7c606d287b6d6b7a6d7c287b7c7a61666f"

//To decipher, you need to create a decipher and use it:
const myDecipher = decipher('mySecretSalt')
myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f")    // --> 'the secret string'

4
let myDecipher = decipher ('CartelSystem') - Anche questo salt decifrerà la stringa. Non devi conoscere la parola esatta "mySecretSalt"
Dror Bar

Inoltre, saltChars in decifratura non viene utilizzato?
Dror Bar,

1
Ancora un altro post in cui qualcuno sta usando ciecamente let. 😒︎
Giovanni,

1
Non è questo a) super rotto e insicuro eb) il "sale" è in realtà la tua "chiave segreta" poiché i sali non dovrebbero essere privati? Penso che sia molto pericoloso pubblicare codice come questo senza commenti che questo codice divertente non è destinato a nessun uso nel mondo reale. La quantità di voti è preoccupante. crypto.stackexchange.com/questions/11466/…
lschmierer

1
Beh, almeno usano la crittografia del suono. Quello che state facendo è fondamentalmente un Cesare Chipher (applicando la stessa chiave per ogni personaggio) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Per quanto riguarda le altre risposte ... Mi aspetto che sia ovvio che qualcosa chiamato un "segreto" è dovrebbe essere tenuto segreto (dall'utente)
lschmierer

19

Le risposte esistenti che sfruttano SJCL, CryptoJS e / o WebCrypto non sono necessariamente errate ma non sono sicure come si potrebbe inizialmente sospettare. Generalmente si desidera utilizzare libsodium . Prima spiegherò perché, poi come.

Perché non SJCL, CryptoJS, WebCrypto, ecc.?

Risposta breve: Affinché la tua crittografia sia effettivamente sicura, queste librerie si aspettano che tu faccia troppe scelte, ad esempio la modalità di cifratura a blocchi (CBC, CTR, GCM; se non riesci a capire quale delle tre che ho appena elencato è sicura per uso e in quali limiti, non si dovrebbe essere gravati da questo tipo di scelta a tutti ).

A meno che il tuo incarico non sia ingegnere crittografico , le probabilità sono impilate contro di te per implementarlo in modo sicuro.

Perché evitare CryptoJS?

CryptoJS offre una manciata di blocchi predefiniti e si aspetta che tu sappia come usarli in modo sicuro. L'impostazione predefinita è anche in modalità CBC ( archiviata ).

Perché la modalità CBC è difettosa?

Leggi questo articolo sulle vulnerabilità di AES-CBC .

Perché evitare WebCrypto?

WebCrypto è uno standard avanzato, progettato dal comitato, per scopi che sono ortogonali all'ingegneria della crittografia. Nello specifico, WebCrypto doveva sostituire Flash, non fornire sicurezza .

Perché evitare SJCL?

L'API pubblica e la documentazione di SJCL invitano gli utenti a crittografare i dati con una password ricordata dall'uomo. Questo è raramente, se mai, quello che vuoi fare nel mondo reale.

Inoltre: il conteggio dei round PBKDF2 predefinito è circa 86 volte più piccolo di quanto si desideri . AES-128-CCM probabilmente sta bene.

Delle tre opzioni sopra, SJCL è la meno probabile che finisca in lacrime. Ma ci sono opzioni migliori disponibili.

Perché è meglio Libsodium?

Non è necessario scegliere tra un menu di modalità di cifratura, funzioni di hash e altre opzioni inutili. Non rischierai mai di rovinare i tuoi parametri e rimuovere tutta la sicurezza dal tuo protocollo .

Al contrario, libsodium offre solo semplici opzioni ottimizzate per la massima sicurezza e API minimaliste.

  • crypto_box()/ crypto_box_open()offre la crittografia autenticata della chiave pubblica.
    • L'algoritmo in questione combina X25519 (ECDH su Curve25519) e XSalsa20-Poly1305, ma non è necessario sapere (o preoccuparsene) per usarlo in modo sicuro
  • crypto_secretbox()/ crypto_secretbox_open()offrire crittografia autenticata con chiave condivisa.
    • L'algoritmo in questione è XSalsa20-Poly1305, ma non è necessario conoscere / preoccuparsi

Inoltre, libsodium ha legami in dozzine di linguaggi di programmazione popolari , quindi è molto probabile che libsodium funzioni solo quando si cerca di interagire con un altro stack di programmazione. Inoltre, libsodium tende ad essere molto veloce senza sacrificare la sicurezza.

Come usare Libsodium in JavaScript?

Innanzitutto, devi decidere una cosa:

  1. Vuoi solo crittografare / decrittografare i dati (e forse in qualche modo usare ancora in modo sicuro il testo normale nelle query del database) e non preoccuparti dei dettagli? O...
  2. Devi implementare un protocollo specifico?

Se hai selezionato la prima opzione , ottieni CipherSweet.js .

La documentazione è disponibile online . EncryptedFieldè sufficiente per la maggior parte dei casi d'uso, ma le API EncryptedRowe EncryptedMultiRowspotrebbero essere più facili se si hanno molti campi distinti che si desidera crittografare.

Con CipherSweet, non è nemmeno necessario sapere cosa sia un nonce / IV per utilizzarlo in modo sicuro.

Inoltre, questo gestisce int/ floatcrittografia senza perdere fatti sui contenuti attraverso la dimensione del testo cifrato.

Altrimenti, vorrai sodio-plus , che è un frontend intuitivo per vari wrapper libsodium. Sodium-Plus ti consente di scrivere codice multipiattaforma performante, asincrono, facile da controllare e ragionare.

Per installare sodium-plus, esegui semplicemente ...

npm install sodium-plus

Al momento non esiste un CDN pubblico per il supporto del browser. Questo cambierà presto. Tuttavia, si può afferrare sodium-plus.min.jsda l'ultima release Github se ne avete bisogno.

const { SodiumPlus } = require('sodium-plus');
let sodium;

(async function () {
    if (!sodium) sodium = await SodiumPlus.auto();
    let plaintext = 'Your message goes here';
    let key = await sodium.crypto_secretbox_keygen();
    let nonce = await sodium.randombytes_buf(24);
    let ciphertext = await sodium.crypto_secretbox(
        plaintext,
        nonce,
        key    
    );
    console.log(ciphertext.toString('hex'));

    let decrypted = await sodium.crypto_secretbox_open(
        ciphertext,
        nonce,
        key
    );

    console.log(decrypted.toString());
})();

La documentazione per sodium-plus è disponibile su Github.

Se desideri un tutorial passo-passo, questo articolo dev.to ha quello che stai cercando.



5

Prima di implementare tutto ciò, vedere la risposta di Scott Arciszewski .

Voglio che tu stia molto attento a ciò che sto per condividere poiché ho poca o nessuna conoscenza di sicurezza (c'è un'alta probabilità che sto usando in modo improprio l'API di seguito), quindi sarei più che benvenuto per aggiornare questa risposta con l'aiuto della comunità .

Come menzionato da @richardtallent nella sua risposta , c'è il supporto per l'API Web Crypto, quindi questo esempio usa lo standard. Al momento della stesura di questo articolo, esiste un 95,88% del supporto globale per i browser .

Condividerò un esempio usando l'API Web Crypto

Prima di procedere, notare ( Citando da MDN ):

Questa API fornisce una serie di primitive crittografiche di basso livello. È molto facile usarli in modo improprio e le insidie coinvolte possono essere molto sottili .

Anche supponendo che tu utilizzi correttamente le funzioni crittografiche di base, la gestione sicura delle chiavi e la progettazione complessiva del sistema di sicurezza sono estremamente difficili da ottenere e sono generalmente il dominio di esperti di sicurezza specializzati.

Errori nella progettazione e nell'implementazione del sistema di sicurezza possono rendere la sicurezza del sistema completamente inefficace.

Se non sei sicuro di sapere cosa stai facendo, probabilmente non dovresti utilizzare questa API .

Rispetto molto la sicurezza e ho persino messo in grassetto parti aggiuntive da MDN ... Sei stato avvertito

ora, all'esempio reale ...


JSFiddle:

Trovato qui: https://jsfiddle.net/superjose/rm4e0gqa/5/

Nota:

Nota l'uso di awaitparole chiave. Usalo all'interno di una asyncfunzione o usa .then()e.catch() .

Genera la chiave:

// https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    const stringToEncrypt = 'https://localhost:3001';
    // https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    // The resultant publicKey will be used to encrypt
    // and the privateKey will be used to decrypt. 
    // Note: This will generate new keys each time, you must store both of them in order for 
    // you to keep encrypting and decrypting.
    //
    // I warn you that storing them in the localStorage may be a bad idea, and it gets out of the scope
    // of this post. 
    const key = await crypto.subtle.generateKey({
      name: 'RSA-OAEP',
      modulusLength: 4096,
      publicExponent:  new Uint8Array([0x01, 0x00, 0x01]),
      hash: {name: 'SHA-512'},
      
    }, true,
    // This depends a lot on the algorithm used
    // Go to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
    // and scroll down to see the table. Since we're using RSA-OAEP we have encrypt and decrypt available
    ['encrypt', 'decrypt']);

    // key will yield a key.publicKey and key.privateKey property.

Encrypt:

    const encryptedUri = await crypto.subtle.encrypt({
      name: 'RSA-OAEP'
    }, key.publicKey, stringToArrayBuffer(stringToEncrypt))
    
    console.log('The encrypted string is', encryptedUri);

Decrypt

   const msg = await  crypto.subtle.decrypt({
      name: 'RSA-OAEP',
    }, key.privateKey, encryptedUri);
    console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)

Conversione di ArrayBuffer avanti e indietro da String (Fine in TypeScript):

  private arrayBufferToString(buff: ArrayBuffer) {
    return String.fromCharCode.apply(null, new Uint16Array(buff) as unknown as number[]);
  }

  private stringToArrayBuffer(str: string) {
    const buff = new ArrayBuffer(str.length*2) // Because there are 2 bytes for each char.
    const buffView = new Uint16Array(buff);
    for(let i = 0, strLen = str.length; i < strLen; i++) {
      buffView[i] = str.charCodeAt(i);
    }
    return buff;
  }

Puoi trovare altri esempi qui (non sono il proprietario): // https://github.com/diafygi/webcrypto-examples


2

CryptoJS non è più supportato. Se vuoi continuare a usarlo, puoi passare a questo URL:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>


Qual è la differenza tra rollup e componenti nella cartella 3.1.2?
Kanagavelu Sugumar,

1
Crypto raccomanda la libreria di forge quando si accede al loro sito.
Dror Bar

1

Usa SimpleCrypto

Utilizzo di encrypt () e decrypt ()

Per utilizzare SimpleCrypto, creare prima un'istanza SimpleCrypto con una chiave segreta (password). Il parametro chiave segreta DEVE essere definito durante la creazione di un'istanza SimpleCrypto.

Per crittografare e decrittografare i dati, utilizzare semplicemente la funzione encrypt () e decrypt () da un'istanza. Questo utilizzerà l'algoritmo di crittografia AES-CBC.

var _secretKey = "some-unique-key";

var simpleCrypto = new SimpleCrypto(_secretKey);

var plainText = "Hello World!";
var chiperText = simpleCrypto.encrypt(plainText);
console.log("Encryption process...");
console.log("Plain Text    : " + plainText);
console.log("Cipher Text   : " + cipherText);
var decipherText = simpleCrypto.decrypt(cipherText);
console.log("... and then decryption...");
console.log("Decipher Text : " + decipherText);
console.log("... done.");

3
SimpleCrypto utilizza AES-CBC non autenticato ed è quindi vulnerabile agli attacchi di testo cifrato scelti.
Scott Arciszewski,

-6

Funzioni semplici,


function Encrypt(value) 
{
  var result="";
  for(i=0;i<value.length;i++)
  {
    if(i<value.length-1)
    {
        result+=value.charCodeAt(i)+10;
        result+="-";
    }
    else
    {
        result+=value.charCodeAt(i)+10;
    }
  }
  return result;
}
function Decrypt(value)
{
  var result="";
  var array = value.split("-");

  for(i=0;i<array.length;i++)
  {
    result+=String.fromCharCode(array[i]-10);
  }
  return result;
} 

4
Sebbene questo frammento di codice possa essere la soluzione, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Johan

1
Questo non è un algoritmo sicuro (nota che Encrypt non sta prendendo un parametro chiave) e può essere facilmente invertito. L'OP ha chiesto qualcosa che contenesse sicurezza.
Mike S,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.