Cifrado y descifrado de cadenas de JavaScript?

153

Estoy interesado en crear una pequeña aplicación para uso personal que cifre y descifre información del lado del cliente mediante JavaScript. La información cifrada se almacenará en una base de datos en un servidor, pero nunca la versión descifrada.

No tiene que ser súper seguro, pero me gustaría usar un algoritmo ininterrumpido actualmente.

Lo ideal sería poder hacer algo como

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

para generar la cadena codificada, y algo así como

var sensical = decrypt(gibberish, key);

para decodificarlo más tarde.

Hasta ahora he visto esto: http://bitwiseshiftleft.github.io/sjcl/

¿Alguna otra biblioteca que debería mirar?

jeremiahs
fuente
2
Eche un vistazo al cifrado Javascript AES
kevinji
10
Parte de la terminología aquí está desactivada. Aquí hay una versión simple 1. Las sales se agregan a la información (generalmente las contraseñas) que se codifica. Su propósito es hacer que el hash sea diferente de lo que sería sin la sal. Esto es útil porque genera hashes pregenerados si su base de datos es pirateada y se eliminan las contraseñas hash del usuario. 2. El hash es una operación unidireccional que traduce la entrada en salida. No se puede revertir o deshacer fácilmente. 3. La codificación no es encriptación. base64_encode, urlencode, etc.
des

Respuestas:

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>

Tomás Kirda
fuente
8
Encriptado es en realidad un objeto, pero puede llamar a encrypted.toString () para obtener la cadena. Podrá descifrar esa cadena más tarde: jsbin.com/kofiqokoku/1
Tomas Kirda
9
Pero, ¿cómo podemos asegurar la frase secreta?
duykhoa
9
Parece que crypto js es un proyecto archivado. Hay un clon en github: github.com/sytelus/CryptoJS pero no se ha actualizado en dos años. ¿Sigue siendo la mejor opción para el cifrado js?
syonip
2
Iría con este: github.com/brix/crypto-js también está disponible a través de NPM
Tomas Kirda
1
@stom depende de usted cómo y dónde lo almacene. No sé si hay una forma realmente segura de almacenarlo en un navegador. Solicítelos del servidor y guárdelos en la memoria.
Tomas Kirda
62

¿Qué tal CryptoJS ?

Es una biblioteca criptográfica sólida, con mucha funcionalidad. Implementa hashers, HMAC, PBKDF2 y cifrados. En este caso, las cifras son lo que necesita. Consulte la guía de inicio rápido en la página de inicio del proyecto.

Podrías hacer algo como con el 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>

En cuanto a la seguridad, en el momento en que escribo el algoritmo AES se considera ininterrumpido.

Editar:

Parece que la URL en línea está inactiva y puede usar los archivos descargados para el cifrado desde el siguiente enlace y colocar los archivos respectivos en su carpeta raíz de la aplicación.

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

o usó otro CDN como https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js

ovidb
fuente
¿Cuál es la diferencia entre paquetes acumulativos y componentes en la carpeta 3.1.2?
Kanagavelu Sugumar
Después de jugar un poco, los componentes son las partes separadas. Necesitará saber qué componentes tomar (y en qué orden) para que funcione. Los archivos de resumen contienen todo lo que necesita para que funcione con una sola referencia de script (mucho mejor ya que el trabajo duro ya está hecho).
shahar eldad
2
Pero, ¿cómo podemos asegurar la frase secreta?
shaijut
@shaijut No lo haces. Ni siquiera lo guarda en ningún lugar, excepto en la RAM al cifrar / descifrar el texto sin formato. La frase de contraseña solo debe almacenarse en el cerebro del usuario (o en un administrador de contraseñas)
slebetman
39

Creé una utilidad de cifrado / descifrado de texto insegura pero simple. Sin dependencias con ninguna biblioteca externa.

Estas son las funciones.

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('');
}

Y puede usarlos de la siguiente manera:

// 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'
Jorgeblom
fuente
44
let myDecipher = decipher ('CartelSystem'): esta sal también descifrará la cadena. No tiene que saber la palabra exacta 'mySecretSalt'
Dror Bar
Además, ¿no se utilizan saltChars en descifrado?
Dror Bar
1
Otra publicación más donde alguien está usando ciegamente let. 😒︎
John
1
¿No es esto a) súper roto e inseguro yb) la 'sal' es de hecho su 'clave secreta' ya que no se espera que las sales sean privadas? Creo que es muy peligroso publicar un código como este sin ningún comentario de que este código divertido no está destinado a ningún uso en el mundo real. La cantidad de votos positivos es preocupante. crypto.stackexchange.com/questions/11466/…
lschmierer
1
Bueno, al menos usan sonido criptográfico. Lo que está haciendo es básicamente un Caesar Chipher (aplicando la misma clave a cada personaje) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher En cuanto a las otras respuestas ... espero que sea obvio que algo llamado "secreto" es se espera que se mantenga en secreto (por el usuario)
lschmierer
19

