Патчинг и monkey-patching в контексте криптографических библиотек JavaScript — это вмешательство в уже существующие реализации функций, объектов или прототипов с целью изменения поведения без модификации исходного кода библиотеки.
Криптографические библиотеки, такие как Stanford JavaScript Crypto Library, строятся вокруг строгих гарантий детерминированности, неизменности алгоритмов и предсказуемости результата. Любое внешнее вмешательство в цепочку вычислений может привести не просто к багам, а к полной компрометации безопасности.
Ключевая особенность криптографии заключается в том, что корректность алгоритма эквивалентна безопасности. В отличие от UI-логики или бизнес-функций, где ошибка часто приводит к деградации функциональности, здесь ошибка может означать:
Monkey-patching в таком контексте становится не просто техникой расширения, а потенциальной точкой атаки.
SJCL реализован как набор модулей, объединённых в единое пространство
имён sjcl. Многие функции экспортируются напрямую через
объектную структуру:
sjcl.hash.sha256.hash
sjcl.cipher.aes.encrypt
sjcl.random.randomWords
Это означает, что любая часть функциональности доступна для перезаписи в рантайме:
const original = sjcl.hash.sha256.hash;
sjcl.hash.sha256.hash = function (data) {
console.log("Хэширование данных");
return original(data);
};
На первый взгляд это выглядит как удобный способ логирования или расширения функциональности. Однако в криптографии это создаёт фундаментальную проблему доверия к коду.
Если заменить или обернуть функции шифрования, хеширования или генерации случайных чисел, можно незаметно изменить свойства алгоритма.
Пример потенциально опасного патча:
const original = sjcl.random.randomWords;
sjcl.random.randomWords = function (n) {
const result = original(n);
result[0] = 0; // искусственное ослабление энтропии
return result;
};
Такое изменение может привести к предсказуемости случайных значений, что критично для ключей и nonce.
Возможна полная замена алгоритма:
sjcl.hash.sha256.hash = function (data) {
return sjcl.hash.sha1.hash(data);
};
Это снижает уровень безопасности без изменения интерфейса, что особенно опасно в больших системах, где код не контролируется централизованно.
После патчинга становится невозможно гарантировать, какой именно код выполняется в момент криптографической операции. Это ломает:
Если несколько модулей применяют monkey-patching, возникает эффект наложения:
libA -> патчит sjcl.hash
libB -> патчит sjcl.hash
Итоговое поведение становится зависимым от порядка загрузки скриптов, что недопустимо для криптографических операций.
Несмотря на риски, патчинг иногда применяется осознанно.
const enc = sjcl.encrypt;
sjcl.encrypt = function (password, data, params, callback) {
console.debug("Шифрование данных запущено");
return enc.call(sjcl, password, data, params, callback);
};
Даже здесь возникает проблема: логирование может раскрывать структуру данных или метаданные использования криптографии.
Иногда патчат генератор случайных чисел:
sjcl.random.addEntropy = function (data, estimatedEntropy, source) {
console.log("Добавление энтропии из источника:", source);
return sjcl.random._addEntropy(data, estimatedEntropy, source);
};
Такие вмешательства требуют крайней осторожности, так как неправильная оценка энтропии может ослабить генератор случайных чисел.
Вместо замены оригинальных функций используется композиция:
function secureHash(data) {
const result = sjcl.hash.sha256.hash(data);
auditLog("hash", data);
return result;
}
Такой подход сохраняет оригинальный алгоритм неизменным.
Создание отдельного слоя поверх библиотеки:
const CryptoLayer = {
hash(data) {
return sjcl.hash.sha256.hash(data);
}
};
Это исключает необходимость вмешательства в оригинальный объект
sjcl.
Для предотвращения случайного патчинга:
Object.freeze(sjcl.hash.sha256);
Object.freeze(sjcl.random);
Это не защищает от преднамеренного обхода, но снижает риск случайных изменений.
Передача зависимостей явно:
function encryptor(sjclLib) {
return {
encrypt(data, key) {
return sjclLib.encrypt(key, data);
}
};
}
Такой подход делает поведение системы предсказуемым и тестируемым.
В продакшн-системах иногда требуется проверка целостности библиотеки:
const baselineHash = sjcl.hash.sha256.hash.toString();
function checkIntegrity() {
if (sjcl.hash.sha256.hash.toString() !== baselineHash) {
throw new Error("Обнаружено изменение криптографического ядра");
}
}
Это не абсолютная защита, но полезный слой мониторинга.
Monkey-patching в криптографии — это не просто техника расширения, а изменение доверенной вычислительной среды. В случае SJCL это особенно критично, поскольку библиотека часто используется в браузерных приложениях, где отсутствует контроль над окружением выполнения.
Основная архитектурная рекомендация сводится к принципу неизменности криптографического ядра:
Такой подход позволяет сохранить математическую корректность алгоритмов и предсказуемость системы даже в условиях сложной интеграции с внешними модулями.