AfterPack JavaScript obfuscator कैसे काम करता है

Standard (Free) output आपके code के साथ क्या करता है, यह reversal का प्रतिरोध कैसे करता है, और एक attacker वास्तव में क्या recover कर सकता है।

AfterPack एक post-build JavaScript protector है। यह आपके toolchain द्वारा बनाया गया bundle लेता है और उसे ऐसी चीज़ में फिर से लिखता है जिसे कोई इंसान, या कोई AI agent, असमान रूप से बड़े प्रयास के बिना न पढ़ सकता है, न search कर सकता है, न बदल सकता है। यह एक पंक्ति में आपकी मौजूदा build से जुड़ जाता है, या बिना किसी plugin के output directory पर चलता है।

निश्चित obfuscation layers क्यों विफल होती हैं

ज़्यादातर obfuscators दो ऐसी चीज़ें ship करते हैं जिनका attacker फ़ायदा उठा सकता है: एक के बाद एक लागू की गई layers का ढेर, और पहले से तैयार संरचनाएँ जिन्हें कोई tool देखते ही पहचान लेता है (एक string array, चाहे encrypted ही क्यों न हो, एक custom VM interpreter, एक पहचाना जा सकने वाला decoder bootstrap)। Reversal का तरीका यांत्रिक है: किसी layer या संरचना को उसके signature से पहचानें, उसे हटाएँ, और तब तक दोहराएँ जब तक मूल code बाहर न आ जाए। webcrack और restringer ठीक यही करते हैं, microseconds में।

AfterPack न layers का ढेर ship करता है, न कोई बना-बनाया decoder। हर build की बनावट अलग होती है, इसलिए एक build के patterns के लिए लिखे गए tools अगली build पर टूट जाते हैं। जो आपका code चलाता है, वह अब भी उन values को पढ़ सकता है जिनका code उपयोग करता है। उन tools से तुलना का अपना अलग पेज है।

AI agents ने ख़तरा बदल दिया

एक frontier model minified code का केवल सार नहीं बताता: Claude Code जैसे agents अब पारंपरिक obfuscators के output को deobfuscate कर देते हैं। एक सार्वजनिक प्रयोग में, एक agent ने javascript-obfuscator और एक commercial tool (JScrambler) दोनों को लगभग दस मिनट में पढ़ने योग्य logic तक वापस उतार दिया (यह run हमारे blog पर लिखा गया है)।

Renaming, एक string array और कुछ layers अब सुरक्षा नहीं देते: ये ठीक वही patterns हैं जिनमें ये tools और models सबसे अच्छे हैं। Threat model क्रम से बताता है कि कौन-सा adversary किस technique को हराता है। Pattern tools के ख़िलाफ़ जो अब भी काम करता है, वह है हर build में नई बनावट: एक build के लिए लिखा गया recognizer अगली build के लिए दोबारा लिखना पड़ता है।

Standard obfuscation पहले से क्या करता है

AfterPack आपके bundler के बाद, built output पर चलता है, और कभी आपके source को नहीं छूता, चाहे framework plugin के ज़रिए हो या post-build CLI के ज़रिए। Quickstart इसका पाँच मिनट वाला रूप है।

AfterPack in your build pipelineYour source.ts / .tsx / .jsBundlerNext / Vite / …AfterPackpost-build sealPolymorphic bundledifferent every releaseCDNship itrandom seed per release → a structurally different artifact every time

