Criptografia e descriptografia de string JavaScript?


153

Estou interessado em criar um pequeno aplicativo para uso pessoal que criptografará e descriptografará informações no lado do cliente usando JavaScript. As informações criptografadas serão armazenadas em um banco de dados em um servidor, mas nunca a versão descriptografada.

Ele não precisa ser super seguro, mas eu gostaria de usar um algoritmo ininterrupto no momento.

Idealmente, eu seria capaz de fazer algo como

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

para gerar a string codificada e algo como

var sensical = decrypt(gibberish, key);

decodificá-lo mais tarde.

Até agora eu já vi isso: http://bitwiseshiftleft.github.io/sjcl/

Alguma outra biblioteca que eu deveria procurar?


2
Dê uma olhada em Javascript AES criptografia
— kevinji


10
Alguma terminologia aqui está desativada. Aqui está uma versão simples 1. Sais são adicionados às informações (geralmente senhas) que estão sendo hash. Seu objetivo é tornar o hash diferente do que seria sem o sal. Isso é útil porque torna pré-gerados os hashes se o seu banco de dados for invadido e as senhas dos usuários com hash forem divulgadas. 2. Hashing é uma operação unidirecional que traduz entrada em saída. Não pode ser facilmente revertido ou desfeito. 3. Codificação não é criptografia. base64_encode, urlencode, etc.
— des

Respostas:


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
Criptografado é realmente um objeto, mas você pode chamar encrypted.toString () para obter a sequência. Você poderá descriptografar essa sequência mais tarde: jsbin.com/kofiqokoku/1
— Tomas Kirda 8/15/15

9
Mas como podemos proteger a senha secreta?
— duykhoa

9
Parece que o crypto js é um projeto arquivado. Há um clone no github: github.com/sytelus/CryptoJS, mas não foi atualizado em dois anos. Essa ainda é a melhor opção para criptografia js?
— 29515 syonip

2
Eu iria com este: github.com/brix/crypto-js também está disponível via NPM
— Tomas Kirda

1
@ depende de você como e onde armazená-lo. Não sei se existe uma maneira realmente segura de armazená-lo em um navegador. Solicite-os ao servidor e armazene-os na memória.
— Tomas Kirda 14/02/19

62

E o CryptoJS ?

É uma biblioteca de criptografia sólida, com muitas funcionalidades. Ele implementa hashers, HMAC, PBKDF2 e cifras. Nesse caso, as cifras são o que você precisa. Confira o guia de início rápido na página inicial do projeto.

Você poderia fazer algo parecido com o 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>

Quanto à segurança, no momento em que escrevo, o algoritmo AES é considerado ininterrupto

Editar:

Parece que o URL on-line está inoperante e você pode usar os arquivos baixados para criptografia a partir do link fornecido abaixo e colocar os respectivos arquivos na pasta raiz do aplicativo.

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

ou usou outro CDN como https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js


Qual é a diferença entre rollups e componentes na pasta 3.1.2?
— Kanagavelu Sugumar

Depois de tocar um pouco, os componentes são as partes separadas. Você precisará saber quais componentes levar (e em que ordem) para fazê-lo funcionar. Os arquivos de rollups contêm tudo o que você precisa para fazê-lo funcionar com apenas uma referência de script (muito melhor porque o trabalho duro já foi feito).
— Shahar Eldad

2
Mas como podemos proteger a senha secreta?
— shaijut 14/02/19

@shaijut Você não. Você nem a salva em nenhum lugar, exceto na RAM, ao criptografar / descriptografar o texto sem formatação. A senha deve ser armazenada apenas no cérebro do usuário (ou em um gerenciador de senhas)
— slebetman 14/01

39

Eu criei um utilitário de codificação / decifração de texto inseguro, mas simples. Não há dependências com nenhuma biblioteca externa.

Estas são as funções

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 você pode usá-los da seguinte maneira:

// 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
deixe myDecipher = decifrar ('CartelSystem') - Esse sal também decifrará a string. Você não precisa saber a palavra exata 'mySecretSalt'
— Dror Bar

Além disso, o saltChars em decifração não está sendo usado?
— Dror Bar

