Szyfrowanie i deszyfrowanie ciągów JavaScript?

152

Jestem zainteresowany zbudowaniem małej aplikacji do użytku osobistego, która będzie szyfrować i odszyfrowywać informacje po stronie klienta za pomocą JavaScript. Zaszyfrowane informacje będą przechowywane w bazie danych na serwerze, ale nigdy w wersji odszyfrowanej.

Nie musi to być super bezpieczne, ale chciałbym użyć obecnie nieprzerwanego algorytmu.

Idealnie byłbym w stanie zrobić coś takiego

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

aby wygenerować zakodowany ciąg i coś podobnego

var sensical = decrypt(gibberish, key);

aby później zdekodować.

Do tej pory widziałem to: http://bitwiseshiftleft.github.io/sjcl/

Jakieś inne biblioteki, którym powinienem zajrzeć?

jeremiahs
źródło
2
Spójrz na szyfrowanie AES
kevinji
10
Pewna terminologia jest tutaj wyłączona. Oto prosta wersja 1. Sole są dodawane do haszowanych informacji (zwykle haseł). Ich celem jest sprawienie, aby hasz był inny niż byłby bez soli. Jest to przydatne, ponieważ tworzy wstępnie generowane skróty, jeśli Twoja baza danych zostanie zhakowana i zahaszowane hasła użytkowników zostaną usunięte. 2. Haszowanie jest operacją jednokierunkową, która przekształca dane wejściowe na dane wyjściowe. Nie można go łatwo cofnąć ani cofnąć. 3. Kodowanie to nie szyfrowanie. base64_encode, urlencode, itd.
des

Odpowiedzi:

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
źródło
8
Encrypted jest w rzeczywistości obiektem, ale możesz wywołać encrypted.toString (), aby uzyskać ciąg. Będziesz mógł odszyfrować ten ciąg później: jsbin.com/kofiqokoku/1
Tomas Kirda
9
Ale jak możemy zabezpieczyć tajne hasło?
duykhoa
9
Wygląda na to, że crypto js to zarchiwizowany projekt. Na github znajduje się klon: github.com/sytelus/CryptoJS, ale nie był aktualizowany od dwóch lat. Czy to nadal najlepsza opcja dla szyfrowania js?
syonip
2
Poszedłbym z tym: github.com/brix/crypto-js jest również dostępny przez NPM
Tomas Kirda
1
@stom to od Ciebie zależy, jak i gdzie będziesz je przechowywać. Nie wiem, czy istnieje naprawdę bezpieczny sposób przechowywania go w przeglądarce. Zażądaj ich od serwera i zapisz w pamięci.
Tomas Kirda,
62

A co z CryptoJS ?

To solidna biblioteka kryptograficzna z wieloma funkcjami. Implementuje hashery, HMAC, PBKDF2 i szyfry. W tym przypadku potrzebne są szyfry. Zapoznaj się z poradnikiem szybkiego startu na stronie głównej projektu.

Możesz zrobić coś takiego z 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>

Jeśli chodzi o bezpieczeństwo, w momencie pisania algorytmu AES uważam, że jest nieprzerwany

Edytować :

Wydaje się, że internetowy adres URL nie działa i możesz użyć pobranych plików do szyfrowania z poniższego linku i umieścić odpowiednie pliki w folderze głównym aplikacji.

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

lub używał innego CDN, takiego jak https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js

ovidb
źródło
Jaka jest różnica między pakietami zbiorczymi a komponentami w folderze 3.1.2?
Kanagavelu Sugumar
Po odrobinie gry elementy są oddzielnymi częściami. Będziesz musiał wiedzieć, które składniki wziąć (iw jakiej kolejności), aby działało. Pliki zbiorcze zawierają wszystko, czego potrzebujesz, aby działały, za pomocą tylko jednego odwołania do skryptu (znacznie lepiej, ponieważ ciężka praca jest już wykonana).
shahar eldad
2
Ale jak możemy zabezpieczyć tajne hasło?
shaijut
@shaijut Ty nie. Nie zapisujesz go nawet nigdzie poza pamięcią RAM podczas szyfrowania / deszyfrowania zwykłego tekstu. Hasło powinno być przechowywane tylko w mózgu użytkownika (lub menedżerze haseł)
slebetman
39

