PBKDF2: Password-Based Key Derivation Function

PBKDF2 (Password-Based Key Derivation Function 2) используется для преобразования пароля в криптографически стойкий ключ. В Web Crypto API этот механизм реализован через метод crypto.subtle.deriveKey и crypto.subtle.deriveBits, позволяя получать ключи из низкоэнтропийных источников (паролей) с помощью псевдослучайной функции на основе HMAC.


Базовый принцип работы PBKDF2

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

Основные компоненты:

  • пароль (password)
  • соль (salt)
  • количество итераций (iterations)
  • псевдослучайная функция (PRF, обычно HMAC-SHA-256)
  • длина выводимого ключа

Ключевая идея заключается в замедлении вычисления производных ключей, что делает атаки перебором существенно дороже.


Импорт пароля в Web Crypto API

Перед использованием PBKDF2 пароль необходимо преобразовать в формат CryptoKey:

const password = "user-password";

const baseKey = await crypto.subtle.importKey(
  "raw",
  new TextEncoder().encode(password),
  { name: "PBKDF2" },
  false,
  ["deriveKey", "deriveBits"]
);

Здесь пароль не используется напрямую — он становится материалом для дальнейшего вывода ключа.


Параметры PBKDF2

Сильная сторона PBKDF2 в гибкости параметров:

  • salt — уникальное случайное значение для каждого пользователя
  • iterations — число повторений (обычно десятки или сотни тысяч)
  • hash — криптографическая хеш-функция (SHA-256, SHA-384, SHA-512)
  • length — длина итогового ключа

Пример подготовки соли:

const salt = crypto.getRandomValues(new Uint8Array(16));

Генерация ключа через deriveKey

Основной способ использования PBKDF2 — получение ключа для симметричного шифрования (например, AES-GCM).

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

Полученный объект CryptoKey можно использовать для шифрования данных:

  • AES-GCM
  • AES-CBC (устаревающий вариант)
  • AES-KW (обёртка ключей)

Получение “сырых” байтов через deriveBits

Если требуется не ключ, а просто производное значение:

const derivedBits = await crypto.subtle.deriveBits(
  {
    name: "PBKDF2",
    salt: salt,
    iterations: 150000,
    hash: "SHA-256"
  },
  baseKey,
  256
);

Результат — ArrayBuffer, который можно использовать для:

  • генерации ключей вручную
  • создания токенов
  • производных идентификаторов

Роль соли в безопасности PBKDF2

Соль предотвращает использование радужных таблиц и делает одинаковые пароли уникальными на выходе.

Ключевые свойства:

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

Пример хранения:

salt + iterations + derivedKey

Без соли PBKDF2 теряет значительную часть своей устойчивости к атаке.


Выбор количества итераций

Итерации напрямую влияют на стоимость вычисления:

  • 10 000 — устаревшие значения
  • 100 000 — минимально допустимый уровень
  • 200 000 – 600 000 — типичный современный диапазон
  • выше — повышенная безопасность при допустимой нагрузке

Баланс заключается в том, чтобы замедлить атакующего, но не пользователя.


Используемые хеш-функции

Web Crypto API поддерживает несколько вариантов:

  • SHA-1 (не рекомендуется)
  • SHA-256 (де-факто стандарт)
  • SHA-384
  • SHA-512 (повышенная стойкость, более высокая нагрузка)

Наиболее распространённый выбор:

hash: "SHA-256"

Типичная схема хранения паролей

PBKDF2 часто используется для серверной и клиентской совместимости:

  1. пользователь вводит пароль
  2. генерируется salt
  3. вычисляется derived key
  4. сохраняются salt + iterations + hash результата

Важно: сам пароль никогда не сохраняется.


Ограничения PBKDF2 в Web Crypto API

Несмотря на универсальность, существуют ограничения:

  • отсутствует возможность настройки HMAC напрямую
  • нет поддержки современных memory-hard алгоритмов (например, Argon2)
  • фиксированная модель PRF (через hash)
  • производительность хуже специализированных KDF в некоторых сценариях

Сравнение с альтернативами

PBKDF2 остаётся стандартом, но имеет конкурентов:

  • scrypt — устойчив к GPU-атакам
  • Argon2 — современный стандарт password hashing
  • HKDF — используется не для паролей, а для ключевого расширения

PBKDF2 чаще применяется там, где требуется совместимость и поддержка браузеров.


Безопасная интеграция в поток шифрования

Типичная схема использования в Web Crypto API:

  1. пароль → importKey
  2. PBKDF2 → deriveKey
  3. AES-GCM → encrypt/decrypt

Пример использования результата:

const iv = crypto.getRandomValues(new Uint8Array(12));

const encrypted = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv: iv
  },
  key,
  new TextEncoder().encode("secret data")
);

Криптографическая устойчивость и практическое применение

PBKDF2 не предназначен для хранения паролей в чистом виде, но используется для:

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

Качество безопасности зависит от:

  • случайности соли
  • достаточного числа итераций
  • выбора SHA-256 или выше

Частые ошибки при использовании

  • повторное использование соли
  • слишком малое число итераций
  • хранение пароля вместо производного ключа
  • использование SHA-1 в новых системах
  • отсутствие ограничения времени вычислений

Каждая из этих ошибок существенно снижает криптостойкость системы.


Итоговая модель работы PBKDF2 в Web Crypto API

  • пароль преобразуется в CryptoKey
  • PBKDF2 многократно применяет HMAC
  • результат зависит от соли и итераций
  • итог используется как ключ или байтовая последовательность
  • безопасность определяется параметрами конфигурации