1
Mais um post onde alguém está usando cegamente let.
— John

1
Não é isso a) super quebrado e inseguro eb) o 'sal' é de fato a sua 'chave secreta', pois não se espera que os sais sejam privados? Eu acho que é muito perigoso publicar um código como esse sem nenhum comentário de que esse código divertido não seja para uso no mundo real. A quantidade de votos positivos é preocupante. crypto.stackexchange.com/questions/11466/…
— lschmierer em 21/04

1
Bem, pelo menos eles usam criptografia de som. O que você está fazendo é basicamente um Caesar Chipher (aplicando a mesma chave para cada personagem) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Em relação às outras respostas ... Eu esperava que fosse óbvio que algo chamado um "segredo" é esperado para ser mantido em segredo (pelo usuário)
— lschmierer 24/04

19

As respostas existentes que utilizam SJCL, CryptoJS e / ou WebCrypto não estão necessariamente erradas, mas não são tão seguras quanto você pode suspeitar inicialmente. Geralmente você deseja usar libsodium . Primeiro vou explicar o porquê, depois como.

Por que não SJCL, CryptoJS, WebCrypto, etc.?

Resposta curta: para que sua criptografia seja realmente segura, essas bibliotecas esperam que você faça muitas escolhas, por exemplo, o modo de cifra em bloco (CBC, CTR, GCM; se você não souber qual dos três que acabei de listar é seguro) uso e sob quais restrições, você não deve ser sobrecarregada com esse tipo de escolha em tudo ).

A menos que o seu cargo seja engenheiro de criptografia , as probabilidades estão contra a sua implementação segura.

Por que evitar o CryptoJS?

O CryptoJS oferece vários blocos de construção e espera que você saiba como usá-los com segurança. O padrão é o modo CBC ( arquivado ).

Por que o modo CBC é ruim?

Leia este artigo sobre as vulnerabilidades do AES-CBC .

Por que evitar o WebCrypto?

O WebCrypto é um padrão potluck, projetado pelo comitê, para propósitos ortogonais à engenharia de criptografia. Especificamente, o WebCrypto foi criado para substituir o Flash, não para fornecer segurança .

Por que evitar o SJCL?

A API pública e a documentação da SJCL pedem que os usuários criptografem dados com uma senha lembrada por humanos. Isso raramente é o que você deseja fazer no mundo real.

Além disso: sua contagem padrão de rodadas PBKDF2 é aproximadamente 86 vezes menor que você deseja . AES-128-CCM provavelmente está bem.

Das três opções acima, o SJCL é o menos provável de terminar em lágrimas. Mas existem melhores opções disponíveis.

Por que Libsodium é melhor?

Você não precisa escolher entre um menu de modos de cifra, funções de hash e outras opções desnecessárias. Você nunca arriscará estragar seus parâmetros e remover toda a segurança do seu protocolo .

Em vez disso, libsodium oferece apenas opções simples ajustadas para segurança máxima e APIs minimalistas.

  • crypto_box()/ crypto_box_open()ofereço criptografia de chave pública autenticada.
    • O algoritmo em questão combina o X25519 (ECDH sobre o Curve25519) e o XSalsa20-Poly1305, mas você não precisa saber (nem se importar) sobre isso para usá-lo com segurança
  • crypto_secretbox()/ crypto_secretbox_open()ofereça criptografia autenticada por chave compartilhada.
    • O algoritmo em questão é XSalsa20-Poly1305, mas você não precisa saber / cuidar

Além disso, o libsodium possui ligações em dezenas de linguagens de programação populares , portanto, é muito provável que o libsodium funcione apenas ao tentar interoperar com outra pilha de programação. Além disso, o libsodium tende a ser muito rápido sem sacrificar a segurança.

Como usar Libsodium em JavaScript?

Primeiro, você precisa decidir uma coisa:

  1. Deseja apenas criptografar / descriptografar dados (e talvez ainda usar de alguma forma o texto sem formatação nas consultas do banco de dados com segurança) e não se preocupar com os detalhes? Ou...
  2. Você precisa implementar um protocolo específico?

Se você selecionou a primeira opção , obtenha o CipherSweet.js .