Stworzyłem niezabezpieczone, ale proste narzędzie do szyfrowania / odszyfrowywania tekstu. Brak zależności z żadną zewnętrzną biblioteką.

To są funkcje

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

Możesz ich używać w następujący sposób:

// 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
źródło
4
let myDecipher = decipher ('CartelSystem') - Ta sól również odszyfruje ciąg. Nie musisz znać dokładnego słowa „mySecretSalt”
Dror Bar
Ponadto, czy saltChars w odszyfrowaniu nie są używane?
Dror Bar
1
Kolejny post, w którym ktoś ślepo używa let. 😒︎
Jan
1
Czy to nie jest a) super zepsute i niepewne b) „sól” jest w rzeczywistości twoim „tajnym kluczem”, ponieważ nie oczekuje się, że sole będą prywatne? Myślę, że bardzo niebezpieczne jest wysyłanie takiego kodu bez żadnych komentarzy, że ten zabawny kod nie jest przeznaczony do żadnego użytku w prawdziwym świecie. Ilość głosów za jest niepokojąca. crypto.stackexchange.com/questions/11466/ ...
lschmierer
1
Przynajmniej używają kryptografii dźwiękowej. To, co robisz, jest w zasadzie szyfrem Cezara (stosując ten sam klucz do każdej postaci) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Jeśli chodzi o inne odpowiedzi ... Oczekuję, że będzie oczywiste, że coś, co nazywa się „sekretem”, to oczekuje się, że będzie trzymany w tajemnicy (przez użytkownika)
lschmierer
18

Istniejące odpowiedzi, które wykorzystują SJCL, CryptoJS i / lub WebCrypto, niekoniecznie są błędne, ale nie są tak bezpieczne, jak można początkowo podejrzewać. Generalnie chcesz używać libsodium . Najpierw wyjaśnię dlaczego, a potem jak.

Dlaczego nie SJCL, CryptoJS, WebCrypto itp.?

Krótka odpowiedź: aby Twoje szyfrowanie było faktycznie bezpieczne, te biblioteki oczekują, że dokonasz zbyt wielu wyborów, np. Tryb szyfrowania blokowego (CBC, CTR, GCM; jeśli nie możesz powiedzieć, który z trzech wymienionych powyżej jest bezpieczny zastosowanie i pod jakimi ograniczeniami, nie powinna być obarczona tego rodzaju wyboru w ogóle ).

Jeśli nie zajmujesz stanowiska inżyniera kryptografii , istnieje duże prawdopodobieństwo, że zaimplementujesz je bezpiecznie.

Dlaczego unikać CryptoJS?

CryptoJS oferuje garść bloków konstrukcyjnych i oczekuje, że będziesz wiedział, jak z nich bezpiecznie korzystać. Nawet domyślnie działa w trybie CBC ( zarchiwizowany ).

Dlaczego tryb CBC jest zły?

Przeczytaj ten artykuł na temat luk w zabezpieczeniach AES-CBC .

Dlaczego unikać WebCrypto?

WebCrypto to potluck standard, zaprojektowany przez komitet do celów ortogonalnych do inżynierii kryptograficznej. W szczególności WebCrypto miało zastąpić Flash, a nie zapewniać bezpieczeństwa .

Dlaczego unikać SJCL?

Publiczny interfejs API i dokumentacja SJCL błaga użytkowników o szyfrowanie danych za pomocą hasła zapamiętanego przez człowieka. Rzadko, jeśli w ogóle, chcesz to robić w prawdziwym świecie.

Dodatkowo: jego domyślna liczba rund PBKDF2 jest około 86 razy mniejsza, niż chcesz . AES-128-CCM prawdopodobnie jest w porządku.

