Риски хранения
чувствительных данных в браузере
При работе с библиотекой idb-keyval данные
сохраняются в IndexedDB — высокопроизводительном хранилище браузера.
Несмотря на удобство и скорость, хранение чувствительных данных в
IndexedDB несет определенные риски:
- Доступ третьих лиц через XSS: Если на сайте
присутствуют уязвимости типа Cross-Site Scripting, злоумышленник может
выполнить скрипт, получить доступ к хранилищу и извлечь сохраненные
данные.
- Физический доступ к устройству: Данные в IndexedDB
сохраняются локально. При доступе к устройству злоумышленник может
экспортировать базу данных браузера и получить сохраненные
значения.
- Отсутствие встроенного шифрования: IndexedDB и
idb-keyval не шифруют данные по умолчанию. Все значения хранятся в
открытом виде, что делает их уязвимыми при компрометации устройства или
профиля браузера.
- Синхронизация и кэширование браузера: Некоторые
браузеры могут синхронизировать IndexedDB между устройствами. Это
увеличивает площадь атаки и риск утечки данных.
Основные
рекомендации при работе с чувствительными данными
- Минимизировать объем локального хранения: Хранить
только минимально необходимую информацию, избегая паролей, токенов
доступа или персональных данных в открытом виде.
- Использовать шифрование на стороне клиента: Перед
сохранением значимых данных применять симметричное шифрование, например
AES через библиотеку Web Crypto API, и хранить только зашифрованные
строки.
- Ограничивать время хранения: Для токенов и
временных данных устанавливать политику автоматического удаления через
таймер или при закрытии сессии.
- Валидация и фильтрация ввода: Для предотвращения
XSS-атак важно проверять и очищать все данные, которые могут попадать в
хранилище.
- Разделение данных по категориям чувствительности:
Разделять критически важные данные (например, пароли) и менее значимые
(например, настройки интерфейса), чтобы риски компрометации
минимизировать.
Альтернативные подходы
к хранению данных
- Серверное хранение с токенами доступа: Вместо
сохранения паролей и конфиденциальной информации в браузере
рекомендуется использовать серверное 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 удобно для быстрых и структурированных
данных, однако для конфиденциальной информации он требует обязательного
шифрования и продуманной стратегии управления сроком жизни данных. В
большинстве случаев безопаснее переносить хранение критических данных на
сервер или использовать специализированные безопасные хранилища.