Обидва підходи змінюють ваш вихідний код певним чином, проте переслідують різні цілі.
Мініфікація стискає файл: вирізає пробіли, коментарі, перенос рядків, а імена змінних і функцій зводить до мінімуму. При цьому структура не змінюється. У JavaScript це особливо ефективно, адже форматування й відступи йому байдуже (окрім крапок з комою). Якщо змінна чи функція експортується за межі модуля, її ім’я лишається незмінним, аби уникнути плутанини. У результаті файл стає меншим і швидше вантажиться з сервера, хоча на швидкість виконання це не впливає. Це як написати: «Првт,звтрбдшшлк.взмпв» — сенс той самий, просто треба вміти читати.
У обфускації ж інша мета — максимально ускладнити можливість реверс-інжинірингу, тобто покликана не дати розібратися, що ж відбувається у вашому коді. Це може бути корисним, коли ви пишете якийсь супер-пупер унікальний алгоритм, за який берете гроші. Ну або зловмисний скрипт.
Під час обфускації структура коду зазнає суттєвих змін. Навіть проста функція стає надзвичайно заплутаною, логіка може розбиватися на окремі шматки, що розкидаються по усьому файлу, прості алгоритми можуть замінятися дуже екзотичними підходами, а часто ще й використовуються дуже неочевидні особливості мови, щоб остаточно зламати ваш мозок. Ще можуть додаватися шматки "мертвого коду", просто аби насмітити і збити вас з пантелику.
Це як зібрати автомобіль з підручного мотлоху: замість колес швабри, замість фар домашні капці, замість керма шухляда, але попри все воно їздить.
Наприклад, банальний Hello, world!
function hi() {
console.log("Hello World!");
}
hi();може перетворитися на ось такого кракена:
function _0x2925(_0x542d0a,_0x2c3c78){var _0xa1e55d=_0xa1e5();return _0x2925=function(_0x292568,_0x3c856f){_0x292568=_0x292568-0x1ac;var _0x3c7a1b=_0xa1e55d[_0x292568];return _0x3c7a1b;},_0x2925(_0x542d0a,_0x2c3c78);}(function(_0x4d614b,_0x34a9e8){var _0x209520=_0x2925,_0x55e423=_0x4d614b();while(!![]){try{var _0x1f7119=-parseInt(_0x209520(0x1b6))/0x1+-parseInt(_0x209520(0x1af))/0x2*(-parseInt(_0x209520(0x1b2))/0x3)+-parseInt(_0x209520(0x1b1))/0x4+parseInt(_0x209520(0x1ad))/0x5+parseInt(_0x209520(0x1b7))/0x6*(-parseInt(_0x209520(0x1ac))/0x7)+parseInt(_0x209520(0x1ae))/0x8*(parseInt(_0x209520(0x1b5))/0x9)+parseInt(_0x209520(0x1b0))/0xa;if(_0x1f7119===_0x34a9e8)break;else _0x55e423['push'](_0x55e423['shift']());}catch(_0x259b9c){_0x55e423['push'](_0x55e423['shift']());}}}(_0xa1e5,0xdb2b8));function _0xa1e5(){var _0x135cc9=['1630085KNrFJy','5633484gfXmBA','7uZhyxi','8869945iTvxpi','661592MxYeut','314PMTrKl','19727980eYXqzm','5305732XTxbqH','18414OMZNhs','Hello\x20World!','log','9OSzIcJ'];_0xa1e5=function(){return _0x135cc9;};return _0xa1e5();}function hi(){var _0x3be6dc=_0x2925;console[_0x3be6dc(0x1b4)](_0x3be6dc(0x1b3));}hi();Звичайно, з появою ШІ процес деобфускації може значно спроститися. До прикладу, ось цей сніпет зверху звичайний ChatGPT розібрав доволі швидко, але це й не дивно, адже я використав доволі прості налаштування для спотворення коду. Проте й із набагато складнішим прикладом він впорався непомірно швидше, ніж це зробила б людина. А це значить лише одне — алгоритми обфускації стануть ще заплутанішими та складнішими.
Підсумуємо — мініфікація слугує для зменшення фізичного розміру вашого файлу, а от обфускація покликана спотворювати ваш код до невпізнаваности, аби приховати його справжнє призначення. І, звичайно ж, ці методики можуть використовуватися разом.
Тож наступного разу, коли зустрінете у коді монстра з десятками _0x123abc, не поспішайте кликати екзорциста. Може, це просто «Hello world», який вирішив переодягнутись у костюм пінгвіна і хоче вкрасти ваші паролі.