A documentação está disponível online . EncryptedFieldé suficiente para a maioria dos casos de uso, mas as APIs EncryptedRowe EncryptedMultiRowspodem ser mais fáceis se você tiver muitos campos distintos que deseja criptografar.

Com o CipherSweet, você nem precisa saber o que é um nonce / IV para usá-lo com segurança.

Além disso, este alças int/ floatcriptografia sem vazamento fatos sobre o conteúdo através de tamanho texto cifrado.

Caso contrário, você desejará o sódio plus , que é uma interface amigável para vários invólucros libsodium. O Sodium-Plus permite que você escreva código de plataforma cruzada, assíncrono e de alto desempenho, fácil de auditar e de raciocinar.

Para instalar o sódio-plus, basta executar ...

npm install sodium-plus

Atualmente, não há CDN pública para suporte ao navegador. Isso mudará em breve. No entanto, você pode pegar sodium-plus.min.jsa partir da versão mais recente Github se você precisar dele.

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

A documentação para o sódio-plus está disponível no Github.

Se você deseja um tutorial passo a passo, este artigo do dev.to tem o que você está procurando.



5

Antes de implementar isso, consulte a resposta de Scott Arciszewski .

Quero que você tenha muito cuidado com o que estou prestes a compartilhar, pois tenho pouco ou nenhum conhecimento de segurança (há uma grande chance de estar usando mal a API abaixo), por isso seria mais que bem-vindo para atualizar esta resposta com a ajuda da comunidade .

Como @richardtallent mencionado em sua resposta , há suporte para a API Web Crypto, portanto, este exemplo usa o padrão. Até o momento da redação deste artigo, 95,88% do suporte global a navegadores .

Vou compartilhar um exemplo usando a API Web Crypto

Antes de prosseguir, observe ( citando na MDN ):

Essa API fornece vários primitivos criptográficos de baixo nível. É muito fácil usá-los mal , e as armadilhas envolvidas podem ser muito sutis .

Mesmo supondo que você use as funções criptográficas básicas corretamente, o gerenciamento de chaves seguro e o design geral do sistema de segurança são extremamente difíceis de acertar e geralmente são de domínio de especialistas em segurança especializados.

Erros no design e implementação do sistema de segurança podem tornar a segurança do sistema completamente ineficaz.

Se você não tem certeza de que sabe o que está fazendo, provavelmente não deve usar esta API .

Eu respeito muito a segurança e até atrevi partes adicionais da MDN ... Você foi avisado

agora, para o exemplo real ...


JSFiddle:

Encontrado aqui: https://jsfiddle.net/superjose/rm4e0gqa/5/

Nota:

Observe o uso de awaitpalavras-chave. Use-o dentro de uma asyncfunção ou use .then()e .catch().

Gere a chave:

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

Criptografar:

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


Descriptografar

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

Convertendo ArrayBuffer para frente e para trás a partir de String (concluído em 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;
  }

Você pode encontrar mais exemplos aqui (eu não sou o proprietário): // https://github.com/diafygi/webcrypto-examples


2

O CryptoJS não é mais suportado. Se você quiser continuar usando, você pode mudar para este URL:

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


Qual é a diferença entre rollups e componentes na pasta 3.1.2?
— Kanagavelu Sugumar 10/08

1
A Crypto está recomendando a biblioteca do forge quando você entra no site.
— Dror Bar

1

Use SimpleCrypto

Usando encrypt () e decrypt ()

Para usar o SimpleCrypto, primeiro crie uma instância do SimpleCrypto com uma chave secreta (senha). Parâmetro da chave secreta DEVE ser definido ao criar uma instância SimpleCrypto.

Para criptografar e descriptografar dados, basta usar a função encrypt () e decrypt () de uma instância. Isso usará o algoritmo de criptografia 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
O SimpleCrypto usa AES-CBC não autenticado e, portanto, é vulnerável a ataques de texto cifrado escolhido.
— Scott Arciszewski

-6

Funções 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;
} 

4
Embora esse snippet de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
— Johan

1
Este não é um algoritmo seguro (observe que Criptografar não está usando um parâmetro-chave) e pode ser facilmente revertido. O OP pediu algo que tivesse segurança.
— Mike S
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.