У меня есть этот фрагмент кода в моем js:
export const encrypt = (data, key) => {
const encrypted = CryptoJS.AES.encrypt(
JSON.stringify(data),
key
).toString();
return encrypted;
};
но когда я расшифровываю его в своем бэкэнде Laravel:
Route::post('/decrypt', function (Request $request) {
try{
$encrypted = $request->input("data");
Crypt::decrypt($encrypted);
}
catch(DecryptException $e) {
dd("error", $e);
}
});
это дает мне DecryptException «Полезная нагрузка недействительна»
Вопрос: Почему вы хотите/необходимо шифровать данные между интерфейсом (браузером) и сервером (сервером)? Если вам нужна безопасность, вы можете просто использовать HTTPS — это автоматически зашифрует весь трафик между браузером и вашим веб-сервером, а не только отдельные фрагменты данных.
Для расшифровки с помощью Crypt::decrypt()
из PHP/Laravel логика шифрования CryptoJS должна имитировать логику шифрования PHP/Laravel. Вы можете найти последнее, например. вот . Как вы можете видеть, ваш текущий код CryptoJS несовместим, например. MAC отсутствует и формат отличается (PHP/Laravel: строка JSON в кодировке Base64, содержащая зашифрованный текст, IV и MAC).
Вы передаете ключ клиенту?
@gre_gor, что ты имеешь в виду, говоря о передаче ключа клиенту? Потому что я поместил ключ в оба конца файла .env.
@ADyson Я просто пробовал что-то новое, действительно ли нет необходимости добавлять шифрование в качестве еще одного уровня безопасности?
@marouaneebourk, если только ваши данные по какой-то причине не являются действительно сверхконфиденциальными (например, правительственная тайна, очень конфиденциальные личные данные или что-то еще, регулируемое специальным законодательством, требующим дополнительной безопасности), то почти наверняка нет. HTTPS должно быть достаточно для подавляющего большинства случаев.
@marouaneebourk, что именно вы подразумеваете под «я вставил ключ в оба конца файла .env»? Оба конца чего? Похоже, что один конец представляет собой сценарий Javascript. Обычно это работает в браузере, где нельзя хранить секреты. Вы хотите сказать, что это на самом деле скрипт nodeJS, работающий на сервере? Пожалуйста, сделайте это на 100% ясным в своем вопросе, потому что такие различия могут быть значительными.
@ADyson, когда я сказал оба конца, я имел в виду интерфейс и серверную часть, поскольку мой laravel .env не читается, но в моем пакете реагирования я не знаю, как именно все работает (я все еще новичок)
Все, что попадает в браузер, может быть прочитано конечным пользователем, если он знает это, а также, возможно, расширениями браузера и т. д., в зависимости от предоставленных разрешений. Поэтому вставлять туда ключ небезопасно. Но вы до сих пор не подтвердили, используется ли ваш JS в браузере или нет.
Что ж... мир шифрования чрезвычайно сложен, поэтому никогда не принимайте ничего как должное. Хотя шифрование/дешифрование с помощью одного и того же инструмента несколько тривиально, выполнение одного и того же в разных системах может оказаться весьма затруднительным.
Здесь, если все элементы на месте (правильная установка, правильная конфигурация...!), проблема может заключаться в data
или key
.
Как отметил @Kris, вам следует проверить, «одинаков ли ключ с обеих сторон». На первый взгляд это может показаться очевидным... но на самом деле это не так.
По сути, перед шифрованием/дешифрованием CryptoJS сначала проверяет, является ли «ключ» реальным ключом или открытым текстовым паролем (называемым парольной фразой). В последнем случае он преобразует открытый текстовый пароль в ключ, а затем обрабатывает данные. Опять же... какой тип преобразования? Ключу какой длины? Все зависит от вашей конфигурации и настроек по умолчанию вашей версии. Обычно размер ключа по умолчанию составляет 256.
Я не вижу никаких настроек пароля в вашем примере с серверной частью, но вам следует проверить, обрабатывает ли он ключ точно так же, как это делает CryptoJS. Если у вас есть контроль, вы можете попытаться воспроизвести в серверной части точное поведение CryptoJS с ключом. Но выполнение преобразования самостоятельно может значительно сэкономить время, предоставив CryptoJS реальный ключ, который он не будет пытаться обработать.
Зашифрованные данные обычно являются двоичными и не подходят для транспортировки. Существует множество форм безопасной передачи данных, и одной из наиболее широко используемых является BASE64. На самом деле это то, во что CryptoJS преобразует данные при использовании toString()
, как вы это делаете.
Теперь вам нужно проверить, что ваш сервер ожидает этот формат и правильно с ним работает, как в интерфейсе, так и в процедуре дешифрования. Например, если Crypt::decrypt()
ожидает двоичные данные, вам следует сначала декодировать полученные данные BASE64.
Похоже, вы столкнулись с проблемой, поскольку методы шифрования и библиотеки, которые вы используете в JavaScript и Laravel, по умолчанию несовместимы. CryptoJS.AES.encrypt и Crypt::decrypt в Laravel используют разные конфигурации и алгоритмы по умолчанию. Вот несколько моментов, которые помогут обеспечить совместимость между вашими методами шифрования JavaScript и Laravel:
Согласованные параметры шифрования. Убедитесь, что JavaScript и Laravel используют одни и те же параметры шифрования (алгоритм, режим, заполнение и т. д.). По умолчанию CryptoJS.AES.encrypt использует режим CBC и заполнение PKCS7. Фасад Laravel Crypt также использует режим AES-256-CBC и заполнение PKCS7, но для этого требуется определенный размер и конфигурация ключа.
Убедитесь, что ключ, используемый в JavaScript, соответствует ключу, используемому в Laravel, и что вы правильно управляете IV (вектором инициализации). Метод CryptoJS.AES.encrypt также может потребовать указания IV. В Laravel фасад Crypt обрабатывает IV внутри себя, но в специальной реализации вам придется управлять IV вручную.
Кодирование: убедитесь, что данные правильно кодируются и декодируются между системами. CryptoJS выводит строки в кодировке Base64, а Laravel также ожидает кодировку Base64.
Я думаю, что ниже должно помочь:
export const encrypt = (data, key) => {
// Ensure your key is 32 bytes for AES-256
const keySize = 256 / 32;
const keyHex = CryptoJS.enc.Hex.parse(key);
const iv = CryptoJS.lib.WordArray.random(16); // Random 16 bytes IV
const encrypted = CryptoJS.AES.encrypt(
JSON.stringify(data),
keyHex,
{ iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
);
// Encode both the IV and the encrypted data
return `${iv.toString(CryptoJS.enc.Base64)}:${encrypted.toString()}`;
};
Route::post('/decrypt', function (Request $request) {
try {
// Extract the IV and encrypted data
$data = $request->input('data');
list($iv, $encrypted) = explode(':', $data, 2);
// Decode the Base64 encoded values
$iv = base64_decode($iv);
$encrypted = base64_decode($encrypted);
// Your key should be 32 bytes for AES-256
$key = env('AES_KEY'); // Ensure this key matches the one used in JavaScript
// Decrypt using openssl
$decrypted = openssl_decrypt(
$encrypted,
'aes-256-cbc',
$key,
OPENSSL_RAW_DATA,
$iv
);
return response()->json(['data' => json_decode($decrypted, true)]);
} catch (DecryptException $e) {
return response()->json(['error' => 'Decryption failed'], 400);
}
});
Большое спасибо за этот ответ, та часть, где вы упомянули отступы и режим, помогла мне больше всего.
Напоминание: ответы, созданные с помощью инструментов искусственного интеллекта, не допускаются в Stack Overflow. Узнать больше
Это функция, которая работала у меня с функцией Crypt::decrypt в laravel. вам просто нужно отключить десериализацию
export function encrypt(data, key) {
let iv = CryptoJS.lib.WordArray.random(16),
generatedKey = CryptoJS.enc.Base64.parse(key);
let options = {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
};
let encrypted = CryptoJS.AES.encrypt(
JSON.stringify(data),
generatedKey,
options
);
encrypted = encrypted.toString();
iv = CryptoJS.enc.Base64.stringify(iv);
let result = {
iv: iv,
value: encrypted,
mac: CryptoJS.HmacSHA256(iv + encrypted, generatedKey).toString(),
};
result = JSON.stringify(result);
result = CryptoJS.enc.Utf8.parse(result);
result = CryptoJS.enc.Base64.stringify(result);
return result;
}
private function decryptValue($value)
{
try {
return json_decode(Crypt::decrypt($value, false), true);
} catch (\Exception $e) {
throw new DecryptException('Decryption failed: ' . $e->getMessage());
}
}
зависит от того, как вы шифруете данные, что такое
data
и одинаков ли ключ с обеих сторон