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

Шифрование данных — преобразование данных в нечитаемый формат с помощью криптографического ключа. Различают шифрование at rest (хранение) и in transit (передача). Без ключа расшифровка вычислительно невозможна.

Зачем нужно

GDPR, PCI DSS, HIPAA требуют шифрование персональных и платёжных данных. При утечке зашифрованной БД злоумышленник получает бесполезный зашифрованный blob без ключей. TLS защищает данные при передаче от прослушивания (MITM).

Где используется

  • In transit: HTTPS (TLS 1.2/1.3) для всего трафика
  • At rest: шифрование PII в БД (номера паспортов, карт)
  • Шифрование файлов в хранилищах (S3, GCS)
  • End-to-end шифрование в мессенджерах

Основной контент

Типы шифрования

Симметричное   — один ключ для шифрования и расшифровки (AES-256-GCM)
               Быстро, используется для данных at rest

Асимметричное  — публичный ключ шифрует, приватный расшифровывает (RSA, EC)
               Медленнее, используется для обмена ключами (TLS handshake)

Гибридное      — TLS: асимметричное для обмена ключом, симметричное для данных

AES-256-GCM для шифрования данных at rest (Node.js)

const crypto = require('crypto');

const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32; // 256 бит
const IV_LENGTH = 16;  // 128 бит

function encrypt(plaintext, key) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv(ALGORITHM, key, iv);

  const encrypted = Buffer.concat([
    cipher.update(plaintext, 'utf8'),
    cipher.final,
  ]);

  const authTag = cipher.getAuthTag; // GCM authentication tag

  // Храним: iv + authTag + encrypted
  return Buffer.concat([iv, authTag, encrypted]).toString('base64');
}

function decrypt(encryptedBase64, key) {
  const data = Buffer.from(encryptedBase64, 'base64');
  const iv = data.slice(0, IV_LENGTH);
  const authTag = data.slice(IV_LENGTH, IV_LENGTH + 16);
  const encrypted = data.slice(IV_LENGTH + 16);

  const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
  decipher.setAuthTag(authTag);

  return Buffer.concat([
    decipher.update(encrypted),
    decipher.final,
  ]).toString('utf8');
}

// Ключ из переменной окружения (32 байта base64)
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'base64');

// Шифрование поля в БД
const encryptedSSN = encrypt(user.ssn, key);
await db.query('UPDATE users SET ssn_encrypted = $1', [encryptedSSN]);

TLS: настройка HTTPS в Node.js

const https = require('https');
const fs = require('fs');

const server = https.createServer({
  key: fs.readFileSync('/path/to/private.key'),
  cert: fs.readFileSync('/path/to/certificate.crt'),
  minVersion: 'TLSv1.2', // Минимум TLS 1.2
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
    'ECDHE-RSA-AES256-GCM-SHA384',
  ].join(':'),
}, app);

HSTS — принудительный HTTPS

# Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# После включения браузеры не будут делать HTTP-запросы к сайту

Частые ошибки

  • Использование ECB режима AES — шифрует блоки независимо, паттерны видны
  • Повторное использование IV (Initialization Vector) с одним ключом — катастрофа для GCM
  • Хранение ключа шифрования рядом с зашифрованными данными в одной БД
  • Самописная криптография — используйте только проверенные библиотеки (Node.js crypto, libsodium)
  • TLS 1.0/1.1 включён — эти версии уязвимы (POODLE, BEAST)

Связанные темы

Ресурсы