Node.js Internals
Crypto Module: Криптография в Node.js
БД с паролями утекла. 100K пользователей. Если пароли хранились в plain text - все аккаунты скомпрометированы за секунды. Если SHA-256 без соли - rainbow tables взломают их за минуты. Если bcrypt с солью - атакующему понадобятся годы на перебор. Криптография - это не паранойя, а базовая гигиена безопасности, как мытьё рук перед едой. И точно так же игнорирование её приводит к болезненным последствиям.
- **LinkedIn breach 2012:** Утекло 6.5M паролей, захешированных SHA-1 без соли. Через несколько дней 90% паролей были взломаны через rainbow tables. Урок: используйте медленные хеш-функции (bcrypt/scrypt) с солью для паролей, SHA для паролей - это грубая ошибка безопасности
- **Stripe webhook signature validation:** Stripe отправляет webhook события (платёж прошёл, подписка отменена). Без проверки подписи атакующий может отправить поддельный POST /webhook с событием 'payment_succeeded' и получить товар бесплатно. HMAC-SHA256 с секретным ключом защищает от tampering - только Stripe знает секрет, только Stripe может создать валидную подпись
- **Let's Encrypt adoption:** До 2016 года SSL сертификаты стоили 50-200/год, процесс получения занимал дни. Let's Encrypt сделал HTTPS бесплатным и автоматическим - 90% новых сайтов используют HTTPS by default. Certbot обновляет сертификаты автоматически каждые 60 дней через cron. Результат: HTTPS стал нормой, а не роскошью
Зачем криптография в Node.js
**Криптография - это не про хакеров в чёрных худи, а про банальную безопасность продакшена.** Хранение паролей пользователей? Нужно хеширование. Передача персональных данных между сервисами? Нужно шифрование. Интеграция с API, требующим подписи запросов? Нужны HMAC или RSA. **Node.js crypto module - это встроенная библиотека на основе OpenSSL**, покрывающая 90% криптографических задач без сторонних зависимостей.
Сценарий: запуск SaaS-платформы. Пользователи регистрируются, вводят пароли. Хранение в plain text при утечке БД - компрометация всех аккаунтов. **Даже MD5 не спасёт** - rainbow tables взломают любой хеш за секунды. Нужен bcrypt или scrypt с солью. Платёжные данные шифруются AES-256-GCM. HTTPS сервер требует TLS с валидными сертификатами. **Криптография - это не опция, а обязательный слой защиты**, как аутентификация или валидация входных данных.
**Node.js crypto module** основан на OpenSSL (с версии 18 - BoringSSL опционально) и предоставляет:\n**1)** Хеш-функции (SHA-256, SHA-512, BLAKE2) для проверки целостности и хеширования паролей.\n**2)** Симметричное шифрование (AES-256-GCM) для защиты данных одним ключом.\n**3)** Асимметричное шифрование (RSA, ECDSA) для обмена ключами и цифровых подписей.\n**4)** TLS/SSL для защищённых соединений (HTTPS, WebSocket Secure).
**Когда использовать каждый тип:** • **Хеширование** - для паролей, checksums, cache keys (нельзя расшифровать обратно). • **Симметричное шифрование** - для хранения данных в БД, токенов сессий (один секретный ключ для encrypt/decrypt). • **Асимметричное шифрование** - для обмена данными между сторонами без общего секрета (публичный ключ для шифрования, приватный для расшифровки). • **Подписи** - для JWT, webhook payloads, API requests (доказать, что данные не изменены).
В чём ключевое отличие хеширования от шифрования?
Хеш-функции и HMAC
**Хеш-функция - это математическая мясорубка:** на вход подаёте данные любого размера, на выходе получаете fixed-size отпечаток (digest). Основные свойства: **1)** Детерминированность (одинаковый вход → одинаковый хеш). **2)** Необратимость (по хешу нельзя восстановить исходные данные). **3)** Лавинный эффект (изменение одного бита входа → 50% битов хеша меняются). **4)** Collision resistance (найти два разных входа с одинаковым хешем - вычислительно невозможно).
**Проблема быстрых хешей для паролей:** SHA-256 вычисляет миллиарды хешей в секунду на GPU. Атакующий с утёкшей БД может перебрать все 8-символьные пароли за минуты через rainbow tables. Решение - **медленные хеш-функции:** bcrypt, scrypt, Argon2. Они специально тормозят вычисление (100-500ms на один пароль), делая brute-force экономически невыгодным. В Node.js используйте `scrypt` (встроенный) или библиотеку bcrypt/argon2.
**Алгоритмы хеширования:** **MD5** (128-bit) - УСТАРЕЛ, collision найдены за секунды. **SHA-1** (160-bit) - УСТАРЕЛ с 2017, Google нашёл коллизии. **SHA-256/SHA-512** (256/512-bit) - стандарт для checksums, но НЕ для паролей (слишком быстрый). **scrypt/bcrypt/Argon2** - для паролей, медленные по design. **BLAKE2** - современная альтернатива SHA-2, быстрее и безопаснее. **HMAC** - хеш с секретным ключом для подписи сообщений.
**Почему crypto.timingSafeEqual важен:** Обычное сравнение строк `===` прерывается на первом несовпадающем символе. Время выполнения зависит от позиции ошибки → **timing attack:** атакующий подбирает подпись посимвольно, измеряя задержку ответа. `timingSafeEqual` всегда проверяет все байты, защищая от утечки информации через timing side-channel.
Почему SHA-256 НЕ подходит для хеширования паролей?
Симметричное и асимметричное шифрование
**Шифрование решает проблему конфиденциальности:** хеш защищает от изменений, но не скрывает данные. **Симметричное шифрование** использует один секретный ключ для encrypt и decrypt (как замок с одним ключом). **Асимметричное** использует пару ключей: публичный для шифрования, приватный для расшифровки (как почтовый ящик: любой может положить письмо через щель, но открыть может только владелец с ключом).
**Когда что использовать:** Симметричное (AES) - для шифрования больших объёмов данных (файлы, БД, сессии), потому что быстрое. Асимметричное (RSA, ECDSA) - для обмена ключами и цифровых подписей, потому что не требует предварительного обмена секретом. **TLS делает оба:** RSA для handshake (обмен ключами), затем AES для передачи данных. Это hybrid cryptography - баланс между безопасностью и производительностью.
**AES (Advanced Encryption Standard):** Симметричный блочный шифр, стандарт с 2001 года. Ключи: 128/192/256 бит. Режимы: **CBC** (Cipher Block Chaining) - требует IV, уязвим к padding oracle. **GCM** (Galois/Counter Mode) - authenticated encryption, защищает от tampering, **РЕКОМЕНДУЕТСЯ**. **CTR** (Counter Mode) - превращает блочный шифр в поточный. **RSA:** Асимметричный, размер ключа 2048-4096 бит. Медленный, используется для ключей/подписей. **ECDSA:** Асимметричный на эллиптических кривых, 256-bit ключ ≈ 3072-bit RSA, быстрее.
**Почему не шифровать всё RSA:** RSA в 1000 раз медленнее AES и может шифровать только данные размером меньше ключа (для 2048-bit ключа - максимум 245 байт). **Hybrid approach:** используйте RSA для обмена AES-ключом, затем шифруйте данные AES. Это делает TLS: клиент генерирует случайный session key, шифрует его публичным ключом сервера (RSA), отправляет. Дальше вся коммуникация идёт через AES с этим session key.
В чём главное преимущество асимметричного шифрования перед симметричным?
TLS/SSL в Node.js
**TLS (Transport Layer Security) - это протокол для защищённой передачи данных по сети.** Он решает три задачи:\n**1)** Шифрование (никто не может подслушать трафик).\n**2)** Аутентификация (соединение идёт точно с нужным сервером, а не с man-in-the-middle).\n**3)** Целостность (данные не изменены в пути). **HTTPS = HTTP + TLS**, WebSocket Secure = WS + TLS. В Node.js модули `https` и `tls` предоставляют встроенную поддержку TLS на основе OpenSSL.
**TLS Handshake в двух словах:** Клиент подключается, сервер отправляет свой сертификат (с публичным ключом). Клиент проверяет сертификат через цепочку доверия до корневого CA. Если валидный - генерирует случайный session key, шифрует его публичным ключом сервера (RSA/ECDHE), отправляет. Теперь обе стороны знают session key и переключаются на симметричное шифрование (AES-GCM) для передачи данных. **Handshake занимает ~100-300ms** из-за криптографических операций и network round-trips.
**TLS версии:** **TLS 1.0/1.1** - устарели, уязвимы (BEAST, POODLE). **TLS 1.2** - стандарт, безопасен. **TLS 1.3** (2018) - быстрее (1-RTT handshake вместо 2-RTT), безопаснее (убрали слабые cipher suites). Node.js 12+ поддерживает TLS 1.3 по умолчанию. **mTLS (mutual TLS)** - не только сервер предъявляет сертификат клиенту, но и клиент серверу. Используется для service-to-service коммуникации (микросервисы, zero-trust networks).
**Production best practices:** **1)** Используйте только TLS 1.2+ (отключите TLS 1.0/1.1). **2)** Настройте strong cipher suites, приоритет TLS 1.3. **3)** Включите HSTS (Strict-Transport-Security header) чтобы браузеры всегда использовали HTTPS. **4)** Certificate pinning для критичных API (храните hash публичного ключа сервера, проверяете при подключении). **5)** Мониторьте сроки действия сертификатов (автоматизируйте обновление через Let's Encrypt/certbot).
Для чего в TLS используется hybrid encryption (RSA + AES)?
Работа с сертификатами
**Сертификат X.509 - это цифровой документ, связывающий публичный ключ с владельцем (доменом, организацией, человеком).** Он содержит: публичный ключ, информацию о владельце (CN = Common Name, например, example.com), срок действия, подпись Certificate Authority (CA). **Цепочка доверия:** сертификат сервера подписан промежуточным CA, тот подписан корневым CA. Корневые CA хранятся в trust store браузера/ОС. Если цепочка валидна до доверенного CA - соединение безопасно.
**Три типа сертификатов:** **1) Self-signed** - сервер сам себе CA, браузер не доверяет (годится для dev/testing). **2) Let's Encrypt** - бесплатный CA, автоматическое обновление через ACME протокол, годится для большинства сайтов. **3) Commercial CA** (DigiCert, GlobalSign) - платные, с EV (Extended Validation) проверкой для банков/корпораций. **Для продакшена используется Let's Encrypt или commercial CA**, self-signed только для внутренних сервисов или dev окружения.
**X.509 сертификат содержит:** **Subject** - владелец (CN: example.com, O: Organization). **Issuer** - кто подписал (Let's Encrypt Authority X3). **Validity** - срок действия (Not Before, Not After). **Public Key** - публичный ключ для TLS. **Signature** - цифровая подпись CA (доказывает, что сертификат не подделан). **SAN (Subject Alternative Names)** - дополнительные домены (www.example.com, api.example.com). **PEM/DER форматы:** PEM - base64 в ASCII (начинается с BEGIN CERTIFICATE), DER - бинарный.
**Частые проблемы с сертификатами:** **1)** CERT_HAS_EXPIRED - обновите сертификат (Let's Encrypt автоматически через certbot renew). **2)** DEPTH_ZERO_SELF_SIGNED_CERT - используете self-signed в продакшене или не передали `ca` опцию. **3)** UNABLE_TO_VERIFY_LEAF_SIGNATURE - цепочка сертификатов неполная (не хватает промежуточного CA, используйте fullchain.pem). **4)** Hostname mismatch - CN или SAN не совпадают с доменом (сертификат для example.com, а подключаетесь к api.example.com).
HTTPS автоматически защищает от всех атак, достаточно получить сертификат
HTTPS защищает только канал связи (от прослушки и tampering). Не защищает от XSS, CSRF, SQL injection, уязвимостей в коде
TLS решает проблему безопасности на транспортном уровне - шифрует данные между клиентом и сервером. Но если приложение уязвимо (не экранирует input, не проверяет CSRF токены), атакующий может эксплуатировать это через HTTPS. HTTPS ≠ Secure Application. Это как бронированная дверь в дом с открытыми окнами - защита важна, но недостаточна
Почему Let's Encrypt сертификаты валидны только 90 дней (а не 1-2 года как коммерческие)?
Ключевые идеи
- **Хеширование vs шифрование:** Хеш (SHA-256, scrypt) - one-way, необратимая функция для паролей и checksums. Шифрование (AES, RSA) - two-way, обратимое с ключом для конфиденциальных данных. Для паролей ВСЕГДА используйте медленные хеш-функции (scrypt/bcrypt) с солью, SHA-256 слишком быстрый для brute-force защиты
- **Hybrid encryption в TLS:** RSA/ECDHE для handshake (обмен session key) + AES-GCM для данных. RSA медленный, но не требует предварительного обмена секретом. AES быстрый, но обе стороны должны знать ключ. TLS использует оба: RSA передаёт AES-ключ, затем всё шифруется через AES. Это баланс безопасности и производительности
- **Сертификаты и цепочка доверия:** X.509 сертификат связывает публичный ключ с доменом, подписан CA. Клиент проверяет цепочку: сертификат сервера → промежуточный CA → корневой CA (в trust store браузера). Let's Encrypt - бесплатный CA с автоматическим обновлением через certbot. Self-signed годится только для dev, в продакшене всегда используйте доверенный CA
Связанные темы
Crypto Module - это фундамент безопасности, он используется везде: от хранения паролей до защиты API и HTTPS серверов:
- Streams API — Crypto streams (createCipheriv/createDecipheriv) для шифрования больших файлов без загрузки в память. Pipe через Transform stream для on-the-fly encryption
- HTTP/HTTPS — https.createServer использует crypto module под капотом для TLS. Настройка cipher suites, минимальной версии TLS, mTLS аутентификации
- Child Processes — Безопасный обмен данными между процессами через IPC с шифрованием. Worker threads для CPU-intensive crypto операций (scrypt, RSA keygen) без блокировки event loop
Вопросы для размышления
- Проектирование системы для обмена медицинскими данными между клиниками. Какой тип шифрования использовать: симметричное (AES) или асимметричное (RSA)? Учитывая, что клиники не знают общего секрета заранее, а данные могут быть большими (100MB+)
- Почему JWT токены подписываются (HMAC/RSA signature), а не шифруются? Ведь в токене могут быть конфиденциальные данные (user_id, email). Когда нужна подпись, а когда шифрование?
- В API есть endpoint для загрузки файлов. Файлы хранятся на S3, зашифрованные AES-256. При каждом запросе генерируется новый IV (initialization vector). Клиент жалуется: 'Я скачал один и тот же файл дважды, но хеши разные - файл повреждён?' Что происходит и как объяснить клиенту?