Protection Map: що захистила збірка

HTML-звіт, який показує, що AfterPack зробив із вашим кодом: кожен токен розфарбовано за тим, скільки трансформацій він зазнав.

Protection Map є способом перевірити ваш захист: ваш оригінальний вихідний код, у якому кожен токен розфарбовано за тим, скільки трансформацій він зазнав.

$ npx afterpack@latest dist/

Ви отримуєте таку мапу:

entitlements.jslight · complexity 5Open full size
entitlements.js, 54 lines, built at the light preset with a directive around the signature check. Click any token to see what was applied to it.

Як її читати

Токени розфарбовано за оцінкою складності: це те, скільки трансформацій зазнав кожен токен, і саме на це число націлюється complexity. Що червоніший токен, то більше трансформацій він зазнав:

КолірОцінка складності
СірийСкладності не додано. Код, який навмисно залишено читабельним, теж сірий.
ЗеленийМенше 4
ЖовтийВід 4 до 7
ЧервонийВід 7 до 10
Темно-червонийПонад 10

Колір показує, скільки застосовано. Виділіть токен, і інспектор покаже, через що пройшла його ділянка: застосовані трансформації, яку частину ділянки вони охоплюють і чому код, що лишився читабельним, лишився таким. Мапа від старішого рушія не має оцінок для окремих токенів і показує весь захищений код одним кольором. Оголошення, яке переписав рушій, наприклад var на let або функцію на стрілкову, наведено в рядку «Also rewritten». Форматер скасовує такі переписування, тож вони не додають ні складності, ні кольору.

Найсильніша трансформація, що торкнулася токена, визначає його мітку класу відновлення, яку показано поруч із його трансформаціями. Це класи статичного зловмисника: вони описують, із чим стикається той, хто читає байти. Міток чотири:

МіткаКлас відновленняЩо це означає
ЗбереженоНемаєНе зачеплено: сторонні імпорти, публічні експорти, усе, що виключено або позначено skip. Залишено читабельним, щоб виклики й далі розв'язувалися.
Перейменовано й закодованополіноміально-ін'єктивнийІдентифікатори перейменовано, рядки закодовано, потік керування сплощено, тотожності злито. Структуру можна відновити, доклавши реальних зусиль; це перемагає AI-пояснювачі та стандартні деобфускатори.
Сплощено й злитополіноміально-неін'єктивнийЛогіку переписано так, що кілька різних оригіналів зводяться до одного результату, і зловмисник відновлює еквівалентне обчислення, а не ваше. Жодна трансформація, що постачається, сьогодні не досягає цього класу.
Знищено й злитодеструктивне злиттяРозкладено й злито в межах усього бандла. Статичний читач відновлює еквівалентну програму, а не те, який саме оригінал написали ви. Недоступно; див. нижче.

Наведіть курсор на токен, щоб побачити застосовані до нього трансформації, згруповані за категоріями (наприклад, сплощення потоку керування), разом із класом, до якого він потрапляє. Спробуйте в живому демо.

Число за кольором є оцінкою складності. Ділянку, яка зазнала менше трансформацій, ніж ви очікували, варто підняти через її preset або позначити preset=hard.

Колір не є гарантією безпеки

Захищена ділянка протистоїть статичному читанню: grep, інструментам для AST, «сліпій» LLM. Значення, яке використовує код під час виконання, усе одно доступне для спостереження зловмиснику, який запускає його в інструментованому середовищі виконання. Справді цінні секрети тримайте на сервері. Мапа показує, із чим стикається статичний читач, і нічого більше.

Позначте цінний код

Мапа показує більше трансформацій там, де ви їх запитуєте. Дві форми директив покривають майже кожен випадок. Вбудована позначка охоплює літерал або вираз у решті свого рядка. Блокова позначка, що стоїть окремо на своєму рядку, відкриває ділянку, яку закриває парний /* @afterpack end */; покривається все між ними, зокрема цілі тіла функцій.

// A single literal — API keys, license secrets, sensitive constants:
const SECRET = /* @afterpack preset=extreme */ "EXPECTED_LICENSE_SECRET";

// A whole function — validators, anti-cheat, payment flow.
// The opener is alone on its line; the matching end closes the block:
/* @afterpack preset=hard */
export function checkLicense(token: string): LicenseClaims | null {
  // ...everything in here is at complexity target 12...
}
/* @afterpack end */

// A span of several statements:
/* @afterpack preset=extreme */
const claims = JSON.parse(atob(payload));
if (claims.secret !== SECRET) return null;
/* @afterpack end */

Якщо позначена вами ділянка показує не більше трансформацій, ніж код довкола неї, директива не спрацювала. Зазвичай це означає, що компілятор прибрав коментар раніше, ніж його побачив AfterPack. AfterPack про це повідомляє. Повна граматика директив наведена в розділі Директиви; позначки, використані вище, це preset=hard і preset=extreme.

Free проти Pro

Free уже перейменовує й кодує на пресеті за замовчуванням light, а починаючи з medium ще й сплощує потік керування та зливає тотожності в межах файлу. Усе це залишається в поліноміально-ін'єктивному класі: статичний читач відновлює вашу оригінальну структуру ціною реальних зусиль, які доводиться повторювати з кожною збіркою.

