Криптографія для розробників: Chapter 1

Jstify Community
7 min readMay 22, 2023

--

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

Cписок наступних частин публікацій:

  1. Криптографія для розробників: Chapter 2
  2. Криптографія для розробників: Chapter 3
  3. Криптографія для розробників: Chapter: 4

Що таке криптографія ?

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

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

Навіщо розробникам турбуватись про неї?

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

Ви можете спитати навіщо ми зачіпаємо дану тему, але відповідь дуже проста, хакерські атаки стають все більш поширеними й виконуються все більш досвідченими злочинцями. Раніше на безпеку у циклі розробки майже не звертали увагу, але на даний час це є один з головних процесів. Багато організацій також впроваджують такі практики, як DevSecOps і «зсув лівої безпеки», в яких потреби безпеки включені в увесь життєвий цикл розробки програмного забезпечення.

Два основні правила

  1. Я не винаходитиму мій власний криптографічний алгоритм
  2. Я не буду писати власну версію вже наявного криптографічного алгоритму

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

Бажано використовувати інструменти, бібліотеки, що вже є наявними у мові програмування, яку ви використовуєте, що позбавить вас головного болю.

Поняття “Безпеки” в криптографії

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

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

Атака грубою силою (brute-force) — це просто перебір всіх можливих комбінацій, поки не підбереться вірна.

Ваш алгоритм вважається зламаним, коли його можна зламати швидше ніж за допомогою brute-force

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

Після того, як ми розібрались з вами, що таке поняття безпеки в криптографії, можна поговорити про типи шифрування.

Типи шифрування

  1. Шифрування в стані спокою — це практика шифрування даних, коли вони находяться на постійному носії. Тобто дані, які у вас на жорсткому диску, або десь на сервері. Проте при такому типі шифрування дані розшифровуються в пам’яті, під час їх обробки, тому якщо зловмисник зможе проникнути у вашу активну систему він може їх вкрасти.
  2. Шифрування під час передавання — це практика шифрування даних під час їх передачі через ненадійний канал. Найпоширенішим прикладом є захист транспортного рівня (TLS), який використовується протоколом HTTPS для захисту доступу до веб-сайтів; це захищає інформацію, якою обмінюються клієнт і веб-сервер через Інтернет (наприклад, паролі чи інші конфіденційні дані).
  3. Наскрізне шифрування (також називається шифруванням E2E або E2EE) — це практика шифрування даних клієнта перед надсиланням на віддалений сервер, щоб лише клієнт мав ключі для їх розшифровки. Це зазвичай використовується з хмарним сховищем; ваші документи шифруються на вашому ноутбуці перед надсиланням до хмарного постачальника, а ключі ніколи не залишають ваш ноутбук. Хмарний постачальник бачить лише зашифровані блоки даних і не може прочитати, що ви зберігаєте в їхній службі, або виконати будь-яку обробку цих даних (хоча вони все одно можуть збирати статистичні дані на основі метаданих, таких як розмір зашифрованих блоків; наприклад, зашифроване відео набагато більше, ніж зашифроване фото!).

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

А тепер давайте, поговоримо про “ґрунт, базу, наріжний камінь”, а сама Binary data і Random Data

Binary and Random Data

Давайте спочатку дамо визначення, що таке Binary Data.

Binary Data — це послідовність байтів, включаючи символи, які не можуть бути представлені як текст.

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

У Node.js буфери — це особливий тип об’єктів, які можуть зберігати необроблені двійкові дані. Buffer являє собою фрагмент пам’яті (зазвичай оперативної пам’яті), виділеної у вашому комп’ютері. Після встановлення розмір буфера не можна змінити.

Buffer зберігає байти. Байт — це послідовність із восьми бітів. Біти є основною одиницею пам’яті на вашому комп’ютері, вони можуть містити значення 0 або 1.

Розгляньмо приклад буфера:

<Buffer 61 2e 71 3b 65 2e 31 2f 61 2e>

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

Ви можете запитати себе: «Якщо це біти та байти, де 0 і 1?»

Це тому, що Node.js відображає байти за допомогою шістнадцяткової системи. Таким чином, кожен байт може бути представлений лише двома цифрами — парою цифр і літер від 0 до 9 і від «a» до «f».

