В 2026 году может показаться странным писать новую библиотеку для MD5. Алгоритм считается устаревшим, браузеры постепенно убирают его из Web Crypto API, а в документации Node.js есть предупреждения о его использовании.

Но реальность сложнее. MD5 всё ещё жив в:

  • Контрольных суммах файлов (многие репозитории до сих пор публикуют MD5-хеши)

  • Системах кэширования (ключи на основе контента)

  • Легаси-протоколах (некоторые API требуют MD5-подписи)

  • Внутренних идентификаторах (где безопасность не важна)

Проблема в том, что существующие реализации либо устарели, либо не имеют TypeScript, либо не работают универсально (Node.js + браузер). Я решил это исправить и представил pure-md5 — современную, типизированную и адаптивную библиотеку.

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

⚠️ Важное предупреждение: MD5 криптографически небезопасен. Не используйте для паролей, токенов или цифровых подписей. Только для контрольных сумм, кэширования и совместимости.

Почему не встроенный crypto?

Первый вопрос, который возникает: «Зачем новая библиотека, если в Node.js есть crypto, а в браузерах — SubtleCrypto

Проблема

Node.js crypto

Browser SubtleCrypto

pure-md5

Работает в браузере

Работает в Node.js

TypeScript из коробки

❌ (нужны @types)

⚠️

MD5 поддержка

⚠️ (убирают)

Размер

Native

Native

~1.4KB gzipped 1

Зависимости

0

0

0

1 ~1.4KB gzipped c tree-shaking (только md5). Весь бандл: ~6KB gzipped

Ключевое преимущество pure-md5 — универсальность. Вы пишете код один раз, и он работает везде. Библиотека автоматически выбирает оптимальный адаптер:

// Один и тот же код работает в Node.js и браузере
import { md5, hashFile } from 'pure-md5';

const hash = md5('hello');
const fileHash = await hashFile('large-file.bin');

Для минимизации размера

import { md5 } from 'pure-md5/md5'; 
// Only md5() function
// Result: ~1.4KB gzipped

Архитектура: Система адаптеров

Сердце библиотеки — адаптивная система, которая пытается использовать нативные API, а при их отсутствии падает на чистую JavaScript-реализацию.

Как работает детекция

// Упрощённая логика выбора адаптера
function detectAdapter() {
  if (isNode && require('node:crypto')) {
    return nodeAdapter;      // Приоритет 1
  }
  if (globalThis.crypto?.subtle) {
    return webCryptoAdapter; // Приоритет 2
  }
  return pureJSAdapter;      // Фолбэк
}

Это даёт несколько преимуществ:

  1. Производительность: В Node.js используется нативный C++ код

  2. Совместимость: В старых браузерах работает JS-реализация

  3. Будущее-proof: Если WebCrypto уберёт MD5, библиотека продолжит работать

Работа с большими файлами: Стримы и прогресс

Одна из самых частых задач — хеширование больших файлов без загрузки в память. В pure-md5 это решено через стриминговый API с отслеживанием прогресса.

Пример: Хеширование файла с прогресс-баром

import { hashFile, createProgressTracker } from 'pure-md5';

const fileSize = 1024 * 1024 * 100; // 100 MB
const progress = createProgressTracker(fileSize, (percent) => {
  console.log(`Прогресс: ${percent.toFixed(1)}%`);
});

const result = await hashFile('large-file.bin', { onProgress: progress });

console.log('MD5:', result.digest); // например: "5d41402abc4b2a76b9719d911017c592"
console.log('Обработано байт:', result.bytesProcessed); // 104857600

Под капотом

Библиотека использует потоковую обработку данных:

// Упрощённая реализация стрима
class MD5Stream extends Transform {
  private hash: any;
  private bytesProcessed: number = 0;
  
  _transform(chunk: Buffer, encoding: string, callback: Function) {
    this.hash.update(chunk);
    this.bytesProcessed += chunk.length;
    this.emit('progress', { bytesProcessed: this.bytesProcessed });
    callback();
  }
  
  _flush(callback: Function) {
    this.emit('md5', { digest: this.hash.digest('hex'), bytesProcessed: this.bytesProcessed });
    callback();
  }
}

Это позволяет хешировать файлы любого размера без риска переполнения памяти.

TypeScript как citizen first

В 2026 году библиотека без типов — это библиотека второго сорта. pure-md5 написана на TypeScript 5.6+ с полными определениями типов.

// Полная типизация всех API
import { md5, hashFile, MD5Result } from 'pure-md5';

const hash: string = md5('hello');

const result: MD5Result = await hashFile('file.txt');
// result.digest: string
// result.bytesProcessed: number

Никаких @types/md5, никаких any. Всё работает из коробки.

Бенчмарки

Размер и скорость — критичные метрики для фронтенд-библиотек.