Pro додає точкове націлювання на окремі ділянки, яке дає змогу розфарбувати саме той десяток функцій, де помилка коштує дорого, а також дві трансформації посилення (selfIntegrity, comparisonHardening). Free добре покриває основну частину реальної кодової бази; Pro потрібен довкола перевірок ліцензій і секретів. Порівняння пліч-о-пліч див. у розділі Тарифи.

Класи «Сплощено й злито» та «Знищено й злито» сьогодні не створює жодна трансформація, що постачається. Див. Класи відновлення.

Згенеруйте свою

$ npx afterpack@latest dist/

Ви отримуєте той самий інтерактивний вигляд, відображений для вашого власного вихідного коду: статичний HTML-файл без входу і без телеметрії, який можна прикріпити до PR. Мапа відображає ваш оригінальний вихідний код, тому за замовчуванням записується лише тоді, коли ваша збірка видала source map, за яким рушій може пройти назад, і не в CI (CI=true або CI=1). В іншому разі її треба ввімкнути явно: передайте protectionMap: { enabled: true } у параметрах плагіна, і плагін сам увімкне мапу бандлера. Передайте --protectionMap.enabled=false, щоб її пропустити. Вказуйте на каталог збірки: Next.js пише в .next/, Nuxt у .output/, більшість бандлерів у dist/.

Де зберігається ваша мапа

Локальна (Free) збірка записує мапу в .afterpack/protectionMap.html на вашій машині і нікуди нічого не надсилає. Каталог має власний .gitignore, тож git його ігнорує. CLI відмовляється записувати мапу в шлях, який роздається сервером. Опублікована Protection Map містить увесь ваш вихідний код. Ніколи не публікуйте її.

Хмарна збірка Pro не записує локального файлу. AfterPack зберігає її мапу в дашборді, а коли protectionMap.enabled увімкнено, збірка після завершення виводить посилання на цю мапу. Мапу створює хмарний рушій, який виконав обфускацію, єдиний рушій, що враховує ваші директиви preset=hard, preset=extreme і skip, тож це єдина мапа, яка може показати їхній ефект.

Збережена мапа містить ваш оригінальний вихідний код

Protection Map є вашим кодом, розфарбованим. У хмарній збірці Pro вона зберігається на інфраструктурі AfterPack і доступна для читання кожному учаснику робочого простору, якому належить проєкт. Якщо ваш вихідний код не повинен бути доступним усьому робочому простору, вимкніть її. Див. нижче.

Що саме зберігається:

  • Дані мапи для кожної збірки: ваш оригінальний вихідний код і метрики для кожного токена, що стоять за кольорами, у Cloudflare R2, зашифровані у стані спокою за допомогою AES-256, разом із рядком підсумкових чисел (покриття, роздування, кількість слабких місць), які дашборд показує на графіках. Ваш обфускований результат не зберігається.
  • В межах одного проєкту, того, до якого прив'язано ключ збірки.
  • Доступні для читання лише учасникам цього робочого простору. Не публічні, не індексуються, без посилання для поширення.
  • Зберігаються один рік і для останніх 500 збірок на проєкт, залежно від того, що закінчиться раніше. Щогодинне завдання видаляє все, що виходить за будь-яку з цих меж. Однорічне вікно дає змогу порівнювати реліз із попереднім. Обмеження у 500 збірок не дає проєкту, який збирається багато разів на день, накопичити копії вихідного коду за цілий рік; для такого проєкту саме це обмеження спрацьовує першим.
  • Збірка, яка не змогла зберегти мапу, натомість залишає примітку з причиною та відповідними розмірами, але ніколи з вашим кодом і ніколи з назвою файлу. Вона зберігається й видаляється за тим самим графіком, а дашборд показує таку збірку як не збережено, а не як збірку, яка ніколи не запускалася.
  • Коли це вікно минуло, дашборд так і пише. Примітки й мапи застарівають разом; історія ваших збірок ні. Збірка, старша за наведений вище строк зберігання, показується як немає запису.

Як це вимкнути

Зберігання мап увімкнене за замовчуванням, для кожного проєкту. Адміністратор робочого простору може вимкнути його в налаштуваннях проєкту. Після цього нові хмарні збірки цього проєкту нічого не генерують і не зберігають; уже збережені мапи застарівають за наведеним вище строком зберігання, а не зникають одразу. Збірок Free це в жодному разі не стосується: їхня мапа записується локально і ніколи не звертається до мережі.

Ваші графіки в дашборді продовжують працювати. Вимкнення зупиняє зберігання вашого вихідного коду. Воно не зупиняє запис того, що виміряла збірка. Покриття, роздування, складність і час збірки не містять коду, і вони й далі записуються та показуються на графіках для проєкту з вимкненим захопленням. Єдиний виняток — кількість слабких місць, бо її рахують усередині збереженої мапи. Обфускований результат не змінюється.

Збережена мапа є єдиним, що AfterPack зберігає постійно і що містить ваш вихідний код. Усе інше щодо хмарної збірки залишається в пам'яті на час одного запиту. Див. Конфіденційність і обробка даних.

Далі