Las respuestas existentes que aprovechan SJCL, CryptoJS y / o WebCrypto no son necesariamente incorrectas, pero no son tan seguras como podría sospechar inicialmente. Generalmente desea usar libsodium . Primero explicaré por qué, luego cómo.

¿Por qué no SJCL, CryptoJS, WebCrypto, etc.?

Respuesta corta: para que su cifrado sea realmente seguro, estas bibliotecas esperan que tome demasiadas opciones, por ejemplo, el modo de cifrado de bloque (CBC, CTR, GCM; si no puede determinar cuál de los tres que acabo de enumerar es seguro uso y bajo qué restricciones, no debería estar abrumado con este tipo de elección en absoluto ).

A menos que su título de trabajo sea ingeniero de criptografía , las probabilidades están en contra de que lo implemente de manera segura.

¿Por qué evitar CryptoJS?

CryptoJS ofrece un puñado de bloques de construcción y espera que sepa cómo usarlos de forma segura. Incluso por defecto es el modo CBC ( archivado ).

¿Por qué es malo el modo CBC?

Lea este artículo sobre vulnerabilidades AES-CBC .

¿Por qué evitar WebCrypto?

WebCrypto es un estándar potluck, diseñado por un comité, para fines ortogonales a la ingeniería de criptografía. Específicamente, WebCrypto estaba destinado a reemplazar Flash, no a proporcionar seguridad .

¿Por qué evitar SJCL?

La API pública y la documentación de SJCL ruegan a los usuarios que cifren los datos con una contraseña recordada por humanos. Esto es raramente, si alguna vez, lo que quieres hacer en el mundo real.

Además: Su conteo de rondas PBKDF2 predeterminado es aproximadamente 86 veces más pequeño de lo que desea que sea . AES-128-CCM probablemente esté bien.

De las tres opciones anteriores, SJCL es la menos propensa a terminar en lágrimas. Pero hay mejores opciones disponibles.

¿Por qué es mejor Libsodium?

No necesita elegir entre un menú de modos de cifrado, funciones hash y otras opciones innecesarias. Nunca se arriesgará a arruinar sus parámetros y eliminar toda la seguridad de su protocolo .

En cambio, libsodium solo le brinda opciones simples ajustadas para máxima seguridad y API minimalistas.

  • crypto_box()/ crypto_box_open()ofrecer cifrado de clave pública autenticado.
    • El algoritmo en cuestión combina X25519 (ECDH sobre Curve25519) y XSalsa20-Poly1305, pero no necesita saber (ni siquiera preocuparse) sobre eso para usarlo de forma segura
  • crypto_secretbox()/ crypto_secretbox_open()ofrecer cifrado autenticado de clave compartida.
    • El algoritmo en cuestión es XSalsa20-Poly1305, pero no necesita saberlo / cuidarlo

Además, libsodium tiene enlaces en docenas de lenguajes de programación populares , por lo que es muy probable que libsodium solo funcione cuando intente interactuar con otra pila de programación. Además, libsodium tiende a ser muy rápido sin sacrificar la seguridad.

¿Cómo usar Libsodium en JavaScript?

Primero, debes decidir una cosa:

  1. ¿Solo desea cifrar / descifrar datos (y tal vez de alguna manera utilizar el texto sin formato en las consultas de la base de datos de forma segura) y no preocuparse por los detalles? O...
  2. ¿Necesita implementar un protocolo específico?

Si seleccionó la primera opción , obtenga CipherSweet.js .

La documentación está disponible en línea . EncryptedFieldes suficiente para la mayoría de los casos de uso, pero las API EncryptedRowy EncryptedMultiRowspueden ser más fáciles si tiene muchos campos distintos que desea cifrar.

Con CipherSweet, ni siquiera necesita saber qué es un nonce / IV para usarlo de forma segura.

Además, esto maneja int/ floatencripta sin filtrar datos sobre el contenido a través del tamaño del texto cifrado.