Є кілька способів створити об’єкт Buffer у Node.js.

  • Buffer.from() — найпростіший метод, приймає два параметри. Першим параметром є власне буде записано у буфер, другий параметр це кодування.
  • Buffer.alloc() — даний метод корисний, коли ви хочете створити порожні буфери, не обов’язково заповнюючи їх даними. За замовченням він приймає число та повертає буфер заданого розміру.
  • Buffer.allocUnsafe() — даний метод не є зовсім безпечним, бо пропускає фазу очищення та заповнення буфера нулями, на відміну від методу alloc(). При використанні даного методу може статись таке, що буфер може розміститись в області пам’яті, яка містить старі дані.
const buffFrom = Buffer.from("Hello First Buffer");
const buffAlloc = Buffer.alloc(4);
const buffAllocUnsafe = Buffer.allocUnsafe(15)

console.log(buffFrom, 'Buffer.from()'); // <Buffer 48 65 6c 6c 6f 20 46 69 72 73 74 20 42 75 66 66 65 72>
console.log(buffAlloc, 'Buffer.alloc()') // <Buffer 00 00 00 00>
console.log(buffAllocUnsafe, 'Buffer.allocUnsafe()') // <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>

Тепер пройдімось по деяких методах Buffer

  1. buffer.length: повертає довжину буфера в байтах.
const buf = Buffer.from('Hello', 'utf8');
console.log(buf.length); // Output: 5

2. buffer.toString(): цей метод перетворює буфер на рядок.

const buf = Buffer.from('Hello', 'utf8');
const str = buf.toString(); // Converts buffer to string using UTF-8 encoding
console.log(str); // Output: Hello

3. buffer.copy(): цей метод копіює дані з буфера в інший буфер.

const buf1 = Buffer.from('Hello', 'utf8');
const buf2 = Buffer.alloc(5);
buf1.copy(buf2); // Copies data from buf1 to buf2
console.log(buf2.toString()); // Output: Hello

4. buffer.slice(): цей метод повертає новий буфер, який посилається на ту саму пам’ять, що й вихідний буфер, але з указаним діапазоном.

const buf = Buffer.from('Hello', 'utf8');
const slicedBuf = buf.slice(0, 2); // Creates a new buffer containing 'He'
console.log(slicedBuf.toString()); // Output: He

Тепер поговорімо про важливість Random Data.

Важливість використання Random Data

В реальних програмах використовують ключ з випадково згенерованими числами, адже якщо у вашій послідовності наступне число випадкове, то зловмисник не зможе вгадати послідовність. Ось тут є проблема з генерацією, таких чисел, бо комп’ютери є детермінованими машинами, тому, за визначенням, генерація випадкових чисел є для них проблемою. На практиці ми будемо покладатись на захищені генератори випадкових чисел (CSPRNG), які використовують різні джерела ентропії (або «шуму») для створення непередбачуваних чисел. Ці системи, як правило, вбудовані в ядро операційних систем.

У Node.js crypto.randomBytes повертає випадкові послідовності байтів за допомогою CSPRNG операційної системи, і це вважається безпечним для криптографічного використання.

Ви можете спитатись — А чому не використовувати Math.random()?

Проблема в тому, що Math.random(), не є безпечним. Під капотом, він використовує генератор псевдовипадкових чисел (PRNG), що в минулому привело до проблем.

Ось деякі з них:

Тому враховуючи вищесказане, ми будемо використовувати модуль з самого Node.js — crypto. Даний модуль надає нам метод crypto.randomBytes(size, callback). Даний метод рекомендується для використання, якщо вам потрібно сформувати випадкову послідовність байтів.

const crypto = require('crypto');

crypto.randomBytes(32, (err, buf) => {
if (err) { throw err }
console.log(buf.toString('hex'))
})
Результат: b1d884f553950524e76090f8e93e0ac6124ac3d6c8f5e374c565b3e59fd32c52

Висновки

Ми з вами пройшли невеличкий вступ у криптографію для розробника, поговорили про те, що таке “Безпека”, дізнались типи шифрування і чим вони відрізняються. Також поговорили про Buffer та Ranom data, та чому це важливо. У наступній частині ми вже перейдемо до практики.

--

--