WebAssembly Güvenlik Modeli: Sandbox ve İzolasyon Nasıl Çalışır?

Bir web uygulaması yöneticisi olarak gece 2’de telefon almak istemezsiniz. Ama “production’daki WASM modülü sistemi ele geçirdi” diye bir çağrı alırsanız, o an WebAssembly güvenlik modelini ne kadar az anladığınızı fark edersiniz. İşte tam bu yüzden bu konuyu derinlemesine ele almak gerekiyor.

WebAssembly, tarayıcıda yüksek performanslı kod çalıştırmak için tasarlandı. Ama artık sunucu tarafında, edge node’larda ve hatta IoT cihazlarında da karşımıza çıkıyor. Bu kadar geniş bir alanda çalışan bir teknolojinin güvenlik modeli, sysadmin olarak sizin için kritik bir konu haline geliyor.

WebAssembly Sandbox Nedir, Neden Önemlidir

WebAssembly’nin temel güvenlik garantisi şu: Bir WASM modülü, çalıştığı ortamın ona verdiği şeylerin dışına çıkamaz. Bu teoride kulağa harika gelir. Pratikte ise dikkatli yapılandırma gerektirir.

WASM sandbox modelini anlamak için şu üç kavramı aklınızda tutun:

  • Linear memory: WASM modülünün erişebildiği bellek bölgesi, tamamen izole edilmiş ve boyutu belirlenmiş
  • Import/Export sistemi: Modülün dış dünyayla iletişim kurabildiği tek kapı
  • Call stack izolasyonu: WASM’ın kendi call stack’i var, host ortamının stack’iyle karışmıyor

Klasik bir native uygulama çalıştırdığınızda, o uygulama işletim sistemine doğrudan sistem çağrıları yapabilir. WASM’da bu mümkün değil. Her şey host ortamının izin verdiği fonksiyonlar üzerinden geçmek zorunda.

# Basit bir WASM modülü oluşturup sandbox davranışını test edelim
# Önce wasmtime'ı kuralım (Fedora/RHEL için)
dnf install -y wasmtime

# Ubuntu/Debian için
wget https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.0/wasmtime-v14.0.0-x86_64-linux.tar.xz
tar -xf wasmtime-v14.0.0-x86_64-linux.tar.xz
sudo mv wasmtime-v14.0.0-x86_64-linux/wasmtime /usr/local/bin/

# Versiyon kontrolü
wasmtime --version

Linear Memory Modeli ve İzolasyon Gerçekliği

WASM modülü çalıştığında, kendisine ayrılmış bir linear memory bloğu alır. Bu blok dışına çıkmaya çalışırsa runtime anında bunu engeller ve trap (tuzak) mekanizması devreye girer.

# Basit bir C programını WASM'a derleyelim ve bellek davranışını görelim
# Emscripten kurulumu
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

# Basit bir C dosyası oluşturalım
cat > memory_test.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    // 1MB bellek tahsis et
    char *buffer = malloc(1024 * 1024);
    if (!buffer) {
        printf("Bellek tahsisi basarisizn");
        return 1;
    }
    
    memset(buffer, 'A', 1024 * 1024);
    printf("Bellek tahsisi basarili: %zu bytesn", (size_t)(1024 * 1024));
    
    // Belleği serbest bırak
    free(buffer);
    return 0;
}
EOF

# WASM'a derle
emcc memory_test.c -o memory_test.wasm -s WASM=1 -s STANDALONE_WASM

# wasmtime ile çalıştır ve bellek limitini gözlemle
wasmtime --max-wasm-stack 1048576 memory_test.wasm

Burada dikkat edilmesi gereken nokta şu: WASM modülü malloc çağırdığında, bu çağrı aslında WASM’ın kendi bellek yöneticisine gidiyor. Host işletim sistemine doğrudan erişim yok.

Import/Export Sistemi: Gerçek Saldırı Yüzeyi

