Шифрування та розшифрування рядків JavaScript?


152

Мені цікаво створити невеликий додаток для особистого користування, яке буде шифрувати та розшифровувати інформацію на стороні клієнта за допомогою JavaScript. Зашифрована інформація буде зберігатися в базі даних на сервері, але ніколи не розшифрована версія.

Це не повинно бути надмірно захищеним, але я хотів би використовувати алгоритм, що не працює в даний час.

В ідеалі я міг би зробити щось подібне

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

генерувати кодований рядок і щось подібне

var sensical = decrypt(gibberish, key);

розшифрувати його пізніше.

Поки я бачив це: http://bitwiseshiftleft.github.io/sjcl/

Будь-які інші бібліотеки, які я повинен переглянути?


2
Погляньте на шифрування Javascript AES
kevinji


10
Деяка термінологія тут відключена. Ось проста версія 1. Солі додаються до інформації (зазвичай паролів) хешованих. Їх мета - зробити хеш іншим, ніж був би без солі. Це корисно, оскільки воно робить попередньо згенеровані хеші, якщо ваша база даних буде зламана і хешировані паролі користувачів. 2. Хешинг - це одностороння операція, яка переводить вхід у вихід. Його не можна легко відмінити або скасувати. 3. Кодування - це не шифрування. base64_encode, UrlEncode і т.д.
дез

Відповіді:


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.toString (), щоб отримати рядок. Ви зможете розшифрувати цей рядок пізніше: jsbin.com/kofiqokoku/1
Томаш Кірда

9
Але як ми можемо захистити таємну парольну фразу?
дуйхоа

9
Здається, криптовалюта - це архівний проект. Клон на github: github.com/sytelus/CryptoJS, але він не оновлювався протягом двох років. Це все-таки найкращий варіант для шифрування js?
syonip

2
Я б пішов з цим: github.com/brix/crypto-js, він також доступний через NPM
Tomas Kirda

1
@stom, від вас залежить, як і де ви його зберігаєте. Я не знаю, чи є дійсно безпечний спосіб зберігати його у браузері. Попросіть їх на сервері та збережіть у пам'яті.
Томаш Кірда

62

Як щодо CryptoJS ?

Це солідна криптобібліотека з великою кількістю функціональних можливостей. Він реалізує хеши, HMAC, PBKDF2 та шифри. У цьому випадку шифри - це те, що вам потрібно. Ознайомтесь із посібником для швидкого початку на домашній сторінці проекту.

Ви можете зробити щось на кшталт 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>

Що стосується безпеки, то на даний момент моє написання алгоритму AES вважається незнімним

Редагувати:

Здається, що онлайн-адреса знижена, і ви можете використовувати завантажені файли для шифрування знизу за вказаним посиланням та розмістити відповідні файли у вашій кореневій папці програми.

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

або використовували інший CDN, наприклад https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js


Яка різниця між накопиченнями та компонентами в папці 3.1.2?
Канагавелу Сугумар

Після трохи відтворення компонентів є відокремленими частинами. Вам потрібно буде знати, які компоненти взяти (і в якому порядку), щоб він працював. Файли збору містять все необхідне, щоб він працював лише з одним посиланням на скрипт (набагато краще, оскільки важка робота вже виконана).
шахар ельдад

2
Але як ми можемо захистити таємну парольну фразу?
shaijut

@shaijut Ви цього не зробите. Ви навіть не зберігаєте його ніде, крім оперативної пам'яті, при шифруванні / розшифровці простого тексту.
Парольну

39

Я створив незахищений, але простий текстовий шифр / розшифровку утиліти. Ніяких залежностей із жодною зовнішньою бібліотекою.

Це функції

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

І ви можете використовувати їх наступним чином:

// 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
нехай myDecipher = дешифрує ("CartelSystem") - ця сіль також розшифрує рядок. Вам не потрібно знати точне слово "mySecretSalt"
Bar

Також, чи не застосовуються солі в розшифровці?
Dror Bar

1
Ще одна публікація, де хтось сліпо використовує let. 😒︎
Іван

1
Чи не це а) супер зламане і незахищене; б) "сіль" - це насправді ваш "секретний ключ", оскільки солі не очікуються приватними? Я вважаю, що дуже небезпечно розміщувати подібний код без будь-яких коментарів, що цей забавний код не призначений для використання в реальному світі. Кількість оплачуваних коштів викликає занепокоєння. crypto.stackexchange.com/questions/11466/…
lschmierer