Free यह सब लागू करता है:

  • यह आपके string literals को encode करता है। किसी भी positive complexity target पर string floor चालू रहता है, इसलिए string literal artifact में text के रूप में पड़े रहने के बजाय एक seed द्वारा चुने गए runtime decoder में फिर से लिखा जाता है। यह डिफ़ॉल्ट रूप से चालू है, और minified code से यही सबसे बड़ा अंतर है। Directive prologues, typeof के comparands, वे property names जिनकी runtime को ज़रूरत है, और export names पढ़ने योग्य रहते हैं, और भारी रूप से minified third-party bundles के भीतर कुछ source string values बची रह सकती हैं; build बताती है कि कितनी बचीं और Protection Map उन्हें सूचीबद्ध करता है।
  • यह आपके logic को ऐसे समतुल्य रूपों में फिर से लिखता है जिन्हें आम tools वापस नहीं समेट पाते: mixed-boolean arithmetic, opaque predicates, एक integer-expression bytecode VM। एक static reader असली मेहनत से अब भी आपके मूल program तक वापस पहुँच सकता है, और एक build की बनावट के लिए लिखा गया tool अगली build पर टूट जाता है। transforms.<kind>.enabled अलग-अलग families को चालू/बंद करता है।
  • यह control और संरचना को नया आकार देता है: control-flow flattening, dead branches, scope deepening, cross-dependency entanglement।
  • यह हर build में सब कुछ बदल देता है, प्रति-build polymorphism के ज़रिए (नीचे देखें)।

Zero-config default light है: string floor और rename किए गए identifiers, बिना किसी संरचनात्मक inflation के। Preset को medium या उससे ऊपर बढ़ाने से ऊपर बताया गया संरचनात्मक काम जुड़ता है; string की पठनीयता preset चाहे जो हो, पहले से अपने floor पर है। यह सब हर tier पर चलता है, Free समेत; Free और Pro की सीमा Tiers पर है।

हर build में एक नया program

AfterPack अपना output एक प्रति-build seed से निकालता है जो डिफ़ॉल्ट रूप से random होता है। एक ही source की दो builds संरचना में अलग निकलती हैं: अलग names, अलग encodings, हर जगह अलग बनावट।

medium और ऊपर, यह किसी दिखावटी फेरबदल से आगे जाता है। AfterPack केवल program के text को नहीं, program की संरचना को ही बदलता है: control flow को flatten करके फिर से क्रमबद्ध किया जाता है, values को store करने के बजाय हर इस्तेमाल पर फिर से गणना की जाती है, और bundle के जो हिस्से स्वतंत्र थे, वे एक-दूसरे से उलझे हुए निकलते हैं। जो ship होता है, वह आपके लिखे code का functional समतुल्य है, एक अलग program जो वैसा ही व्यवहार करता है, और seed तय करता है कि इस बार आपको कौन-सा समतुल्य मिलेगा।

इसलिए एक build के patterns के लिए लिखे गए tools अगली build पर टूट जाते हैं, और हर release में patch की जगह फिर से ढूँढनी पड़ती है। जो code चलाता है, वह अब भी उसकी values पढ़ सकता है। बार-बार ship करने से यह लागत हर बार दोहराई जाती है। Builds और releases बताता है कि इसे आदत कैसे बनाएँ।

Reproducible output के लिए, cache hits या audits के लिए, seed को CLI पर --seed=git से, या plugin में seed: "git" से pin करें। दोनों इसे आपके commit से निकालते हैं। देखें Builds और releases।

एक attacker वास्तव में क्या recover कर सकता है

हर transform reversal की लागत एक ही तरह से नहीं बढ़ाता। AfterPack अपनी सुरक्षा को reversal classes में बाँटता है; यह जानना कि किसी region को कौन-सी class कवर करती है, आपको बताता है कि attacker के हाथ क्या लगता है।

ये static-attacker classes हैं: ये बताती हैं कि bytes पढ़ने वाले व्यक्ति के सामने क्या आता है, चाहे grep हो, AST tooling हो, या कोई LLM जो code को चलाए बिना समझाता है। जो attacker artifact को एक instrumented runtime के तहत चलाता है, वह एक अलग, ज़्यादा ताक़तवर मामला है, जिसे नीचे Scope में और threat model पेज पर कवर किया गया है।

