Bokeh'da Cross-Site WebSocket Hijacking: CVE-2026-21883
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:
dashboard.corp.attacker.comgibi bir domain kaydeder- O domain’de kötü amaçlı bir website oluşturur
- Bir kurbanı kötü amaçlı siteye yönlendirir
- Kötü amaçlı site zafiyetli Bokeh server’a bir WebSocket bağlantısı başlatır
- Origin header (
http://dashboard.corp.attacker.com) hatalı mantık nedeniyle allowlist ile eşleşir - 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:
hostparçaları:['example', 'com', 'evil', 'com']patternparçaları:['example', 'com']zipexample==example(OK) vecom==com(OK) karşılaştırması yapar- Iteration durur ve fonksiyon
Truedö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
- SQL Injection Zafiyeti: GeoPandas to_postgis() Fonksiyonunda Güvenlik Açığı
- CVE-2025-66019: pypdf’te LZW Decompression DoS Zafiyeti