Хранение чувствительных данных: риски и альтернативы

Риски хранения чувствительных данных в браузере

При работе с библиотекой idb-keyval данные сохраняются в IndexedDB — высокопроизводительном хранилище браузера. Несмотря на удобство и скорость, хранение чувствительных данных в IndexedDB несет определенные риски:

  • Доступ третьих лиц через XSS: Если на сайте присутствуют уязвимости типа Cross-Site Scripting, злоумышленник может выполнить скрипт, получить доступ к хранилищу и извлечь сохраненные данные.
  • Физический доступ к устройству: Данные в IndexedDB сохраняются локально. При доступе к устройству злоумышленник может экспортировать базу данных браузера и получить сохраненные значения.
  • Отсутствие встроенного шифрования: IndexedDB и idb-keyval не шифруют данные по умолчанию. Все значения хранятся в открытом виде, что делает их уязвимыми при компрометации устройства или профиля браузера.
  • Синхронизация и кэширование браузера: Некоторые браузеры могут синхронизировать IndexedDB между устройствами. Это увеличивает площадь атаки и риск утечки данных.

Основные рекомендации при работе с чувствительными данными

  1. Минимизировать объем локального хранения: Хранить только минимально необходимую информацию, избегая паролей, токенов доступа или персональных данных в открытом виде.
  2. Использовать шифрование на стороне клиента: Перед сохранением значимых данных применять симметричное шифрование, например AES через библиотеку Web Crypto API, и хранить только зашифрованные строки.
  3. Ограничивать время хранения: Для токенов и временных данных устанавливать политику автоматического удаления через таймер или при закрытии сессии.
  4. Валидация и фильтрация ввода: Для предотвращения XSS-атак важно проверять и очищать все данные, которые могут попадать в хранилище.
  5. Разделение данных по категориям чувствительности: Разделять критически важные данные (например, пароли) и менее значимые (например, настройки интерфейса), чтобы риски компрометации минимизировать.

Альтернативные подходы к хранению данных

  • Серверное хранение с токенами доступа: Вместо сохранения паролей и конфиденциальной информации в браузере рекомендуется использовать серверное API. На клиенте хранить только ограниченные по сроку действия токены (JWT, OAuth), при этом их можно шифровать перед сохранением.
  • Секретные менеджеры и безопасные хранилища: Для десктопных и мобильных приложений есть безопасные хранилища операционной системы, такие как Keychain на iOS/macOS или Keystore на Android, где хранение данных осуществляется с аппаратным шифрованием.
  • Session Storage для временных данных: Если данные нужны только в рамках одной сессии, безопаснее использовать sessionStorage, поскольку он очищается при закрытии вкладки, снижая риск длительного хранения.
  • Шифрование на уровне IndexedDB: Если использование idb-keyval неизбежно, данные можно шифровать до записи в базу и расшифровывать при чтении, обеспечивая дополнительный уровень защиты.

Практическая реализация шифрования с idb-keyval

Для безопасного хранения данных через idb-keyval можно использовать Web Crypto API:

import { set, get } from 'idb-keyval';

// Функция для генерации ключа AES
async function generateKey() {
  return crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
}

// Шифрование строки
async function encryptData(key, data) {
  const enc = new TextEncoder();
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    enc.encode(data)
  );
  return { iv, encrypted };
}

// Дешифрование строки
async function decryptData(key, iv, encrypted) {
  const decrypted = await crypto.subtle.decrypt(
    { name: "AES-GCM", iv },
    key,
    encrypted
  );
  const dec = new TextDecoder();
  return dec.decode(decrypted);
}

// Пример сохранения зашифрованного объекта
async function saveSecure(key, value) {
  const cryptoKey = await generateKey();
  const { iv, encrypted } = await encryptData(cryptoKey, JSON.stringify(value));
  await set('secure-data', { iv, encrypted, cryptoKey });
}

// Пример извлечения и расшифровки данных
async function loadSecure() {
  const stored = await get('secure-data');
  if (!stored) return null;
  const data = await decryptData(stored.cryptoKey, stored.iv, stored.encrypted);
  return JSON.parse(data);
}

Такой подход позволяет хранить чувствительные данные локально без риска их простой компрометации при стандартных атаках.

Заключение по подходу

Использование idb-keyval удобно для быстрых и структурированных данных, однако для конфиденциальной информации он требует обязательного шифрования и продуманной стратегии управления сроком жизни данных. В большинстве случаев безопаснее переносить хранение критических данных на сервер или использовать специализированные безопасные хранилища.