Web Crypto API изначально проектировался с учётом строгой модели безопасности браузера. Все криптографические операции выполняются в пределах изолированного контекста, который ограничен политиками происхождения (Same-Origin Policy) и механизмами контроля доступа к данным.
Ключевая особенность — невозможность прямого доступа к приватным
ключам и чувствительным материалам через обычные JavaScript-объекты.
Вместо этого используются объекты типа CryptoKey, которые
инкапсулируют внутреннее состояние и управляются самим браузером.
const key = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
console.log(key); // CryptoKey, а не сырой ключ
Даже при выводе в консоль содержимое ключа не раскрывается. Это предотвращает утечку данных через логирование, сериализацию или случайную передачу.
Каждый ключ создаётся с параметром extractable, который
определяет возможность извлечения его содержимого.
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
false, // ключ нельзя экспортировать
["encrypt", "decrypt"]
);
Если extractable: false, попытка экспорта завершится
ошибкой:
await crypto.subtle.exportKey("raw", key); // выбросит исключение
Это критический механизм защиты от XSS-атак: даже если вредоносный
скрипт получит доступ к объекту CryptoKey, он не сможет
извлечь его содержимое.
Web Crypto API доступен исключительно в безопасных контекстах (secure contexts), то есть:
Попытка использовать API в небезопасной среде приведёт к отсутствию доступа:
if (!window.crypto || !window.crypto.subtle) {
throw new Error("Web Crypto API недоступен");
}
Это предотвращает атаки типа man-in-the-middle, при которых трафик может быть перехвачен и модифицирован.
Все криптографические операции жёстко привязаны к origin (домен + протокол + порт). Ключи, созданные на одном origin, не могут быть использованы на другом.
Пример:
https://example.com не может получить доступ к ключам
https://api.example.comЭто защищает от утечек между различными частями приложения или сторонними встроенными ресурсами (iframe).
Интерфейс crypto.subtle намеренно минималистичен. Он не
предоставляет:
Асинхронность играет важную роль: она предотвращает блокировки и затрудняет тайминговые атаки.
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
data
);
XSS (Cross-Site Scripting) остаётся одной из главных угроз для клиентской криптографии. Если злоумышленник внедряет скрипт в страницу, он получает доступ к:
CryptoKeyДаже несмотря на защиту от экспорта ключей, XSS может:
Пример уязвимого сценария:
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
sensitiveData
);
При XSS злоумышленник может заменить sensitiveData или
перехватить его до вызова.
Один из самых эффективных способов защиты — строгая CSP:
Content-Security-Policy:
default-src 'self';
script-src 'self';
object-src 'none';
Запрещает:
Inline JavaScript — частая причина XSS. Использование только внешних файлов снижает риск:
<script src="app.js"></script>
Вместо:
<script>
// небезопасно
</script>
Секреты (например, токены) не должны быть доступны через Jav * aScript:
Set-Cookie: session=abc123; HttpOnly; Secure;
Это предотвращает их кражу даже при XSS.
Ключи не должны храниться в:
Если хранение необходимо, предпочтительно использовать
CryptoKey с extractable: false.
Каждый ключ должен иметь строго ограниченные права:
["encrypt"] // вместо ["encrypt", "decrypt"]
Это снижает ущерб при компрометации.
Критические операции можно изолировать:
<iframe sandbox="allow-scripts"></iframe>
Это создаёт дополнительный уровень изоляции выполнения.
Механизм защиты от XSS, предотвращающий небезопасную работу с DOM:
window.trustedTypes.createPolicy("default", {
createHTML: (input) => input
});
Позволяет контролировать точки вставки HTML.
Важно учитывать, что Web Crypto API защищает только криптографические операции, но не весь поток данных.
Уязвимости возникают до и после шифрования:
До:
После:
Рекомендуется:
innerHTML,
eval)При XSS возможно переопределение глобальных функций:
crypto.subtle.encrypt = function() {
// перехват
};
Защита:
const subtle = crypto.subtle;
Object.freeze(subtle);
Или:
const { encrypt } = crypto.subtle;
Фиксация ссылок снижает риск подмены.
Web Workers позволяют выполнять криптографию в отдельном потоке:
const worker = new Worker("crypto-worker.js");
Преимущества:
Передача ключей:
worker.postMessage({ key });
CryptoKey может передаваться, но остаётся защищённым
объектом.
Несмотря на встроенные механизмы защиты:
Основная модель безопасности предполагает:
При проектировании необходимо учитывать:
Что защищается:
От чего защищается:
От чего не защищает:
extractable: falseТакая комбинация позволяет использовать Web Crypto API максимально безопасно даже в условиях потенциальных XSS-угроз.