Метод subtle.digest

digest — один из базовых методов Web Crypto API, предназначенный для вычисления криптографического хеша от произвольных данных. Он относится к объекту crypto.subtle и реализует односторонние хеш-функции, такие как SHA-1, SHA-256, SHA-384 и SHA-512.

Хеширование в данном контексте — это преобразование входных данных произвольной длины в фиксированную строку байтов, называемую «дайджестом». Результат нельзя обратить обратно в исходные данные, что делает хеш-функции фундаментом для проверки целостности данных, хранения паролей (в связке с другими механизмами), цифровых подписей и множества криптографических протоколов.


Сигнатура метода

crypto.subtle.digest(algorithm, data)

Параметры

algorithm — строка или объект, определяющий используемую хеш-функцию:

  • "SHA-1" (устаревший, небезопасный)
  • "SHA-256" (наиболее распространённый)
  • "SHA-384"
  • "SHA-512"

data — входные данные для хеширования. Должны быть представлены в виде:

  • ArrayBuffer
  • TypedArray (например, Uint8Array)
  • DataView

Возвращаемое значение

Метод возвращает Promise, который резолвится в ArrayBuffer, содержащий бинарный хеш.


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

Алгоритм digest работает исключительно с байтовыми данными. Любые строки должны быть предварительно закодированы, чаще всего в UTF-8 через TextEncoder.

Результат — это «сырые» байты. Для удобства их обычно преобразуют в шестнадцатеричную строку или Base64.


Простейший пример SHA-256

const encoder = new TextEncoder();
const data = encoder.encode("hello world");

const hashBuffer = await crypto.subtle.digest("SHA-256", data);

На этом этапе hashBuffer содержит бинарное представление хеша.


Преобразование результата в читаемый формат

HEX-строка

function bufferToHex(buffer) {
  const bytes = new Uint8Array(buffer);
  return Array.from(bytes)
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

Использование:

const encoder = new TextEncoder();
const data = encoder.encode("hello world");

const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashHex = bufferToHex(hashBuffer);

console.log(hashHex);

Base64

function bufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = "";
  for (let b of bytes) {
    binary += String.fromCharCode(b);
  }
  return btoa(binary);
}

Поддерживаемые алгоритмы

SHA-1

Ранее широко использовался, но считается криптографически слабым из-за коллизий. Его использование в современных системах не рекомендуется.

SHA-256

Стандарт де-факто. Используется в TLS, блокчейнах, цифровых подписях.

SHA-384 и SHA-512

Часть семейства SHA-2 с увеличенной длиной хеша и более высокой криптостойкостью.


Важные особенности реализации

1. Асинхронность

digest всегда возвращает Promise, даже несмотря на то, что операция может выполняться быстро:

const hash = await crypto.subtle.digest("SHA-256", data);

2. Только бинарные данные

Нельзя передать строку напрямую:

// Ошибка
crypto.subtle.digest("SHA-256", "text");

Корректный вариант:

const data = new TextEncoder().encode("text");

3. Не требует ключей

В отличие от encrypt, sign или deriveKey, метод digest не использует криптографические ключи. Это чистая односторонняя функция.


Практические сценарии использования

Проверка целостности данных

Хеш можно использовать для сравнения файлов или сообщений:

const originalHash = await crypto.subtle.digest("SHA-256", data1);
const newHash = await crypto.subtle.digest("SHA-256", data2);

Совпадение хешей означает идентичность данных.


Контроль изменений

Хеши позволяют быстро определить, изменился ли контент, без побайтового сравнения.


Идентификация данных

В системах кэширования или распределённых хранилищах хеш используется как идентификатор блока данных.


Работа с файлами

Частый сценарий — хеширование файла, загруженного пользователем:

async function hashFile(file) {
  const buffer = await file.arrayBuffer();
  return await crypto.subtle.digest("SHA-256", buffer);
}

Ограничения и нюансы

Невозможность частичного обновления

digest не поддерживает потоковое обновление. Весь ввод должен быть доступен сразу. Для больших данных это может быть ограничением.


Отсутствие соли

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

const salted = new TextEncoder().encode(password + salt);
const hash = await crypto.subtle.digest("SHA-256", salted);

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

Хотя Web Crypto API реализован на уровне браузера и обычно использует нативные оптимизации, хеширование больших массивов данных может быть ресурсоёмким.


Сравнение алгоритмов

Алгоритм Длина хеша Статус
SHA-1 160 бит небезопасен
SHA-256 256 бит рекомендован
SHA-384 384 бит безопасен
SHA-512 512 бит безопасен

Внутреннее представление данных

Результат digest — это ArrayBuffer. Он не содержит строк и не предназначен для прямого отображения. Его структура — последовательность байтов фиксированной длины, зависящей от алгоритма.

Пример:

  • SHA-256 → 32 байта
  • SHA-512 → 64 байта

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

Передача строки вместо байтов

crypto.subtle.digest("SHA-256", "hello"); // ошибка

Попытка синхронного использования

const hash = crypto.subtle.digest("SHA-256", data);
console.log(hash); // Promise, а не результат

Игнорирование кодировки

Разные кодировки дают разные хеши. Стандарт — UTF-8:

new TextEncoder().encode("текст");

Связь с другими методами Web Crypto API

digest часто используется как вспомогательный этап:

  • перед sign (подпись хеша)
  • в протоколах аутентификации
  • при генерации идентификаторов
  • в комбинации с deriveKey для создания ключей из паролей (через PBKDF2, где digest играет внутреннюю роль)

Поведение в разных окружениях

Метод доступен:

  • в современных браузерах (Chrome, Firefox, Edge, Safari)
  • в безопасных контекстах HTTPS
  • в Web Workers

Недоступен:

  • в insecure HTTP контексте
  • в старых браузерах без Web Crypto API

Минимальный шаблон использования

async function sha256(message) {
  const data = new TextEncoder().encode(message);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);

  return [...new Uint8Array(hashBuffer)]
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

Поведение при одинаковых входных данных

Хеш-функции детерминированы: одинаковый вход всегда даёт одинаковый результат, независимо от устройства, времени или окружения.


Криптографический смысл результата

Результат digest не является «шифром». Он:

  • не расшифровывается
  • не содержит исходных данных
  • не является обратимым преобразованием

Его единственная функция — компактное представление данных с фиксированной длиной и высокой чувствительностью к изменениям входа (эффект лавины).