Spośród trzech powyższych opcji SJCL najrzadziej kończy się łzami. Ale są dostępne lepsze opcje.

Dlaczego Libsodium jest lepszy?

Nie musisz wybierać między menu trybów szyfrowania, funkcjami skrótu i ​​innymi niepotrzebnymi opcjami. Nigdy nie ryzykujesz zepsucia parametrów i usunięcia wszystkich zabezpieczeń z protokołu .

Zamiast tego libsodium oferuje proste opcje dostrojone pod kątem maksymalnego bezpieczeństwa i minimalistycznych interfejsów API.

  • crypto_box()/ crypto_box_open()oferują uwierzytelnione szyfrowanie z kluczem publicznym.
    • Omawiany algorytm łączy X25519 (ECDH over Curve25519) i XSalsa20-Poly1305, ale nie musisz o tym wiedzieć (ani nawet przejmować się), aby go bezpiecznie używać
  • crypto_secretbox()/ crypto_secretbox_open()oferują szyfrowanie z uwierzytelnianiem za pomocą klucza wspólnego.
    • Omawiany algorytm to XSalsa20-Poly1305, ale nie musisz o tym wiedzieć / dbać

Dodatkowo libsodium ma powiązania w dziesiątkach popularnych języków programowania , więc jest bardzo prawdopodobne, że libsodium będzie działać tylko podczas próby współdziałania z innym stosem programowania. Ponadto libsodium zwykle działa bardzo szybko bez poświęcania bezpieczeństwa.

Jak korzystać z Libsodium w JavaScript?

Najpierw musisz zdecydować o jednej rzeczy:

  1. Czy chcesz po prostu zaszyfrować / odszyfrować dane (a może nadal w jakiś sposób bezpiecznie używać zwykłego tekstu w zapytaniach do bazy danych) i nie martwić się o szczegóły? Lub...
  2. Czy potrzebujesz zaimplementować określony protokół?

Jeśli wybrałeś pierwszą opcję , pobierz CipherSweet.js .

Dokumentacja jest dostępna online . EncryptedFieldjest wystarczający w większości przypadków użycia, ale interfejsy API EncryptedRowi EncryptedMultiRowsmogą być łatwiejsze, jeśli masz wiele różnych pól, które chcesz zaszyfrować.

Dzięki CipherSweet nie musisz nawet wiedzieć, czym jest nonce / IV, aby bezpiecznie go używać.

Ponadto obsługuje int/ floatszyfrowanie bez wycieku faktów o zawartości poprzez rozmiar tekstu zaszyfrowanego.

W przeciwnym razie będziesz potrzebować sodu-plus , który jest przyjazną dla użytkownika nakładką na różne opakowania libsodium. Sodium-Plus umożliwia pisanie wydajnego, asynchronicznego kodu wieloplatformowego, który jest łatwy do audytu i uzasadnienia.

Aby zainstalować sodu-plus, po prostu uruchom ...

npm install sodium-plus

Obecnie nie ma publicznego CDN do obsługi przeglądarki. Wkrótce to się zmieni. Jednakże, można pobrać sodium-plus.min.jsz najnowszej wersji GitHub jeśli jest to potrzebne.

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

Dokumentacja sodu-plus jest dostępna na Github.

Jeśli potrzebujesz samouczka krok po kroku, w tym artykule dev.to znajdziesz to, czego szukasz.

Scott Arciszewski
źródło
16

Nowoczesne przeglądarki obsługują teraz crypto.subtleAPI, które zapewnia natywne funkcje szyfrowania i deszyfrowania (nie mniej asynchroniczne!) Przy użyciu jednej z następujących metod: AES-CBC, AES-CTR, AES-GCM lub RSA-OAEP.

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