Размер бандла (gzipped)

Библиотека

Размер

Зависимости

pure-md5

~1.4KB

0

md5 (pvorb/node-md5)

~6KB

3 (charenc, crypt, is-buffer)

js-md4

~2KB

0

blueimp-md5

~2KB

0

crypto-js

~4KB+

0

spark-md5

~3KB

0

Скорость (операций в секунду, Node.js)

Библиотека

ops/sec

Node.js crypto

1,200,000

pure-md5 (node adapter)

1,150,000

pure-md5 (pure JS)

450,000

md5 (pvorb)

~350,000

crypto-js

380,000

spark-md5

420,000

В Node.js pure-md5 почти не уступает нативному crypto благодаря адаптеру. В браузере чистая JS-реализация всё ещё быстрее конкурентов за счёт оптимизаций.

Сравнение с пакетом md5

Пакет md5 (от pvorb/node-md5) — одна из самых популярных реализаций MD5 (2.5M недельных скачиваний). Он работает только в Node.js и использует побайтовую обработку строк, что снижает производительность.

Основные различия:

Характеристика

pure-md5

md5 (pvorb)

Совместимость

Node.js + Browser

Только Node.js

Типизация

✅ TypeScript из коробки

❌ Нет типов

Размер

~1.4KB

~6KB

Зависимости

0

3 (charenc, crypt, is-buffer)

Tree-shaking

✅ Поддерживает

❌ Нет (CommonJS)

Stream API

✅ Встроенный

❌ Нет

Адаптеры

Автоматический выбор

Только one-size-fits-all

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

~1.15M ops/sec

~350K ops/sec

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

// md5 (pvorb) - только в Node.js
import md5 from 'md5';

// Ошибка! Не работает в браузере
const hash = md5('hello');
// pure-md5 - универсальное решение
import { md5 } from 'pure-md5';

// Работает везде
const hash = md5('hello'); // ✅ Node.js
const hash2 = md5('hello'); // ✅ Browser

Почему md5 медленнее?

Пакет md5 использует побайтовую обработку и вспомогательные библиотеки для работы с кодировками:

// Пример из md5 (pvorb)
const str = new String(string);
const bytes = [];
for (let i = 0; i < str.length; i++) {
  bytes.push(str.charCodeAt(i));
}
// ... затем обработка массива байт

В pure-md5 используется более эффективный подход — работа с 32-битными словами и оптимизированные циклы.

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

1. Проверка целостности скачанного файла

import { verifyFile } from 'pure-md5';

const isVerified = await verifyFile(
  'downloaded-file.zip',
  '5d41402abc4b2a76b9719d911017c592' // Ожидаемый хеш от сервера
);

if (!isVerified) {
  throw new Error('Файл повреждён при загрузке');
}
// true или false

2. Ключ кэша на основе контента

import { md5 } from 'pure-md5';

function getCacheKey(content: string): string {
  return `cache:${md5(content)}`;
}

const key = getCacheKey(JSON.stringify(userData));
// cache:5d41402abc4b2a76b9719d911017c592

3. Универсальное хеширование (Node + Browser)

// Один файл работает везде
import { md5 } from 'pure-md5';

export function generateId(data: string | Uint8Array): string {
  if (data instanceof Uint8Array) {
    // Для бинарных данных нужно преобразование
    return md5(new TextDecoder().decode(data));
  }
  return md5(data);
}
// md5() принимает string или Buffer (Node.js)
// Для ArrayBuffer/Uint8Array в браузере используйте hashBinary() через adapter

Безопасность: Где можно и нельзя использовать

Сценарий

Можно?

Альтернатива

Контрольные суммы файлов

Ключи кэша

Внутренние ID объектов

Пароли пользователей

bcrypt, argon2

JWT токены

HS256, RS256

Цифровые подписи

SHA-256, EdDSA

Правило: Если злоумышленник может получить выгоду от подделки хеша — MD5 не подходит.

Установка и быстрый старт

npm install pure-md5
# или
yarn add pure-md5
# или
pnpm add pure-md5
import { md5, hashFile, createMD5Stream } from 'pure-md5';

// Строка
const hash = md5('hello');
// "5d41402abc4b2a76b9719d911017c592"

// Файл
const fileHash = await hashFile('path/to/file.txt');

// Стрим
const stream = createMD5Stream();
fs.createReadStream('file.bin').pipe(stream);

Заключение

pure-md5 не пытается возродить MD5 для безопасности. Это инструмент для конкретных задач, где MD5 всё ещё используется, но реализация должна быть современной, типизированной и универсальной.

Если вам нужна контрольная сумма файла, ключ кэша или совместимость с легаси-API — pure-md5 даст вам лучший Developer Experience без лишних зависимостей.

Ссылки: