Bokeh'da Cross-Site WebSocket Hijacking: CVE-2026-21883
Bokeh üzerinde güvenlik incelemesi yaparken WebSocket origin doğrulamasında Cross-Site WebSocket Hijacking (CSWSH) zafiyeti buldum. Server tarafındaki match_host fonksiyonu hostname’i yanlış karşılaştırıyor; saldırgan kötü bir subdomain kaydederek allowlist’i aşabiliyor.
Zafiyet nerede?
Sorun src/bokeh/server/util.py içinde. match_host, allowlist pattern’leriyle hostname’i Python’un zip() ile karşılaştırıyor. zip() en kısa iterable bittiğinde durduğu için host pattern’den daha uzun olsa bile eşleşme dönüyor.
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
Pattern’in host’tan uzun olup olmadığına bakılıyor, ama host’un pattern’den uzun olması engellenmiyor. Bu yüzden pattern ile başlayan ek segmentli bir host (örn. example.com.evil.com) yanlışlıkla kabul ediliyor.
Saldırı nasıl işliyor?
Bokeh server allowlist’te örneğin ['dashboard.corp'] varsa:
- Saldırgan
dashboard.corp.attacker.comgibi bir domain alıyor - O domain’de kötü amaçlı bir site açıyor
- Kurbanı bu siteye çekiyor
- Site, kurbanın tarayıcısından Bokeh server’a WebSocket bağlantısı açıyor
- Origin header (
http://dashboard.corp.attacker.com) hatalı mantık yüzünden allowlist ile eşleşiyor - Bağlantı kabul ediliyor; saldırgan kurban adına Bokeh ile etkileşebiliyor
Ö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 şöyle oluyor: host → ['example','com','evil','com'], pattern → ['example','com']. zip sadece ilk iki çifti karşılaştırıp True dönüyor; geri kalan segment’lere bakılmıyor.
Etki
Cross-Site WebSocket Hijacking mümkün oluyor. Bağlantı kurulduktan sonra saldırgan:
- Bokeh server’daki hassas verilere erişebiliyor
- Görselleştirmeleri değiştirebiliyor
- Kurban adına (WebSocket mesajlarıyla tanımlı) işlem yapabiliyor
Kapsam: Sadece deploy edilmiş Bokeh server instance’ları etkilenir. Static HTML, standalone plot veya Jupyter kullanımı etkilenmez. Authentication hook’ları ayrıca korunmalı; bu zafiyet onları bypass etmez. Internal server’ı tek başına dışarı açmaz.
Düzeltme
Bokeh 3.8.2’de düzeltildi. Host ve pattern’in aynı sayıda segment’e sahip olması zorunlu tutuluyor.
Önerilen mantık:
if len(pattern_parts) != len(host_parts):
return False
Böylece example.com.evil.com artık example.com ile eşleşmiyor.
Etkilenen / düzeltilmiş sürümler
- Etkilenen: Bokeh < 3.8.2
- Düzeltilmiş: Bokeh 3.8.2 ve sonrası
Raporlama
Bokeh güvenlik ekibine bildirdim. GHSA-793v-589g-574v, CVE-2026-21883 atandı, fix 3.8.2’de.
Özet
WebSocket’te origin doğrulama CSWSH’i önlemek için gerekli. Bokeh’teki match_host host’u pattern ile segment sayısına bakmadan kısmen karşılaştırıyordu; saldırgan kötü subdomain ile allowlist’i aşabiliyordu. Hostname/origin kontrolü yaparken pattern ile host’un segment sayısının aynı olduğundan emin ol; kısmi eşleşmeye güvenme.
İlgili içerik
- SQL Injection Zafiyeti: GeoPandas to_postgis() Fonksiyonunda Güvenlik Açığı
- CVE-2025-66019: pypdf’te LZW Decompression DoS Zafiyeti