किसी भी preset पर build पूरे bundle में renamed & encoded class में ही रहती है; भारी preset उसी काम को ज़्यादा करता है, किसी दूसरी class में नहीं ले जाता। Protection Map हर token को उसकी complexity के आधार पर रंगता है और आपके चुने हुए code की class बताता है।

Region overrides, preset=hard और preset=extreme, किसी region का complexity target बढ़ाते हैं और उसका string floor ज़बरदस्ती चालू करते हैं, जिससे वही काम वहाँ केंद्रित होता है जहाँ आप कहते हैं।

Secrets कैसे बचते हैं

किसी secret की सुरक्षा आपके code को नया आकार देने से अलग mechanism इस्तेमाल करती है। एक secret (API key, license seed, threshold) हर उस program में मौजूद होता है जो वैसा ही व्यवहार करता है, इसलिए उसके आसपास के code को नया आकार देना उसे छुपाता नहीं।

Static या blind-LLM पाठक से secret को जो छुपाता है, वह है non-materialization: value output में कभी दिखाई ही नहीं देती। transforms.comparisonHardening.enabled यह उस literal के लिए करता है जिससे आपका code तुलना करता है, तुलना को एक one-way digest check में फिर से लिखकर, ताकि केवल digest ship हो। किसी secret literal पर preset=extreme recovery को महँगा बनाता है, और literal फिर भी output में मौजूद रहता है।

जो attacker आपके code को एक instrumented runtime के तहत चलाता है, वह हर उस secret तक पहुँच सकता है जिसे code खुद इस्तेमाल करता है। कोई भी build-time obfuscator इसे नहीं बदलता: अगर चलते program को value की ज़रूरत है, तो एक दृढ़ dynamic attacker उसे देख सकता है। Non-materialization static और blind-LLM पाठक को हराता है और हर rotation पर दोबारा निकालने की लागत बढ़ाता है। यह client-side secret को unrecoverable नहीं बनाता। ऊँचे मूल्य वाले secrets server-side होने चाहिए, जहाँ client उन्हें कभी नहीं रखता।

Scope

/* @afterpack preset=hard */ region में रखा license check, जो बदलते seed के साथ बार-बार ship होता है, अगली release आने से पहले crack करने के लिए engineering घंटों के लायक नहीं है। ज़्यादातर products के लिए यही सही पैमाना है।

यह बचाव आर्थिक है। असीमित समय मिलने पर, कोई भी client-side code कुछ हद तक reverse किया जा सकता है। लक्ष्य लागत को मूल्य से ऊपर धकेलना है, और उस लागत को हर release पर दोहराना है। जब कोई asset किसी भी reversal प्रयास से ज़्यादा मूल्यवान हो, तो उसे server-side रखें जहाँ client उसे कभी नहीं रखता। AfterPack उस architecture को मज़बूत करता है; वह उसकी जगह नहीं लेता। Best-practices guide बताती है कि सुरक्षा वहाँ कैसे लगाएँ जहाँ वह मायने रखती है।

आगे

  • Reversal classes: तीन-class वाली table विस्तार से, प्रति transform।
  • Threat model: A0 से A5 तक adversary की सीढ़ी और static-बनाम-dynamic axis।
  • Protection Map: प्रति-token view जो साबित करता है कि किस पंक्ति पर कौन-सी class लगी।
  • Best practices: क्या चिह्नित करें, क्या अछूता छोड़ें, और builds को कैसे rotate करें।
  • Tiers: पूरी Free / Pro / Team / Enterprise सीमा।
  • Quickstart: plugin install करें और मिनटों में protected output ship करें।
  • Frameworks: आपके stack का plugin, या जब कोई plugin न हो तो CLI वाला रास्ता।
  • Configuration: ऊपर नामित हर key, और कौन-सी surface उसे सेट कर सकती है।
  • Presets: minify/light/medium/hard/extreme की सीढ़ी।
  • Directives: preset=hard और skip के पीछे की marker grammar।