1 puan yazan GN⁺ 2024-07-08 | 1 yorum | WhatsApp'ta paylaş
  • @sebastianwessel/quickjs, QuickJS motorunu WebAssembly’ye derlenmiş bir sandbox içinde JavaScript ve TypeScript kodunu güvenle çalıştıran bir TypeScript paketidir
  • Güvenilmeyen kodu izole ederek çalıştırmaya odaklanır ve QuickJS WebAssembly sandbox’ı üzerinden hafif ve hızlı bir çalışma ortamı sunar
  • Temel Node.js modülleri desteği, sanal dosya sistemi bağlama, özel Node modülleri bağlama, fetch istemcisi sağlama, test runner ve Chai tabanlı expect içerir
  • Kullanım örneğinde loadQuickJs() ile QuickJS wasm bir kez yüklendikten sonra runSandboxed() ve evalCode() ile kod çalıştırılır; allowFetch, allowFs, env gibi seçenekler aktarılır
  • TypeScript uygulamalarında harici JavaScript kodu çalıştırması gereken geliştiriciler, izolasyon, performans ve API sadeliğini birlikte dikkate alan bir çalışma ortamı olarak kullanabilir

QuickJS WebAssembly Sandbox Paketi

  • @sebastianwessel/quickjs, JavaScript ve TypeScript kodunu WebAssembly sandbox’ı içinde çalıştırmayı sağlayan bir TypeScript paketidir
  • QuickJS motorunu WebAssembly’ye derleyerek kullanır ve güvenilmeyen kodu izole bir ortamda çalıştırma amacına uygundur
  • Paket, hafif ve hızlı QuickJS motorundan yararlanarak kod çalıştırma için sağlam bir ortam sunar
  • Tam dokümantasyon, örnekler ve çevrimiçi playground sunar

Sunulan Özellikler

  • Güvenli çalıştırma: Güvenilmeyen JavaScript ve TypeScript kodunu güvenli ve izole bir ortamda çalıştırır
  • Temel Node.js modülleri: Yaygın kullanım senaryoları için temel standart Node.js modülleri desteği sağlar
  • Dosya sistemi: Sanal dosya sistemi bağlanabilir
  • Özel Node modülleri: Kullanıcı tanımlı Node modülleri bağlanabilir
  • Ağ çağrıları: http(s) çağrıları yapabilmek için fetch istemcisi sağlar
  • Test desteği: Test runner ve Chai tabanlı expect içerir
  • Performans ve entegrasyon: QuickJS motorunun hafifliğinden ve verimliliğinden yararlanır; mevcut TypeScript projelerine kolayca entegre edilebilir
  • Sade API: Sandbox içinde JavaScript ve TypeScript kodunu çalıştırmak ve yönetmek için kullanıcı dostu bir API sağlar

Temel Kullanım Akışı

  • Önce @jitl/quickjs-ng-wasmfile-release-sync içinden variant alınır; @sebastianwessel/quickjs içinden loadQuickJs ve SandboxOptions kullanılır
  • loadQuickJs(variant), QuickJS wasm’ini yükleyip başlatır; bu işlem kaynak yoğun olduğu için mümkünse yalnızca bir kez yapılması önerilir
  • loadQuickJs(), runSandboxed döndürür ve bu daha sonra sandbox çalıştırmaları için kullanılır
  • SandboxOptions örneği şu seçenekleri içerir
    • allowFetch: true: Koda fetch enjekte eder ve veri isteklerine izin verir
    • allowFs: true: Sanal dosya sistemini bağlar ve node:fs modülünü sağlar
    • env: Sandbox kodunda kullanılacak ortam değişkeni değerlerini aktarır

Çalıştırma Örneğinde Mümkün Olan Davranışlar

  • Sandbox içindeki kod, path içinden join import eder ve join('src','dist') çağrısıyla host sistem günlüğüne src/dist çıktısını yazar
  • env.MY_ENV_VAR değerini okuyup host sistem günlüğüne "env var value" çıktısını yazar
  • new URL('https://example.com') ile URL oluşturur, fetch(url) çağırır ve ardından f.text() sonucunu döndürür
  • Kod, runSandboxed(async ({ evalCode }) => evalCode(code), options) biçiminde çalıştırılır
  • Sonuç örneği { ok: true, data: '<!doctype html>\n<html>\n[....]</html>\n' } biçimindedir

Lisans ve Uygun Kullanım Alanları

  • Bu proje MIT License ile sunulur
  • TypeScript uygulaması içinde JavaScript kodunu güvenle çalıştırmak isteyen geliştiriciler için uygundur
  • QuickJS WebAssembly sandbox’ı üzerinden performans ve güvenliği birlikte sağlayan bir çalışma ortamı sunar