De lo contrario, querrá sodio plus , que es una interfaz fácil de usar para varios envoltorios de libsodio. Sodium-Plus le permite escribir código multiplataforma asíncrono y de rendimiento que sea fácil de auditar y razonar.

Para instalar sodium-plus, simplemente ejecute ...

npm install sodium-plus

Actualmente no hay una CDN pública para el soporte del navegador. Esto cambiará pronto. Sin embargo, se puede agarrar sodium-plus.min.jsde la última versión de Github si lo necesita.

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 documentación para sodio plus está disponible en Github.

Si desea un tutorial paso a paso, este artículo dev.to tiene lo que está buscando.

Scott Arciszewski
fuente
16

Los navegadores modernos ahora admiten la crypto.subtleAPI, que proporciona funciones nativas de cifrado y descifrado (¡asíncrono no menos!) Utilizando uno de estos métodos: AES-CBC, AES-CTR, AES-GCM o RSA-OAEP.

https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto

richardtallent
fuente
3
De las opciones anteriores, solo AES-GCM y RSA-OAEP son razonables. :(
Scott Arciszewski
5

Antes de implementar algo de esto, vea la respuesta de Scott Arciszewski .

Quiero que tengas mucho cuidado con lo que estoy a punto de compartir, ya que tengo poco o ningún conocimiento de seguridad (hay una gran posibilidad de que esté haciendo un mal uso de la API a continuación), por lo que sería más que bienvenido para actualizar esta respuesta con la ayuda de la comunidad .

Como @richardtallent mencionó en su respuesta , hay soporte para Web Crypto API, por lo que este ejemplo utiliza el estándar. Al momento de escribir este artículo, hay un 95.88% de soporte de navegador global .

Voy a compartir un ejemplo usando la Web Crypto API

Antes de continuar, tenga en cuenta ( Citando de MDN ):

Esta API proporciona una serie de primitivas criptográficas de bajo nivel. Es muy fácil usarlos mal , y las trampas involucradas pueden ser muy sutiles .

Incluso suponiendo que utilice correctamente las funciones criptográficas básicas, la administración segura de claves y el diseño general del sistema de seguridad son extremadamente difíciles de corregir y generalmente son del dominio de expertos en seguridad especializados.

Los errores en el diseño y la implementación del sistema de seguridad pueden hacer que la seguridad del sistema sea completamente ineficaz.

Si no está seguro de saber lo que está haciendo, probablemente no debería usar esta API .

Respeto mucho la seguridad, e incluso agregué piezas adicionales de MDN ... Te han advertido

ahora, para el ejemplo real ...


JSFiddle:

Encontrado aquí: https://jsfiddle.net/superjose/rm4e0gqa/5/

Nota:

Tenga en cuenta el uso de awaitpalabras clave. Úselo dentro de una asyncfunción o use .then()y .catch().

Genera la clave:

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

Cifrar:

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

Descifrar

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

Convertir ArrayBuffer de ida y vuelta desde String (hecho en 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;
  }

Puede encontrar más ejemplos aquí (no soy el propietario): // https://github.com/diafygi/webcrypto-examples

Jose A
fuente
2

CryptoJS ya no es compatible. Si desea continuar usándolo, puede cambiar a esta url:

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

Dmytro Lopushanskyy
fuente
¿Cuál es la diferencia entre paquetes acumulativos y componentes en la carpeta 3.1.2?
Kanagavelu Sugumar
1
Crypto recomienda la biblioteca de falsificaciones cuando ingrese a su sitio.
Dror Bar
1

Use SimpleCrypto

Usando encrypt () y decrypt ()

Para usar SimpleCrypto, primero cree una instancia de SimpleCrypto con una clave secreta (contraseña). El parámetro de clave secreta DEBE definirse al crear una instancia de SimpleCrypto.

Para cifrar y descifrar datos, simplemente use la función cifrar () y descifrar () de una instancia. Esto utilizará el algoritmo de cifrado 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.");
Murtaza Hussain
fuente
3
SimpleCrypto utiliza AES-CBC no autenticado y, por lo tanto, es vulnerable a los ataques de texto cifrado elegidos.
Scott Arciszewski el
-6

Funciones simples,


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;
} 
Omi
fuente
44
Si bien este fragmento de código puede ser la solución, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
Johan
1
Este no es un algoritmo seguro (tenga en cuenta que Encrypt no está tomando un parámetro clave) y puede invertirse fácilmente en ingeniería. El OP pidió algo que tuviera seguridad.
Mike S