1
Принаймні, вони використовують криптовалюту. Те , що ви робите в основному Цезар Chipher (застосовуючи той же ключ до кожного символу) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Що стосується інших відповідей ... Я очікую , що це буде очевидно , що що - то називається «секрет» є очікується, що вони будуть збережені в таємниці (користувачем)
lschmierer

18

Існуючі відповіді, які використовують SJCL, CryptoJS та / або WebCrypto, не обов'язково помиляються, але вони не такі безпечні, як ви могли спочатку підозрювати. Як правило, ви хочете використовувати лібсодіум . Спочатку я поясню, чому, потім як.

Чому б не SJCL, CryptoJS, WebCrypto тощо?

Коротка відповідь: для того, щоб ваше шифрування було фактично захищеним, ці бібліотеки очікують, що ви зробите занадто багато варіантів, наприклад, режим блокового шифрування (CBC, CTR, GCM; якщо ви не можете сказати, для кого із трьох, що я тільки перерахував, безпечно використання і під які обмеження, ви не повинні бути обтяжені такого роду вибору взагалі ).

Якщо ваша посада не є інженером криптографії , шанси складені проти вас, щоб її надійно реалізувати.

Чому слід уникати CryptoJS?

CryptoJS пропонує кілька будівельних блоків і очікує, що ви знаєте, як їх безпечно використовувати. Це навіть за замовчуванням у режимі CBC ( заархівовано ).

Чому режим CBC поганий?

Прочитайте цю інформацію про вразливості AES-CBC .

Чому слід уникати WebCrypto?

WebCrypto - стандарт potluck, розроблений комітетом, для цілей, ортогональних для криптографії. Зокрема, WebCrypto повинен був замінити Flash, а не забезпечити безпеку .

Чому слід уникати SJCL?

Публічний API та документація SJCL просять користувачів зашифрувати дані за допомогою запам'ятовуваного людиною пароля. Це рідко, якщо взагалі, те, що ви хочете робити в реальному світі.

Додатково: за замовчуванням PBKDF2 кругле число приблизно в 86 разів менше, ніж ви хочете . AES-128-CCM, напевно, добре.

З трьох вищезазначених варіантів SJCL є найменшою ймовірністю закінчитися сльозами. Але є кращі варіанти.

Чому лібсодіум краще?

Вам не потрібно вибирати між меню режимів шифрування, хеш-функцій та іншими непотрібними параметрами. Ви ніколи не ризикуєте перекрутити свої параметри і видалити всю безпеку з протоколу .

Натомість libsodium просто надає прості параметри, налаштовані на максимальну безпеку та мінімалістичний API.

  • crypto_box()/ crypto_box_open()пропонувати аутентифіковане шифрування відкритого ключа.
    • Розглянутий алгоритм поєднує X25519 (ECDH над Curve25519) та XSalsa20-Poly1305, але вам не потрібно знати (або навіть дбати) про це, щоб безпечно його використовувати
  • crypto_secretbox()/ crypto_secretbox_open()запропонувати шифроване автентичне використання спільного ключа.
    • Розглянутий алгоритм XSalsa20-Poly1305, але вам не потрібно знати / доглядати

Крім того, libsodium має прив’язки до десятків популярних мов програмування , тому велика ймовірність, що libsodium буде просто працювати при спробі взаємодіяти з іншим стеком програмування. Крім того, лібсодіум, як правило, дуже швидко, не приносячи шкоди безпеці.

Як користуватися Libsodium в JavaScript?

Спочатку потрібно вирішити одне:

  1. Ви просто хочете зашифрувати / розшифрувати дані (а може, все ще якось надійно використовувати відкритий текст у запитах до бази даних) і не турбуватися про деталі? Або ...
  2. Чи потрібно реалізувати конкретний протокол?

Якщо ви вибрали перший варіант , знайдіть CipherSweet.js .

Документація доступна в Інтернеті . EncryptedFieldдостатньо для більшості випадків використання, але API EncryptedRowта EncryptedMultiRowsAPI можуть бути простішими, якщо у вас є багато різних полів, які ви хочете зашифрувати.

З CipherSweet вам не потрібно навіть знати, що nonce / IV це безпечне використання.

Крім того, це обробляє int/ floatшифрує без витоків фактів щодо вмісту через розмір шифротексту.

