HN रिलीज़: WebAssembly QuickJS सैंडबॉक्स में JavaScript चलाएँ
(github.com/sebastianwessel)@sebastianwessel/quickjsएक TypeScript पैकेज है जो QuickJS engine को WebAssembly में compile किए गए sandbox के भीतर JavaScript और TypeScript code को सुरक्षित रूप से चलाता है- इसका फोकस untrusted code को isolate करके चलाने पर है, और QuickJS WebAssembly sandbox के ज़रिए हल्का और तेज़ execution environment देता है
- इसमें basic Node.js module support, virtual file system mount, custom Node module mount,
fetchclient, test runner, और Chai-आधारितexpectशामिल हैं - उपयोग उदाहरण में
loadQuickJs()से QuickJS wasm को एक बार load करने के बादrunSandboxed()औरevalCode()से code चलाया जाता है, औरallowFetch,allowFs,envजैसे options दिए जाते हैं - TypeScript application में बाहरी JavaScript code चलाने की ज़रूरत रखने वाले developer इसे isolation, performance, और API simplicity को साथ लेकर चलने वाले execution environment के रूप में उपयोग कर सकते हैं
QuickJS WebAssembly सैंडबॉक्स पैकेज
@sebastianwessel/quickjsएक TypeScript पैकेज है जो JavaScript और TypeScript code को WebAssembly sandbox के भीतर चलाने की सुविधा देता है- यह QuickJS engine को WebAssembly में compile करके उपयोग करता है, और untrusted code को isolated environment में चलाने के लिए बनाया गया है
- यह पैकेज हल्के और तेज़ QuickJS engine का उपयोग करके code execution के लिए मज़बूत environment प्रदान करता है
- यह पूर्ण documentation, examples, और online playground प्रदान करता है
उपलब्ध सुविधाएँ
- सुरक्षित execution: untrusted JavaScript और TypeScript code को सुरक्षित और isolated environment में चलाता है
- बेसिक Node.js modules: सामान्य use case के लिए standard Node.js modules का basic support देता है
- फ़ाइल सिस्टम: virtual file system को mount किया जा सकता है
- Custom Node modules: user-defined Node modules को mount किया जा सकता है
- Network calls:
fetchclient देता है जिससे http(s) calls की जा सकती हैं - Test support: test runner और Chai-आधारित
expectशामिल है - Performance और integration: QuickJS engine की हल्केपन और दक्षता का लाभ उठाता है और मौजूदा TypeScript project में आसानी से integrate किया जा सकता है
- सरल API: sandbox के भीतर JavaScript और TypeScript code चलाने और manage करने के लिए user-friendly API देता है
बेसिक उपयोग प्रवाह
- पहले
@jitl/quickjs-ng-wasmfile-release-syncसेvariantimport किया जाता है, और@sebastianwessel/quickjsसेloadQuickJsऔरSandboxOptionsउपयोग किए जाते हैं loadQuickJs(variant)QuickJS wasm को load और initialize करता है, और चूँकि यह काम resource-intensive है, इसलिए जहाँ संभव हो इसे केवल एक बार करना recommended हैloadQuickJs()runSandboxedलौटाता है, जिसका बाद में sandbox execution के लिए उपयोग होता हैSandboxOptionsउदाहरण में ये options शामिल हैंallowFetch: true: code मेंfetchinject करता है और data request की अनुमति देता हैallowFs: true: virtual file system को mount करता है औरnode:fsmodule उपलब्ध कराता हैenv: sandbox code में उपयोग के लिए environment variable values पास करता है
execution उदाहरण में संभव व्यवहार
- sandbox के भीतर का code
pathसेjoinimport करता है औरjoin('src','dist')कॉल करके host system log मेंsrc/distआउटपुट करता है env.MY_ENV_VARपढ़कर host system log में"env var value"आउटपुट करता हैnew URL('https://example.com')से URL बनाता है औरfetch(url)कॉल करने के बादf.text()का result लौटाता है- code को
runSandboxed(async ({ evalCode }) => evalCode(code), options)के रूप में चलाया जाता है - result उदाहरण
{ ok: true, data: '<!doctype html>\n<html>\n[....]</html>\n' }के रूप में है
लाइसेंस और उपयुक्त उपयोग
- यह project MIT License के तहत उपलब्ध है
- TypeScript application के भीतर JavaScript code को सुरक्षित रूप से चलाना चाहने वाले developers के लिए यह उपयुक्त है
- QuickJS WebAssembly sandbox के ज़रिए यह performance और safety दोनों देने वाला execution environment प्रदान करता है
1 टिप्पणियां
Hacker News की राय
quickjs-emscripten runtime library के लेखक के रूप में, quickjs-emscripten के लिए बनाई गई ergonomic standard library style मुझे पसंद आई
उत्सुक हूं कि क्या इसे browser या bundler में चलाकर देखा गया है। variant name को string के रूप में लेकर
import(variantName)में dynamically पास करने का तरीका Webpack जैसे tools के साथ अच्छी तरह काम नहीं कर सकताSecurity warning: यह library guest के untrusted code को host
fetchfunction जैसी cookies के साथfetchcall करने देती है।fetchenable करके untrusted code नहीं चलाना चाहिएअगर sandbox के अंदर का code host context के रूप में authenticated arbitrary HTTP API call कर सकता है, तो वह “sandbox” नहीं है
quickjs-emscripten low-level है और magic जैसी features से बचता है, इसका कारण यह है कि हम confidence से कह सकें कि जो API हम देते हैं वे सुरक्षित हैं। magic जैसी serialization या आसान network/filesystem access features की requests हम आमतौर पर reject करते हैं, क्योंकि ऐसे code में security mistakes होना आसान होता है
untrusted code चलाते समय सिर्फ sandbox itself नहीं, बल्कि sandbox को expose किए गए API के लिए लिखे गए code का भी सावधानी से audit करना चाहिए
किसी दूसरे HN user का Fetch cookies के बारे में पूछा गया comment देखकर संभावित security issue का पता चला
आगे पढ़ने के लिए: Figma plugin sandbox security लेख https://www.figma.com/blog/how-we-built-the-figma-plugin-sys..., https://www.figma.com/blog/an-update-on-plugin-security/, Quickjs-emscripten README https://github.com/justjake/quickjs-emscripten
सर्वर साइड और ब्राउज़र साइड दोनों पर JavaScript को sandbox करने के कई तरीके हैं, लेकिन DOM access को “sandbox” करने का कोई तरीका है या नहीं, यह नहीं पता
उदाहरण के लिए, किसी अविश्वसनीय third-party को पहले से तय स्थान के DOM elements तक ही access देना। मेरी जानकारी में इसे allow करने वाली इकलौती तकनीक iframe है, लेकिन अफसोस कि यह भारी और धीमी है
मैं ऐसा app बना रहा हूँ जो plugins को host कर सके, लेकिन plugins को DOM access देने पर लगता है कि वे सचमुच कुछ भी कर सकते हैं
वह सुरक्षित evaluation function काफी कमाल का है। core शायद यहां है: https://github.com/Agoric/realms-shim/blob/v1.1.0/src/evalua...
platform level पर इस समस्या को हल करने के लिए isolated web components का idea भी है: https://github.com/WICG/webcomponents/issues/1002
jsmirrors से प्रेरित होकर आप खुद “dommirrors” जैसा कुछ try कर सकते हैं, लेकिन यह बड़ा काम है। jsmirrors को जैसा है वैसा इस्तेमाल करके आप जो चाहते हैं उसका कुछ हिस्सा हासिल करने का workaround भी है, लेकिन यह इस्तेमाल में सुविधाजनक नहीं है
हालांकि DOM access को mediate या simulate करके देना लगभग निश्चित रूप से वह नहीं है जो आप सच में चाहते हैं। बेहतर है कि सामने वाले को असल में क्या करने की अनुमति देनी है, यह समझें और फिर सिर्फ वही capability दें जो उस behavior को संभव बनाती है
उदाहरण के लिए, अगर आप उन्हें कहीं button जोड़ने देना चाहते हैं, तो लग सकता है कि parent element को anchor point की तरह देना और
document.createElementसे DOM node बनवाना चाहिए। लेकिन असल में आप उन्हेंdocument.createElementaccess नहीं देना चाहते, बल्कि add-button capability देना चाहते हैं। इसलिएaddButtonimplement कर देंऔर देखें: <https://news.ycombinator.com/item?id=30703531#30706060>
जो लोग कहते हैं कि CSP इसी काम के लिए है, उनकी बात न सुनना बेहतर है। ऐसा नहीं है। सटीक कहें तो CSP अपने मूल लक्ष्य वाले use case में भी खराब design और user-hostile कचरा है, और मुझे लगता है कि इसे आज जिस तरह implement और extend किया गया है वैसा नहीं होना चाहिए था। इस पर निर्भर रहना जोखिम भरा है
parent page DOM content का size मापकर iframe का size एक तय सीमा के भीतर adjust कर सकता है, इसलिए इसे app UI में ज्यादा natural तरीके से integrate किया जा सकता है
users के बीच exposure level और trust level के हिसाब से iframe के अंदर impersonation/phishing और clickjacking attempts से सावधान रहना चाहिए। Ideally frame को web requests बिल्कुल भी न कर पाने के लिए lock करना चाहिए, जिसका मतलब है कि image loading भी नहीं होगी
ऐसा करने पर, मान लें user को fake password form में password डालने के लिए धोखा भी दे दिया जाए, तो भी data को frame के बाहर leak करने का कोई तरीका नहीं होगा
iframe किन resource types को request कर सकता है, इसे सीमित करने का मुख्य तरीका Content-Security-Policy है, और इसके जरिए third-party images, scripts आदि सब बंद किए जा सकते हैं
https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameE...
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
दूसरे sandbox attributes भी on करें, और webcam जैसे privacy-sensitive DOM API access बंद करने चाहिए
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/if...
https://developer.mozilla.org/en-US/docs/Web/Security/IFrame...
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pe...
इससे जिन HTML elements और attributes को allow करना है, उन्हें whitelist तक सीमित किया जा सकता है। native DOM event receiving तक allow करना हो तो जटिल हो जाएगा, लेकिन असंभव नहीं है
event name string और event receive करने वाले element id को लेने वाली API जैसी चीज चाहिए होगी, फिर actual DOM में वह event detect करके उसी event को उपयोग हो रहे JavaScript sandbox के अंदर replicate करना होगा। उस sandbox के अंदर ऊपर बताई API तक access होना चाहिए
backend पर HTML को dynamically modify करके ऐसा
divtag डालें जिसका id value hash sequence हो। साथ ही HTML को modify करके अपने domain से third-party code request करने वालाscripttag डालें, और tracking के लिए उसscripttag के data attribute में hash value जोड़ देंबैकएंड पर third-party code को बदलकर
document.औरwindow.के सभी instances कोdocument.getElementById(hash_value).से replace करें, और सभी query selectors को#hash_valueसे शुरू करवाएँElementprototype के.parentNodeको custom property से replace करके, दिए गए container से बाहर निकलने की कोशिशों की जांच और उन्हें block करना होगाफिर HTML document को browser पर भेजें। third-party code टूट जाए तो भी ठीक है। constraints third party को बतानी होंगी, और server पर भेजने से पहले अपने code की testing उन्हें खुद करनी होगी
अहम बात सिर्फ यह है कि code dynamically दिए गए container से बाहर न जा सके। security violations खोजने के लिए इस हिस्से को नियमित रूप से test करना होगा
हो सकता है असल में काम न करे, लेकिन experiment करके देखना दिलचस्प होगा
संयोग से पिछले हफ्ते QuickJS आज़माया, और आखिर में isolated-vm पर आकर रुका। दोनों ने सुरक्षा संबंधी ज़रूरतें पूरी कीं, लेकिन setup, teardown और
evalचलाने के overhead के मामले में isolated-vm का performance कहीं बेहतर थादिलचस्प approach है। worker के जरिए isolation और JavaScript environment cleanup तकनीक इस्तेमाल करने वाली एक दूसरी JavaScript sandbox library[1] के लेखक के तौर पर, मुझे लगता है कि JavaScript को interpret करने का तरीका—यानी JavaScript के अंदर JavaScript, या इस मामले में WASM के अंदर JavaScript—सबसे ऊंचे स्तर का isolation देता है
साथ ही यह host JavaScript virtual machine के bugs के सीधे exposure से भी बचाता है। अगर target Node है तो यह और अहम हो सकता है, क्योंकि कुछ नए trends को छोड़ दें तो Node.js, Deno के उलट, isolation और sandboxing को ध्यान में रखकर design किया गया नहीं लगता
API देखने पर यह साफ नहीं दिखता कि
createRuntime,fetchके अलावा host environment calls define कर सकता है या नहीं। यह feature काफी उपयोगी होगा, क्योंकि इससे बाहरी दुनिया से communication को सब-कुछ allow या सब-कुछ block करने के बजाय नियंत्रित तरीके से सीमित किया जा सकता हैइसी तरह, browser support भी दिखता नहीं है। कम-से-कम esm.sh से जल्दी जांचने पर तो ऐसा ही लगा। यह भी उपयोगी feature हो सकता है
overhead कितना है, यह जानने के लिए test करने वाला हूं, और जैसा ऊपर कहा, approach अपने-आप में काफी मजबूत लगती है
[1] @exact-realty/lot
host functions expose करने, guest functions call करने, custom module loader आदि के लिए API भी हैं: https://github.com/justjake/quickjs-emscripten?tab=readme-ov...
newFunctionAPI docs: https://github.com/justjake/quickjs-emscripten/blob/main/doc...CPU अब इतने तेज हो गए हैं कि बात कुछ ऐसी लग रही है: चलो अब interpreter के अंदर interpreter चलाते हैं
QuickJS में JavaScript चलाने का फायदा मैं performance को नहीं कहूंगा। QuickJS, host JavaScript virtual machine से बिल्कुल compete नहीं करता
हालांकि यह पुराने C interpreters या JavaScript में implement किए गए interpreters से तेज हो सकता है
इससे user-provided JavaScript code चलाया जा सकेगा। मैं user के TypeScript code को sandbox environment में bundler से bundle करने का तरीका ढूंढ रहा था
QuickJS के अंदर webpack जैसे bundler चलाने के तरीके पर कोई recommendation है?
[1]: https://webcontainers.io/
बहुत शानदार। अगर यह WASM में compile हुआ है, तो सोच रहा हूं कि क्या इसे browser में run किया जा सकता है। अगर हो सके, और
fetchrequests बिना cookies attach किए की जा सकें, तो यह दिलचस्प होगापिछली नौकरी में Quickjs-emscripten में इतने ज्यादा segmentation fault और silent errors आए कि project hold पर चला गया
अगर फिर से करना पड़े, तो शायद ऐसा engine चुनूंगा जो ज्यादा programs के साथ ठीक से चलता हो और जिसकी officially approved WASM bundle हो
मुझे लगता था कि iframe से code को safely sandbox किया जा सकता है, लेकिन पक्का नहीं हूं। बेशक, अपना interpreter इस्तेमाल करने पर ज्यादा granular time limits, custom APIs जैसी चीजें और भी जोड़ी जा सकती हैं
अभी मैं ऐसा iframe इस्तेमाल कर रहा हूं जो window message से code लेता है, input code evaluate करता है और window message से response वापस भेज सकता है, इसलिए यह काफी ठीक काम करता है। हालांकि sandbox से बाहर निकलने का कोई loophole है या नहीं, इस पर भरोसा नहीं है
ज्यादा complex cases में, जैसे package dependencies, मैंने Babel से parse करके CDN(esm.sh) का इस्तेमाल करने वाला import map बनाने की कोशिश की थी, लेकिन CDN वाला version कभी-कभी सही से काम नहीं करता था
इसलिए मैंने StackBlitz इस्तेमाल किया, और वह काफी ठीक काम करता है, लेकिन non-secure context वाले environments में कुछ problems हैं
आखिर में मैंने एक छोटा web server बनाया जो code और dependencies(package.json) लेता है, Docker container के अंदर Vite build चलाता है और result वापस करता है। यह अपने हिसाब से ठीक काम करता है, लेकिन कभी-कभी slow है
पूरी तरह client-side build कर पाना अच्छा होगा, और StackBlitz मोटे तौर पर यही काम कर रहा है