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?

jeremiahs
fonte
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>

Tomas Kirda
fonte
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

ovidb
fonte
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'
Jorgeblom
fonte
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.

Scott Arciszewski
fonte
16

Os navegadores modernos agora oferecem suporte à crypto.subtleAPI, que fornece funções nativas de criptografia e descriptografia (assíncronas!) Usando um destes métodos: AES-CBC, AES-CTR, AES-GCM ou RSA-OAEP.

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

richardtallent
fonte
3
Das opções acima, apenas o AES-GCM e o RSA-OAEP são razoáveis. :(
Scott Arciszewski
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

Jose A
fonte
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>

Dmytro Lopushanskyy
fonte
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.");
Murtaza Hussain
fonte
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;
} 
Omi
fonte
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