Практика: шифрование данных паролем пользователя

Общая схема гибридного подхода

Шифрование данных с использованием пароля пользователя в Web Crypto API строится на связке двух криптографических примитивов:

  • вывод ключа из пароля (KDF) — обычно PBKDF2
  • симметричное шифрование данных — чаще всего AES-GCM

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

Типовая схема:

  1. Пользователь вводит пароль
  2. Генерируется случайная соль
  3. Из пароля и соли выводится ключ PBKDF2
  4. С помощью ключа выполняется AES-GCM шифрование
  5. Сохраняются: зашифрованные данные, соль, IV

Криптографическая база Web Crypto API

Web Crypto API доступна через:

crypto.subtle

Основные используемые методы:

  • deriveKey — получение ключа из пароля
  • encrypt — шифрование
  • decrypt — расшифрование
  • generateKey — генерация ключей AES
  • importKey — импорт сырого пароля как материала

Кодирование строк в бинарный формат

Web Crypto работает с ArrayBuffer, поэтому текст необходимо преобразовывать:

const enc = new TextEncoder();
const dec = new TextDecoder();

const data = enc.encode("секретное сообщение");

Генерация соли и IV

Соль и вектор инициализации должны быть случайными:

const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
  • salt — защищает от атак с радужными таблицами
  • iv (nonce) — уникален для каждого шифрования в AES-GCM

Вывод ключа из пароля (PBKDF2)

PBKDF2 применяется через deriveKey.

async function deriveKey(password, salt) {
  const baseKey = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );

  return crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 100000,
      hash: "SHA-256"
    },
    baseKey,
    {
      name: "AES-GCM",
      length: 256
    },
    false,
    ["encrypt", "decrypt"]
  );
}

Ключевые параметры:

  • iterations — число итераций (минимум десятки тысяч, часто 100000+)
  • hash — SHA-256 как стандартный выбор
  • AES-GCM 256 — современный симметричный алгоритм

Шифрование данных

async function encryptData(password, plaintext) {
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const iv = crypto.getRandomValues(new Uint8Array(12));

  const key = await deriveKey(password, salt);

  const encoded = new TextEncoder().encode(plaintext);

  const ciphertext = await crypto.subtle.encrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    encoded
  );

  return {
    ciphertext: new Uint8Array(ciphertext),
    salt,
    iv
  };
}

Формат хранения зашифрованных данных

Для хранения обычно используется сериализация:

function toBase64(buffer) {
  return btoa(String.fromCharCode(...buffer));
}

function fromBase64(str) {
  return Uint8Array.from(atob(str), c => c.charCodeAt(0));
}

Структура объекта:

{
  "ciphertext": "...base64...",
  "salt": "...base64...",
  "iv": "...base64..."
}

Расшифрование данных

Процесс обратный:

async function decryptData(password, payload) {
  const salt = payload.salt;
  const iv = payload.iv;

  const key = await deriveKey(password, salt);

  const decrypted = await crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv: iv
    },
    key,
    payload.ciphertext
  );

  return new TextDecoder().decode(decrypted);
}

Полный цикл работы

const password = "strong-password";
const message = "конфиденциальные данные";

const encrypted = await encryptData(password, message);

const decrypted = await decryptData(password, encrypted);

Особенности AES-GCM

AES-GCM выполняет одновременно:

  • шифрование данных
  • проверку целостности (authentication tag)

Это означает, что при изменении зашифрованного текста расшифрование завершится ошибкой.


Ошибки при работе с Web Crypto

1. Повторное использование IV

Использование одинакового IV с одним ключом критически нарушает безопасность AES-GCM.

2. Слабая соль или её отсутствие

Без соли пароль становится уязвим к предвычисленным атакам.

3. Малое число итераций PBKDF2

Низкие значения (например, <10 000) позволяют ускорить перебор пароля.

4. Хранение пароля в памяти

Пароль должен использоваться только для вывода ключа и не сохраняться.


Производительность PBKDF2

PBKDF2 намеренно медленный. При 100 000 итераций:

  • время вывода ключа может составлять десятки или сотни миллисекунд
  • это является защитным механизмом против brute-force атак

Формирование безопасного хранилища

Практическая структура данных:

  • salt — 16 байт
  • iv — 12 байт
  • ciphertext — переменной длины

Рекомендуемое объединение:

{
  v: 1,
  alg: "AES-GCM",
  kdf: "PBKDF2",
  salt: "...",
  iv: "...",
  data: "..."
}

Поток обработки данных

  1. Пароль → PBKDF2 → ключ AES-GCM
  2. Ключ + IV → шифрование данных
  3. Сохранение результата
  4. При расшифровании повторное получение ключа через salt
  5. Расшифрование с тем же IV

Безопасностные характеристики модели

  • пароль никогда не используется напрямую как ключ
  • ключ не хранится в явном виде
  • каждый объект шифруется уникальным IV
  • соль защищает от атак словарями
  • AES-GCM обеспечивает целостность

Типовые сценарии использования

  • локальное шифрование пользовательских заметок
  • защищённое хранение данных в IndexedDB
  • клиентское шифрование перед отправкой на сервер
  • офлайн-шифрование файлов в браузере

Ограничения подхода

  • невозможность восстановления данных без пароля
  • зависимость от качества пароля пользователя
  • отсутствие серверного восстановления ключа без компромисса безопасности
  • необходимость аккуратной работы с бинарными данными

Работа с IndexedDB для хранения результата

const record = {
  ciphertext: encrypted.ciphertext,
  salt: encrypted.salt,
  iv: encrypted.iv
};

Далее сохраняется как бинарный Blob или base64-строки.


Поведение при неверном пароле

AES-GCM не различает:

  • неверный пароль
  • повреждённые данные

Обе ситуации приводят к исключению при decrypt.


Итоговая архитектура решения

  • UI слой: ввод пароля и текста
  • Crypto слой: PBKDF2 + AES-GCM
  • Storage слой: IndexedDB / localStorage (base64)
  • Transport слой: при необходимости передача зашифрованного payload

Практическая модель безопасности

Комбинация PBKDF2 + AES-GCM в Web Crypto API формирует стандартный подход клиентского шифрования, где безопасность определяется:

  • энтропией пароля
  • параметрами KDF
  • уникальностью IV
  • корректным хранением соли и метаданных