1 yorum

 
GN⁺ 2024-07-08
Hacker News yorumları
  • quickjs-emscripten çalışma zamanı kütüphanesinin yazarı olarak, quickjs-emscripten için yaptığınız ergonomik standart kütüphane tarzını beğendim
    Tarayıcıda ya da bir bundler ile çalıştırmayı denediniz mi merak ediyorum. Varyant adını string olarak alıp dinamik biçimde import(variantName) içine verme yöntemi, Webpack gibi araçlarla pek iyi uyum sağlamayabilir
    Güvenlik uyarısı: Bu kütüphane, konuk tarafındaki güvenilmeyen kodun, host fetch fonksiyonuyla aynı çerezleri kullanarak fetch çağırmasına olanak tanıyor. fetchi etkinleştirip güvenilmeyen kod çalıştırmamalısınız
    Sandbox içindeki kod, host bağlamında kimliği doğrulanmış keyfi HTTP API çağrıları yapabiliyorsa, bu bir “sandbox” değildir
    quickjs-emscripten’in düşük seviyeli olup sihirli özelliklerden kaçınmasının nedeni, sunduğu API’nin güvenli olduğunu rahatlıkla söyleyebilmek. Sihirli serileştirme ya da kolay ağ/dosya sistemi erişimi gibi özellik isteklerini çoğunlukla geri çeviriyorum; çünkü bu tür kodlar güvenlik hatalarına çok açık alanlar
    Güvenilmeyen kod çalıştırırken yalnızca sandbox’ın kendisini değil, sandbox’a açtığınız API için yazdığınız kodu da dikkatle denetlemelisiniz
    Başka bir HN kullanıcısının Fetch çerezleriyle ilgili sorduğu yorumu görünce olası güvenlik sorununu fark ettim
    Daha fazla okuma: Figma eklenti sandbox güvenliği yazıları 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

    • iframe kullanınca, bu kütüphanenin kullanılıp kullanılmamasından bağımsız olarak fetch çerezleri sorununu engellemenin mümkün olup olmadığını merak ediyorum. Yoksa iframe içinde de hâlâ sorun olup olmadığını bilmek isterim
  • Hem sunucu tarafında hem de tarayıcı tarafında JavaScript’i sandbox’a almak için birçok yöntem var, ama DOM erişimini “sandbox’a almanın” bir yolu var mı bilmiyorum
    Örneğin güvenilmeyen bir üçüncü tarafa yalnızca önceden belirlenmiş bir konumdaki DOM öğelerine erişim vermek gibi. Bildiğim kadarıyla buna izin veren tek teknoloji iframe; ne yazık ki ağır ve yavaş
    Eklenti barındırabilen bir uygulama geliştiriyorum; eklentilere DOM erişimi verirsem kelimenin tam anlamıyla her şeyi yapabilir hale geliyorlar gibi görünüyor

    • Salesforce bunu web component’ler ile yamalanmış bir ShadowRoot’u birleştirerek çözüyor. Shadow root referansına sahip kodun belgenin geri kalanına çıkıp gitmesini engelliyor ve SES (Secure EcmaScript) ile ilişkili güvenli değerlendirme fonksiyonlarıyla güvenilmeyen script’lerin erişebileceği global nesneleri kısıtlıyor
      O güvenli değerlendirme fonksiyonu epey etkileyici. Çekirdeği muhtemelen burası: https://github.com/Agoric/realms-shim/blob/v1.1.0/src/evalua...
      Bu sorunu platform düzeyinde çözmeye yönelik izole edilmiş web component’ler fikri de var: https://github.com/WICG/webcomponents/issues/1002
    • En yakın şey Allen Wirfs-Brock’un jsmirrors prototipi; ancak bildiğim kadarıyla DOM için spesifikasyon aşamasına gelmedi ve zaten pek de böyle bir niyeti yoktu. Yalnızca JavaScript programlama sistemi için capability (yetenek/izin) konusunu ele alıyordu
      jsmirrors’tan esinlenerek “dommirrors” gibi bir şeyi kendiniz denemek mümkün, ama büyük iş. jsmirrors’u olduğu gibi kullanıp istediğiniz şeye bir ölçüde ulaşan geçici çözümler de var, fakat kullanımı rahat değil
      Ancak DOM erişimini aracılık ederek ya da simüle ederek vermek neredeyse kesinlikle gerçekten istediğiniz şey değil. Karşı tarafın gerçekten ne yapmasına izin vermek istediğinizi belirleyip yalnızca o davranışı mümkün kılan yetkiyi vermek daha doğru
      Örneğin bir yere düğme ekletmek istiyorsanız, bir üst öğeyi ankraj noktası olarak verip document.createElement ile DOM node’u oluşturmalarını sağlamanız gerektiğini düşünebilirsiniz. Ama aslında istediğiniz şey onlara document.createElement erişimi vermek değil, add-button yetkisi vermek. Bu yüzden addButton implement etmelisiniz
      Devamı: <https://news.ycombinator.com/item?id=30703531#30706060>
      CSP’nin bu iş için olduğunu söyleyenleri dinlememek iyi olur. Değil. Daha doğrusu CSP, asıl hedeflediği kullanım için bile kötü tasarlanmış ve kullanıcıya düşmanca bir çöp; bugünkü gibi implemente edilmemeli ve genişletilmemeliydi diye düşünüyorum. Buna bel bağlamak tehlikeli
    • Gerçekten güvenli erişim istiyorsanız tek yöntem üçüncü taraf koda başka bir domain’den iframe vermek ve sandbox özniteliğini ayarlamak
      Üst sayfada DOM içeriğinin boyutunu ölçüp iframe boyutunu belirli sınırlar içinde ayarlayabilirsiniz; böylece uygulama arayüzüne daha doğal şekilde entegre edilebilir
      Kullanıcılar arası görünürlük seviyesi ve güven düzeyine bağlı olarak iframe içindeki kimliğe bürünme/phishing ve clickjacking girişimlerine dikkat etmek gerekir. İdeal olarak frame’in web isteği yapması tamamen engellenmeli; bu, görüntü yüklemenin de olmayacağı anlamına gelir
      Böylece örneğin kullanıcı sahte bir parola formuna parolasını girmesi için kandırılsa bile veriyi frame dışına sızdırmanın bir yolu olmaz
      iframe’in isteyebileceği kaynak türlerini sınırlamanın başlıca yolu Content-Security-Policy’dir; bununla üçüncü taraf görselleri, script’leri vb. tamamen kapatabilirsiniz
      https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameE...
      https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
      Diğer sandbox özniteliklerini de açmalı, web kamerası gibi gizlilik açısından hassas DOM API’lerine erişimi kapatmalısınız
      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...
    • Olası yöntem, üçüncü tarafın render etmek istediği HTML’i string olarak gönderen bir API ekleyip bunu çeşitli kütüphanelerden biriyle parse etmek ve parse edilen veriye dayanarak DOM’u yeniden oluşturmak gibi görünüyor
      Böylece izin verilecek HTML öğelerini ve özniteliklerini whitelist ile sınırlayabilirsiniz. Native DOM event’lerini dinlemeye de izin vermek karmaşıklaşır, ama imkânsız değil
      Event adı string’ini ve event’i alacak öğe id’sini alan bir API gibi bir şeye ihtiyaç olur; gerçek DOM’da ilgili event’i algılayıp aynı event’i kullanılan JavaScript sandbox’ının içine kopyalamak gerekir. O sandbox içinde de az önce bahsedilen API’ye erişim olmalı
    • Aklıma gelen hızlı bir yaklaşım şöyle olabilir: backend’de üçüncü taraf kodu isteyip o kodu bir hash dizisiyle ilişkilendirmek
      Backend’de HTML’i dinamik olarak değiştirip id değeri hash dizisi olan bir div etiketi eklemek. Ayrıca HTML’i değiştirip kendi domain’inizden üçüncü taraf kodu isteyen bir script etiketi koymak ve takip için o script etiketinin data özniteliğine hash değerini eklemek