WASM’ın en kritik güvenlik noktası import sistemidir. Modül dışarıya ne kadar fonksiyon export ediyor, dışarıdan ne kadar fonksiyon import ediyor, bunlar güvenlik açısından belirleyici.

# wasm-tools ile bir modülün import/export yapısını analiz edelim
cargo install wasm-tools

# Modülün import ve export listesini çıkar
wasm-tools print hedef_modul.wasm | grep -E "(import|export)"

# Daha detaylı analiz için
wasm-tools validate hedef_modul.wasm && echo "Modul gecerli"

# Import edilen fonksiyonları listele
wasm-tools print hedef_modul.wasm | grep "import" | awk '{print $3, $4}'

Bir WASM modülüne wasi_snapshot_preview1.fd_write gibi bir import veriyorsanız, o modüle dosya yazma yetkisi veriyorsunuz demektir. Import listesi ne kadar kısaysa, saldırı yüzeyi o kadar dar olur.

Gerçek dünya senaryosu olarak şunu düşünün: Bir e-ticaret platformunda kullanıcıların kendi fiyatlandırma algoritmalarını WASM modülü olarak yüklemesine izin veriyorsunuz. Bu senaryoda hangi import’ları verdiğiniz hayati önem taşıyor.

// Node.js üzerinde kısıtlı WASM ortamı oluşturma
const fs = require('fs');

async function runRestrictedWasm(wasmBuffer) {
    // Sadece matematiksel işlemlere izin veren minimal import nesnesi
    const restrictedImports = {
        env: {
            // Sadece log fonksiyonu, dosya sistemi yok, ağ yok
            log_value: (value) => {
                // Değeri logla ama hiçbir yan etki yaratma
                console.log(`WASM log: ${value}`);
            },
            // Rastgele sayı üretimi - kontrollü
            get_random: () => Math.random(),
        }
        // Dikkat: wasi import'ları YOK
        // Dosya sistemi erişimi YOK
        // Ağ erişimi YOK
    };

    const wasmModule = await WebAssembly.compile(wasmBuffer);
    const instance = await WebAssembly.instantiate(wasmModule, restrictedImports);
    
    return instance.exports;
}

// Kullanım
const wasmBuffer = fs.readFileSync('user_algorithm.wasm');
const exports = await runRestrictedWasm(wasmBuffer);
const result = exports.calculate_price(100, 0.2);
console.log(`Hesaplanan fiyat: ${result}`);

WASI: WebAssembly System Interface ve Güvenlik Kapıları

WASI, WASM’ı tarayıcı dışında çalıştırmak için geliştirildi. Ama aynı zamanda güvenlik modelini de genişletiyor ve karmaşıklaştırıyor.

WASI’nin capability-based security modeli şu şekilde çalışır: Modüle dosya sistemi erişimi vermek istiyorsanız, ona sadece belirli bir dizin handle’ı verirsiniz. Modül bu handle’ın dışına çıkamaz.

# wasmtime ile WASI capability'lerini kısıtlama
# Sadece /tmp/wasm-sandbox dizinine erişim ver
mkdir -p /tmp/wasm-sandbox

# Hiçbir capability vermeden çalıştır (en kısıtlı mod)
wasmtime run --dir /tmp/wasm-sandbox program.wasm

# Ağ erişimine de izin ver (dikkatli kullan!)
wasmtime run 
    --dir /tmp/wasm-sandbox 
    --tcplisten 127.0.0.1:8080 
    server.wasm

# Ortam değişkenlerine erişimi kısıtla
wasmtime run 
    --dir /tmp/wasm-sandbox 
    --env APP_MODE=production 
    --env LOG_LEVEL=info 
    program.wasm
# NOT: --env ile sadece belirtilen değişkenler görünür
# Host'un tüm env değişkenleri görünmez