richardtallent
źródło
3
Spośród powyższych opcji rozsądne są tylko AES-GCM i RSA-OAEP. :(
Scott Arciszewski
4

Przed wdrożeniem któregokolwiek z nich, zapoznaj się z odpowiedzią Scotta Arciszewskiego .

Chcę, abyś był bardzo ostrożny z tym, co mam zamiar udostępnić, ponieważ mam niewielką lub żadną wiedzę na temat bezpieczeństwa (istnieje duże prawdopodobieństwo, że nadużywam poniższego interfejsu API), więc byłbym bardziej niż mile widziany, aby zaktualizować tę odpowiedź z pomocą społeczności .

Jak @richardtallent wspomniał w swojej odpowiedzi , istnieje wsparcie dla Web Crypto API, więc w tym przykładzie zastosowano standard. W chwili pisania tego tekstu na świecie obsługiwanych jest 95,88% przeglądarek .

Zamierzam udostępnić przykład za pomocą Web Crypto API

Zanim przejdziemy dalej, zwróć uwagę ( cytat z MDN ):

Ten interfejs API zapewnia szereg prymitywów kryptograficznych niskiego poziomu. Jest to bardzo łatwo je niewłaściwie , a pułapki związane mogą być bardzo subtelne .

Nawet zakładając prawidłowe wykorzystanie podstawowych funkcji kryptograficznych, bezpieczne zarządzanie kluczami i ogólny projekt systemu zabezpieczeń są niezwykle trudne do osiągnięcia i są na ogół domeną specjalistów ds. Bezpieczeństwa.

Błędy w projektowaniu i wdrażaniu systemu bezpieczeństwa mogą sprawić, że bezpieczeństwo systemu będzie całkowicie nieskuteczne.

Jeśli nie jesteś pewien, czy wiesz, co robisz, prawdopodobnie nie powinieneś używać tego interfejsu API .

Bardzo szanuję bezpieczeństwo, a nawet pogrubiłem dodatkowe części z MDN ... Zostałeś ostrzeżony

teraz, do rzeczywistego przykładu ...


JSFiddle:

Znaleziono tutaj: https://jsfiddle.net/superjose/rm4e0gqa/5/

Uwaga:

Zwróć uwagę na użycie awaitsłów kluczowych. Użyj go wewnątrz asyncfunkcji lub użyj .then()i .catch().

Wygeneruj klucz:

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

Zaszyfruj:

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

Odszyfruj

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

Konwersja ArrayBuffer tam iz powrotem z String (Gotowe w 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;
  }

Więcej przykładów znajdziesz tutaj (nie jestem właścicielem): // https://github.com/diafygi/webcrypto-examples

Jose A.
źródło
2

CryptoJS nie jest już obsługiwany. Jeśli chcesz nadal z niego korzystać, możesz przełączyć się na ten adres URL:

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

Dmytro Lopushanskyy
źródło
Jaka jest różnica między pakietami zbiorczymi a komponentami w folderze 3.1.2?
Kanagavelu Sugumar
1
Crypto poleca bibliotekę Forge po wejściu na ich stronę.
Dror Bar
1

Użyj SimpleCrypto

Używanie encrypt () i decrypt ()

Aby korzystać z SimpleCrypto, najpierw utwórz instancję SimpleCrypto z tajnym kluczem (hasłem). Podczas tworzenia instancji SimpleCrypto MUSI być zdefiniowany parametr klucza tajnego.

Aby zaszyfrować i odszyfrować dane, po prostu użyj funkcji encrypt () i decrypt () z instancji. Spowoduje to użycie algorytmu szyfrowania 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
źródło
3
SimpleCrypto używa nieuwierzytelnionego AES-CBC i dlatego jest podatny na ataki z wykorzystaniem wybranego szyfrogramu.
Scott Arciszewski
-6

Proste funkcje,


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
źródło
4
Chociaż ten fragment kodu może być rozwiązaniem, dołączenie wyjaśnienia naprawdę pomaga poprawić jakość Twojego posta. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a osoby te mogą nie znać powodów, dla których zaproponowałeś kod.
Johan
1
Nie jest to bezpieczny algorytm (zauważ, że Encrypt nie przyjmuje parametru klucza) i można go łatwo odwrócić. OP poprosił o coś, co było zabezpieczone.
Mike S