Arka uçta üçüncü taraf kodunu değiştirip document. ve window. geçen tüm yerleri document.getElementById(hash_value). ile değiştirmek ve tüm sorgu seçicilerin #hash_value ile başlamasını sağlamak
Element prototipindeki .parentNode yerine özel bir özellik koyup, sağlanan kapsayıcının dışına çıkma girişimlerini denetleyip engellemek gerekir
Ardından HTML belgesini tarayıcıya gönderirsiniz. Üçüncü taraf kodu bozulursa sorun değil. Kısıtlamalar üçüncü taraflara bildirilmeli ve sunucuya göndermeden önce kendi kodlarını test etmeleri onların sorumluluğunda olmalı
Önemli olan tek şey, kodun dinamik olarak sağlanan kapsayıcının dışına çıkamamasını sağlamak. Güvenlik ihlallerini bulmak için bu bölüm düzenli olarak test edilmeli
Gerçekte çalışmayabilir ama denemesi eğlenceli olur gibi

  • Geçen hafta tesadüfen QuickJS’i denedim ve sonunda isolated-vm’de karar kıldım. İkisi de güvenlik sözleşmesini karşılıyordu, ancak kurulum, kapatma ve eval çalıştırma ek yükü açısından isolated-vm çok daha performanslıydı

  • İlginç bir yaklaşım. Worker ile izole eden ve JavaScript ortamı temizleme teknikleri kullanan başka bir JavaScript sandbox kütüphanesinin[1] yazarı olarak, JavaScript’i yorumlama yönteminin — yani JavaScript içinde JavaScript’in ya da bu örnekte olduğu gibi WASM içinde JavaScript’in — en yüksek izolasyon düzeyini sağladığını düşünüyorum
    Ayrıca doğrudan host JavaScript sanal makinesinin kendi hatalarına maruz kalmaz. Node’u hedefliyorsanız bu daha da önemli olabilir; çünkü bazı yeni akımlar hariç Node.js, Deno’nun aksine izolasyon ve sandboxing düşünülerek tasarlanmış gibi görünmüyor
    API’ye bakınca createRuntime’ın fetch dışında host ortam çağrıları tanımlamaya izin verip vermediği görünmüyor. Bu özellik epey faydalı olurdu; çünkü dış dünyayla iletişimi ya tamamen açmak ya da tamamen kapatmak yerine kontrollü biçimde sınırlamayı mümkün kılar
    Benzer şekilde tarayıcıları da desteklemiyor gibi görünüyor. En azından esm.sh ile hızlıca kontrol ettiğimde durum buydu. Bu da yararlı bir özellik olabilir
    Ek yükün ne düzeyde olduğunu merak ettiğim için test edeceğim; yukarıda söylediğim gibi yaklaşımın kendisi oldukça sağlam görünüyor
    [1] @exact-realty/lot

  • CPU’lar çok hızlandığına göre artık yorumlayıcı içinde yorumlayıcı çalıştıralım deniyor gibi görünüyor

  • QuickJS’te JavaScript çalıştırmanın avantajı olarak performansı göstermezdim. QuickJS, host JavaScript sanal makinesiyle hiç rekabet edemez
    Ancak eski C yorumlayıcılarından veya JavaScript ile yazılmış yorumlayıcılardan daha hızlı olabilir

    • Betik çalışma süresinden ziyade Node.js sürecinin başlama süresi baskınsa performans kazancı olabilir. Muhtemelen birçok serverless function tarzı betik bu kategoriye giriyordur
  • Bununla kullanıcı tarafından sağlanan JavaScript kodunu çalıştırmak mümkün olabilir. Kullanıcının TypeScript kodunu sandbox ortamında bundle etmenin bir yolunu arıyordum
    QuickJS içinde webpack gibi bir bundler çalıştırmanın yolu konusunda önerisi olan var mı merak ediyorum

    • QJS ile nasıl yapılır bilmiyorum ama tarayıcıda bundler çalıştırmak istiyorsanız WebContainers tam da bunun için yapılmış gibi görünüyor
      [1]: https://webcontainers.io/
  • Çok havalı. WASM’e derlendiyse tarayıcıda çalıştırmanın mümkün olup olmadığını merak ediyorum. Mümkünse ve fetch isteklerini isteklere cookie eklemeden yapabiliyorsa ilginç olurdu

  • Önceki işimde Quickjs-emscripten’de çok fazla segmentation fault ve sessiz hata gördüğümüz için proje askıya alınmıştı
    Yeniden yapacak olsam, daha fazla programda düzgün çalışan ve resmen onaylanmış bir WASM bundle’ı olan bir motor kullanırdım

  • Kodun iframe ile güvenli biçimde sandbox’a alınabileceğini düşünüyordum ama emin değilim. Elbette kendi yorumlayıcınızı kullanırsanız daha sıkı zaman limitleri, özel API’ler gibi daha fazla özellik ekleyebilirsiniz

    • Ben de özellikle service worker işin içine girince bu konuyu daha çok bilmek isterim
      Şu anda kodu window message ile alan bir iframe kullanıyorum; giriş kodunu değerlendirip yanıtı window message ile geri gönderebildiği için oldukça iyi çalışıyor. Ancak sandbox dışına kaçış için bir açık olup olmadığından emin değilim
      Daha karmaşık durumlarda, örneğin paket bağımlılıklarını Babel ile parse edip sonra CDN (esm.sh) kullanan bir import map oluşturmayı denedim, fakat CDN’lenmiş sürümün iyi çalışmadığı durumlar oldu
      Bu yüzden StackBlitz kullandım ve oldukça iyi çalışıyor, ancak güvenli bağlam olmayan ortamlarda bazı sorunlar var
      Sonunda kodu ve bağımlılıkları (package.json) alıp Docker container içinde Vite build’i yapan ve sonucu döndüren küçük bir web sunucusu yaptım. Fena çalışmıyor ama bazen yavaş
      Tamamen istemci tarafında build edebilmek güzel olurdu; StackBlitz aşağı yukarı bunu yapıyor
    • iframe’e fazla geniş yetkiler verilmiş durumda. Örneğin iframe verileri üçüncü taraflara sızdırabilir