Bu capability modeli, Linux’taki capability sistemiyle benzer bir felsefeye sahip. Ama WASI’yi sunucu ortamında kullanırken dikkat edilmesi gereken bir nokta var: runtime’ın nasıl yapılandırıldığı, güvenliğin gerçekte ne kadar sağlandığını belirler.

Gerçek Dünya: Edge Computing’de WASM Güvenliği

Cloudflare Workers, Fastly Compute@Edge gibi platformlar WASM’ı edge’de çalıştırıyor. Bu platformlarda her istek, yeni bir WASM instance’ında işleniyor. Bu izolasyon modeli, geleneksel process izolasyonundan farklı.

# Cloudflare Workers için wrangler kurulumu ve güvenlik yapılandırması
npm install -g wrangler

# wrangler.toml yapılandırması oluştur
cat > wrangler.toml << 'EOF'
name = "secure-edge-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"

# KV namespace bağlama - sadece tanımlananlar erişilebilir
[[kv_namespaces]]
binding = "USER_DATA"
id = "your-kv-namespace-id"

# Durable Objects için izolasyon
[[durable_objects.bindings]]
name = "SESSION_STORE"
class_name = "SessionStore"

# Ortam değişkenleri - secret olmayanlar
[vars]
ENVIRONMENT = "production"
MAX_REQUEST_SIZE = "10485760"

# Secret'lar wrangler secret put ile eklenir, burada tanımlanmaz
EOF

# Secret ekleme (asla wrangler.toml içine koymayın)
wrangler secret put DATABASE_PASSWORD
wrangler secret put API_KEY

Edge’de çalışan WASM modülleri için dikkat edilmesi gereken bir senaryo: Tenant izolasyonu. Çok kiracılı bir sistemde her tenant’ın kodu birbirinden izole olmalı.

// Edge worker'da güvenli tenant izolasyonu
export default {
    async fetch(request, env, ctx) {
        // Tenant ID'yi header'dan al ve doğrula
        const tenantId = request.headers.get('X-Tenant-ID');
        
        if (!tenantId || !isValidTenantId(tenantId)) {
            return new Response('Yetkisiz erişim', { status: 401 });
        }
        
        // Her tenant'ın sadece kendi namespace'ine erişimi var
        // env.USER_DATA KV binding tenant prefix'i ile kullanılır
        const tenantPrefix = `tenant:${tenantId}:`;
        
        // Tenant verisi oku - başka tenant'ın verisine erişim imkansız
        const userData = await env.USER_DATA.get(`${tenantPrefix}profile`);
        
        // Request boyutunu kontrol et
        const contentLength = parseInt(request.headers.get('Content-Length') || '0');
        const maxSize = parseInt(env.MAX_REQUEST_SIZE);
        
        if (contentLength > maxSize) {
            return new Response('İstek çok büyük', { status: 413 });
        }
        
        return processRequest(request, userData, tenantId);
    }
};

function isValidTenantId(id) {
    // Sadece alphanumeric karakterler ve tire
    return /^[a-zA-Z0-9-]{1,64}$/.test(id);
}

Yan Kanal Saldırıları: Spectre ve WASM

2018’de Spectre açığı ortaya çıktığında, tarayıcı geliştiricileri WASM’da SharedArrayBuffer ve yüksek çözünürlüklü timer’ları devre dışı bırakmak zorunda kaldı. Bu karar, WASM’ın yan kanal saldırılarına karşı ne kadar savunmasız olabileceğini gösterdi.

# Tarayıcı güvenlik başlıklarını doğru yapılandırma
# Nginx yapılandırması - WASM uygulamaları için güvenli başlıklar

cat > /etc/nginx/conf.d/wasm-security-headers.conf << 'EOF'
# SharedArrayBuffer için zorunlu başlıklar
# Bu başlıklar olmadan Spectre koruması devre dışı kalır
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;

# Ek güvenlik başlıkları
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'" always;