В іншому випадку ви захочете натрій плюс , який є зручним для користувачів для різних обгортків з лібсодію. Sodium-Plus дозволяє писати виконавський, асинхронний, кросплатформенний код, який легко перевіряти та міркувати.

Щоб встановити натрій плюс, просто запустіть ...

npm install sodium-plus

Наразі не існує публічного CDN для підтримки браузера. Це незабаром зміниться. Однак ви можете взяти sodium-plus.min.jsз останнього випуску Github, якщо вам це потрібно.

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

Документація щодо натрію плюс доступна на Github.

Якщо ви хочете покроковий посібник, ця стаття про розробку містить те, що ви шукаєте.


16

Сучасні браузери тепер підтримують crypto.subtleAPI, який надає нативні функції шифрування та дешифрування (асинхронізація не менше!), Використовуючи один із цих методів: AES-CBC, AES-CTR, AES-GCM або RSA-OAEP.

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


3
З вищезазначених варіантів розумними є лише AES-GCM та RSA-OAEP. :(
Скотт

4

Перш ніж здійснити будь-що з цього, перегляньте відповідь Скотта Арчішевського .

Я хочу, щоб ви були дуже обережними з тим, що я збираюся поділитися, оскільки у мене мало знань про безпеку (є велика ймовірність, що я неправильно використовую API нижче), тому я буду радий оновити цю відповідь за допомогою громади .

Як @richardtallent згадував у своїй відповіді , існує підтримка API API для веб-крипто, тому в цьому прикладі використовується стандарт. Станом на це написання, існує 95,88% глобальної підтримки браузера .

Я буду ділитися прикладом за допомогою Web Crypto API

Перш ніж ми продовжимо, зверніть увагу ( цитуючи з MDN ):

Цей API пропонує ряд криптографічних примітивів низького рівня. Це дуже легко зловжити їх , і підводні камені , пов'язані можуть бути дуже тонкими .

Навіть якщо припустити, що ви правильно користуєтесь основними криптографічними функціями, безпечне управління ключами та загальна конструкція системи безпеки надзвичайно важко виправитись і, як правило, домен спеціалізованих експертів із безпеки.

Помилки в розробці та впровадженні системи безпеки можуть зробити безпеку системи абсолютно неефективною.

Якщо ви не впевнені, що знаєте, чим займаєтесь, ви, ймовірно, не повинні використовувати цей API .

Я дуже поважаю безпеку, і навіть закреслив додаткові деталі від MDN ... Вас попереджали

тепер, до фактичного прикладу ...


JSFiddle:

Знайдено тут: https://jsfiddle.net/superjose/rm4e0gqa/5/

Примітка:

Зверніть увагу на використання awaitключових слів. Використовуйте його всередині asyncфункції або використовуйте .then()і .catch().

Створіть ключ:

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

Шифрувати:

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

Розшифруйте

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

Перетворення ArrayBuffer назад і назад з рядка (Готово в 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;
  }

Ви можете знайти більше прикладів тут (я не власник): // https://github.com/diafygi/webcrypto-examples


2

CryptoJS більше не підтримується. Якщо ви хочете продовжити його використання, ви можете перейти до цієї URL-адреси:

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


Яка різниця між накопиченнями та компонентами в папці 3.1.2?
Канагавелу Сугумар

1
Crypto рекомендує бібліотеку кузні під час заходу на їхній сайт.
Dror Bar

1

Використовуйте SimpleCrypto

Використання шифрування () та дешифрування ()

Щоб використовувати SimpleCrypto, спочатку створіть екземпляр SimpleCrypto із секретним ключем (паролем). Параметр секретного ключа ОБОВ'ЯЗКОВО визначати під час створення екземпляра SimpleCrypto.

Для шифрування та дешифрування даних просто використовуйте функцію encrypt () та decrypt () від екземпляра. Для цього буде використаний алгоритм шифрування 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 використовує неаутентифікований AES-CBC і тому є вразливим до атак вибраних шифротекстів.
Скотт

-6

Прості функції,


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
Хоча цей фрагмент коду може бути вирішенням, зокрема пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
Йоган

1
Це не безпечний алгоритм (зауважте, що Encrypt не приймає ключовий параметр), і його можна легко змінити. ОП попросила щось, що на ньому було забезпечене.
Майк S
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.