Bokeh’in güvenlik incelemesi sırasında, WebSocket origin validation mantığında Cross-Site WebSocket Hijacking (CSWSH) saldırılarına izin veren bir zafiyet buldum. Bokeh’in server kodundaki match_host fonksiyonu, kötü amaçlı subdomain’ler kaydedilerek bypass edilebilecek hatalı hostname eşleştirmesi kullanıyor.

Zafiyet

Sorun src/bokeh/server/util.py dosyasında. match_host fonksiyonu, allowlist pattern’lerine karşı hostname’leri Python’un zip() fonksiyonunu kullanarak karşılaştırıyor. zip() en kısa iterable bittiğinde durur, bu da bir güvenlik açığı yaratır.

Zafiyetli Kod:

def match_host(host: str, pattern: str) -> bool:
    # ...
    host_parts = host.split('.')
    pattern_parts = pattern.split('.')

    if len(pattern_parts) > len(host_parts):
        return False

    for h, p in zip(host_parts, pattern_parts):
        if h == p or p == '*':
            continue
        else:
            return False
    return True

Kod pattern’in host’tan uzun olup olmadığını kontrol ediyor, ancak host’un pattern’den uzun olup olmadığını kontrol etmiyor. Bu, pattern ile başlayan (ama ek segmentler içeren) bir host’un yanlışlıkla eşleşmesine neden olur.

Saldırı Nasıl Çalışır

Bir Bokeh server’ın ['dashboard.corp'] gibi bir allowlist’i varsa, bir saldırgan şunları yapabilir:

  1. dashboard.corp.attacker.com gibi bir domain kaydeder
  2. O domain’de kötü amaçlı bir website oluşturur
  3. Bir kurbanı kötü amaçlı siteye yönlendirir
  4. Kötü amaçlı site zafiyetli Bokeh server’a bir WebSocket bağlantısı başlatır
  5. Origin header (http://dashboard.corp.attacker.com) hatalı mantık nedeniyle allowlist ile eşleşir
  6. Bağlantı kabul edilir ve saldırgan kurban adına Bokeh server ile etkileşime girebilir

Örnek:

from bokeh.server.util import match_host

allowed_pattern = "example.com"
malicious_origin = "example.com.evil.com"

if match_host(malicious_origin, allowed_pattern):
    print(f"[ZAFİYETLİ] {malicious_origin} {allowed_pattern} ile eşleşti")

Çıktı:

[ZAFİYETLİ] example.com.evil.com example.com ile eşleşti

Eşleşme şu nedenle olur:

  1. host parçaları: ['example', 'com', 'evil', 'com']
  2. pattern parçaları: ['example', 'com']
  3. zip example==example (OK) ve com==com (OK) karşılaştırması yapar
  4. Iteration durur ve fonksiyon True döner

Etki

Bu zafiyet Cross-Site WebSocket Hijacking saldırılarını mümkün kılar. Bir saldırgan WebSocket bağlantısı kurduktan sonra:

  • Bokeh server’dan sensitive data’ya erişebilir
  • Visualization’ları değiştirebilir
  • Kurban adına işlemler yapabilir (eğer Bokeh uygulaması WebSocket mesajları üzerinden böyle işlemlere izin veriyorsa)

Kapsam:

  • Sadece deployed Bokeh server instance’larını etkiler
  • Static HTML output, standalone embedded plot’lar veya Jupyter notebook kullanımını etkilemez
  • Yerinde authentication hook’ları varsa bunları bypass etmez
  • Private/internal network server’ları dışarıdan erişilebilir hale getiremez

Fix

Bokeh bunu 3.8.2 versiyonunda düzeltti. Fix, karşılaştırmadan önce host ve pattern’in aynı sayıda parçaya sahip olduğundan emin olur.

Önerilen Fix:

if len(pattern_parts) != len(host_parts):  # Eşit uzunluk zorunlu
    return False

Bu, ek segmentler içeren host’ların pattern’lerle eşleşmesini önler.

Etkilenen Versiyonlar

  • Bokeh < 3.8.2

Patch Edilmiş Versiyonlar

  • Bokeh 3.8.2 ve sonrası

Disclosure Timeline

  • İlk Rapor: Bokeh güvenlik ekibine raporlandı
  • Security Advisory: GHSA-793v-589g-574v
  • CVE: CVE-2026-21883
  • Fix: Version 3.8.2 yayınlandı

Sonuç

WebSocket bağlantılarında origin validation, CSWSH saldırılarını önlemek için kritiktir. Bokeh’in match_host fonksiyonundaki hatalı hostname eşleştirme mantığı, saldırganların kötü amaçlı subdomain’ler kaydederek allowlist kontrollerini bypass etmesine izin verdi. Fix, karşılaştırmadan önce katı uzunluk eşleştirmesi sağlar.

Ana çıkarım: Hostname veya origin validate ederken, her zaman pattern ve host’un aynı sayıda segment’e sahip olduğunu kontrol et. Kısmi eşleşmelere güvenme.

İlgili İçerik

Kaynaklar