# WASM dosyaları için doğru MIME type
types {
    application/wasm wasm;
}

# WASM dosyalarını cache'le ama güvenli bir şekilde
location ~* .wasm$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Cross-Origin-Resource-Policy "same-site";
    
    # Integrity kontrolü için ETag
    etag on;
}
EOF

nginx -t && systemctl reload nginx

WASM Modülü Doğrulama ve Güvenli Dağıtım

Production ortamında çalıştıracağınız WASM modüllerini doğrulamadan deploy etmek, imzasız bir binary çalıştırmaktan farksız.

# WASM modülü için SHA-256 hash oluştur ve doğrula
# Deploy öncesi hash hesapla
sha256sum production_module.wasm > production_module.wasm.sha256
cat production_module.wasm.sha256

# Deploy sonrası doğrula
sha256sum -c production_module.wasm.sha256

# wasm-tools ile modülün geçerliliğini kontrol et
wasm-tools validate production_module.wasm
echo "Validation exit code: $?"

# Modülün içindeki custom section'ları incele (gizli kod olabilir)
wasm-tools print production_module.wasm | grep -i "custom"

# Modülün toplam kompleksitesini ölçmek için
wasm-tools stats production_module.wasm

# Otomatik doğrulama scripti
cat > /usr/local/bin/validate-wasm.sh << 'EOF'
#!/bin/bash
set -euo pipefail

WASM_FILE="$1"
EXPECTED_HASH="$2"

echo "WASM modülü doğrulanıyor: $WASM_FILE"

# Hash doğrulama
ACTUAL_HASH=$(sha256sum "$WASM_FILE" | awk '{print $1}')
if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then
    echo "HATA: Hash uyuşmuyor!"
    echo "Beklenen: $EXPECTED_HASH"
    echo "Bulunan: $ACTUAL_HASH"
    exit 1
fi

# Yapısal doğrulama
if ! wasm-tools validate "$WASM_FILE"; then
    echo "HATA: WASM modülü geçersiz!"
    exit 1
fi

echo "Doğrulama basarili: $WASM_FILE"
EOF

chmod +x /usr/local/bin/validate-wasm.sh

Kaynak Limitleri ve DoS Koruması

WASM sandbox belleği izole eder ama sonsuz döngüyü engellemez. CPU ve bellek tüketen bir WASM modülü, host sistemi etkileyebilir. Bu yüzden kaynak limitleri kritik.

# wasmtime ile kaynak limitleri belirleme
wasmtime run 
    --max-wasm-stack 8388608           # 8MB stack limiti
    --wasm-timeout 30000               # 30 saniye zaman aşımı
    --fuel 1000000000                  # Instruction sayısı limiti
    computation_heavy.wasm

# Systemd ile WASM runtime'ı sınırlandırma
cat > /etc/systemd/system/wasm-worker.service << 'EOF'
[Unit]
Description=WASM Worker Service
After=network.target

[Service]
Type=simple
User=wasm-runner
Group=wasm-runner
ExecStart=/usr/local/bin/wasmtime run /opt/wasm/worker.wasm

# Bellek limiti
MemoryMax=512M
MemorySwapMax=0

# CPU limiti
CPUQuota=50%

# Dosya sistemi kısıtlamaları
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=yes

# Ağ kısıtlaması (gerekli değilse)
PrivateNetwork=yes

# Capability kısıtlaması
CapabilityBoundingSet=
AmbientCapabilities=

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now wasm-worker

Monitoring ve Güvenlik Olaylarını Tespit Etme

WASM çalışırken neler olduğunu görmek, geleneksel process monitoring’den farklı.

# wasmtime için tracing etkinleştirme
WASMTIME_LOG=wasmtime_runtime=trace wasmtime run program.wasm 2>&1 | tee wasm-trace.log

# Kaynak kullanımı monitoring scripti
cat > /usr/local/bin/monitor-wasm.sh << 'EOF'
#!/bin/bash

WASM_PID=$(pgrep -f "wasmtime run")

if [ -z "$WASM_PID" ]; then
    echo "WASM worker çalışmıyor!"
    exit 1
fi

# Anlık kaynak kullanımı
echo "=== WASM Worker Kaynak Kullanımı ==="
echo "PID: $WASM_PID"

# CPU ve bellek
ps -p "$WASM_PID" -o pid,pcpu,pmem,rss,vsz,etime

# Açık dosya tanımlayıcıları (beklenmedik dosya erişimi?)
echo ""
echo "=== Açık Dosyalar ==="
lsof -p "$WASM_PID" 2>/dev/null | grep -v "mem" | tail -20

# Ağ bağlantıları
echo ""
echo "=== Ağ Bağlantıları ==="
ss -tp | grep "$WASM_PID"

# Sistem çağrıları (strace ile - dikkatli kullan, overhead yüksek)
# strace -p "$WASM_PID" -e trace=network,file -f 2>&1 | head -100
EOF

chmod +x /usr/local/bin/monitor-wasm.sh

# Cron job ile periyodik kontrol
echo "*/5 * * * * /usr/local/bin/monitor-wasm.sh >> /var/log/wasm-monitor.log 2>&1" | crontab -

Güvenlik Açıklarına Karşı Savunma Stratejisi

Bir sysadmin olarak WASM dağıtımlarında uygulamanız gereken katmanlı savunma stratejisi şu başlıkları kapsamalı:

  • Modül doğrulama: Her deploy öncesi hash ve yapısal kontrol, imza doğrulama pipeline’a entegre edilmeli
  • Minimal import prensibi: WASM modülüne sadece gerçekten ihtiyaç duyduğu import’ları verin, dosya sistemi erişimi gerekli değilse vermeyin
  • Kaynak kısıtlamaları: CPU, bellek ve zaman aşımı limitleri her zaman tanımlanmalı, varsayılan değerlere güvenmeyin
  • Ağ izolasyonu: WASM worker’ları ağ erişimine ihtiyaç duymuyorsa PrivateNetwork=yes ile tamamen izole edin
  • Capability-based erişim: WASI kullanıyorsanız sadece gerekli dizinlere ve sadece gerekli haklarla erişim verin
  • Güncel runtime: wasmtime, wasmer gibi runtime’ların güvenlik yamalarını takip edin, otomatik güncelleme pipeline’ı kurun
  • Audit logging: Hangi modüllerin ne zaman çalıştırıldığını, kaynak kullanımlarını ve olağandışı aktiviteleri kaydedin
  • Tenant izolasyonu: Çok kiracılı sistemlerde her tenant için ayrı process veya en azından ayrı WASM instance kullanın

Sonuç

WebAssembly’nin güvenlik modeli, doğru yapılandırıldığında gerçekten güçlü bir izolasyon sağlar. Ama “WebAssembly sandbox’ta çalışıyor, güvenlidir” deyip geçmek, büyük bir hata olur. Güvenlik, runtime’ın ne kadar iyi yapılandırıldığına, modüle hangi import’ların verildiğine ve host ortamının nasıl korunduğuna bağlı.

Sysadmin perspektifinden en önemli çıkarım şu: WASM, geleneksel container veya VM izolasyonunun yerini tutmaz, tamamlar. Kritik uygulamalar için WASM izolasyonunu systemd sandbox’ı, network namespace’leri ve cgroup limitleriyle birlikte kullanın. Güvenlik katmanlı olduğunda, bir katmanın atlatılması yeterli olmaz.

Edge computing’de WASM kullanımı artmaya devam edecek. Bu trendi yakından takip edin, runtime’larınızı güncel tutun ve güvenlik modelini gerçekten anlayarak yapılandırın. Gece 2’deki telefon çağrısından kaçınmanın en iyi yolu, production’a almadan önce doğru soruları sormaktır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir