20 Ekim 2026 akşamı, PHP'de iki kritik uzaktan kod çalıştırma açığının birkaç gün içinde duyurulacağını iddia eden bir paylaşım dolaşmaya başladı. Açıklar bir çerçevede ya da eklentide değil, dilin çalışma ortamının kendisindeydi. Müşterilerimiz için işlettiğimiz sitelerin neredeyse tamamı PHP ile çalışıyor. Bu yazı sonraki 48 saati anlatıyor: ne yaptık, hangi sırayla yaptık ve neyi yanlış yaptık.
Söylentiden resmî duyuruya
İlk paylaşım doğrulanmamıştı; bu yüzden ilk iş gerçek olup olmadığını anlamaktı. Birkaç saat içinde PHP güvenlik ekibi bir ön duyuru yayımladı: CVSS v4.0'a göre 9.2 puanlı iki kritik açık ve 21 Ekim 14:00 UTC için planlanmış bir güvenlik sürümü. Ayrıntılar sızdığı için sürüm bir gün öne çekilmişti. Düzeltmeler sonunda yaklaşık 13:34 UTC'de yayımlandı. O ana kadar PHP projesi dışında kimse hangi sürümlerin ve ayarların etkilendiğini bilmiyordu. Biz de en kötü duruma göre plan yaptık: her sunucudaki her PHP sürümü.
İşe bir envanter çıkararak başladık: her sunucu, kurulu her PHP sürümü, site sunan her havuz ve her birinin güncellemeleri hangi paket kaynağından aldığı. Bu envanter tek tek düzeltmelerin hepsinden daha önemli çıktı. Eski bir sunucunun, paket kaynağının güncelleme yayımlamayı tamamen bıraktığı bir işletim sistemi sürümünde çalıştığını gösterdi. Yani o sunucuya normal yoldan hiçbir yama gelmeyecekti.
Yamadan önce katmanlı önlemler
Henüz yama yokken, her biri ucuz ve geri alınabilir katmanlar halinde koruma ekledik:
- Şüpheli giriş noktasını kapatmak. Açıkları yeniden ürettiğini söyleyen bir araştırmacı, PHP'nin oturum yükleme ilerlemesi (session upload progress) işleyicisini işaret ediyordu. Hiçbir sitenin buna dayanmadığını kontrol ettikten sonra her sunucudaki her PHP sürümünde
session.upload_progress.enabled = Offayarını yaptık. Resmî duyuru daha sonra bu yolun PHP-FPM'de istismar edilebildiğini doğruladı; ama bunun birkaç yoldan yalnızca biri olduğunu da söyledi. Biz de bunu hiçbir zaman bir düzeltme saymadık. - Saldırı izi aramak. Tüm sunucuları yalnızca okuyarak taradık: PHP işçilerinin çökmeleri (bellek bozma denemesi genellikle işçiyi çökertir), PHP'nin başlattığı kabuk ya da indirme araçları, yayına aldığımız hâlle karşılaştırılarak değişmiş PHP dosyaları, yeni cron işleri, kullanıcılar ve SSH anahtarları. Hiçbir şey bulmadık.
- Sunucuyu kilitlemek. Sitelerin çoğu Cloudflare'in arkasında. Cloudflare'in acil kuralları saldırı trafiğini süzebiliyor, ama yalnızca trafik Cloudflare'den geçiyorsa. Sunucunun IP adresini bilen biri Cloudflare'i atlayabilir. Web sunucularını, Cloudflare arkasındaki sitelerin yalnızca Cloudflare'den ve sunucunun kendisinden bağlantı kabul edeceği şekilde değiştirdik; henüz Cloudflare'de olmayan birkaç siteyi de oraya taşıdık. Bunun nasıl yapıldığını ayrı bir rehberde anlattık.
Düzeltme ve onu vermeyen paket sunucusu
Sürüm yayımlandığında ayrıntılar ciddiydi. CVE-2026-109618, PHP bir isteği işlerken belleği tükendiğinde belleği bozuyor. CVE-2026-109619 ise bir isteğin durumunun aynı işçideki bir sonraki isteğe sızmasına yol açıyor. İkisi de kimlik doğrulaması olmadan kod çalıştırmaya izin veriyor. Düzeltilmiş sürümler 8.2.35, 8.3.36, 8.4.27 ve 8.5.12; 7.4 için de küçük bir geri taşıma var. Teknik ayrıntılar güvenlik uyarımızda.
Yamalı paketler birkaç saat içinde derlendi. Ama herkesin indirdiği paket sunucusu akşamın büyük kısmında HTTP 503 hatası verdi; bütün dünya aynı güncellemeyi aynı anda indiriyordu. Üstelik eski bir paket listesi sunmaya devam etti. Beklemek yerine, yayımlanmış paketleri doğrudan derleme hizmetinden indirdik ve her sunucuya yalnızca zaten kurulu olan paketleri kurduk; her site mevcut PHP sürümünde kaldı. Akşamın sonunda web sunucularımızdaki her PHP sürümü (7.4, 8.2, 8.3, 8.4 ve 8.5) yamalı bir sürümle çalışıyordu. Uzun süre çalışan PHP işçilerini de yeniden başlattık; böylece eski kodu bellekte tutan süreç kalmadı. Derlenmesi PHP eklenti deposuna bağlı olan, konteynerde çalışan tek bir bileşen beklemek zorunda kaldı: o depo da aşırı yüklüydü. Bileşen, depoya erişilebildiği anda yeniden derleniyor.
Ömrü dolmuş bir sunucuyu taşımak
İşletim sistemi artık güncelleme almayan sunucunun bir yamadan fazlasına ihtiyacı vardı. Sunucunun tam ve doğrulanmış bir yedeğini sunucunun dışına aldık, aynı akşam desteklenen bir sürüme yükselttik ve sitelerinin PHP sürümünü yeni sisteme yeniden kurduk. Olayın müşterilerimizin görebildiği tek kısmı buydu: o sunucudaki siteler yaklaşık 30 dakika kapalı kaldı. Hizmet yeniden başlatmalarındaki birkaç saniye dışında diğer bütün siteler ayakta kaldı.
Üç hata
Hatalar yaptık ve bunlar iyi giden kısımlar kadar öğretici.
- Kullanılan bir çalışma ortamını kaldırdık. İlk saatlerde, hiçbir şey kullanmıyor gibi göründüğü için bir sunucudan eski bir PHP sürümünü kaldırdık. Cron işlerine ve betiklere bakmıştık, ama servis tanımlarına bakmamıştık; bir izleme ajanı onunla çalışıyordu. Bellekteki kopyayla çalışmayı sürdürdüğü için hiçbir şey bozulmadı ve saatler sonra kendi saldırı izi taramamız bunu yakaladı; geri kurduk. Ders: bir şeyi kaldırmadan önce servis tanımlarına bakmak.
- Uygulamalar yerine yöneticiyi yeniden başlattık. Node.js'i güncelledikten sonra yalnızca uygulamaları değil, onları çalıştıran süreç yöneticisini de yeniden başlattık. Yönetici eski bir kayıtlı süreç listesini yükledi ve bir iç servis, biz geri getirene kadar yaklaşık on dakika kapalı kaldı. Ders: yöneticiyi değil, uygulamaları yeniden başlatmak.
- İşletim sistemi yükseltmesi istemediğimiz bir şey kurdu. Sürüm yükseltmesi, gerçek web sunucumuzun ihtiyaç duyduğu portu kapan bir web sunucusu modülünü sessizce kurdu. Yükseltme sonrası kontrollerimiz bunu, yükseltmeyi bitti saymadan önce yakaladı ve modülü kaldırdık. Bu güvenlik açısından da önemliydi: o modül, tam da bu açık için PHP'yi çalıştırmanın en savunmasız yolu.
Oradayken düzelttiğimiz başka şeyler
Aynı günlerde iki güvenlik sürümü daha geldi. Bir sertifika otoritesinin ara anahtarlarının ele geçirildiği ortaya çıktı; sunucularımızdaki sertifika listelerini güncelledik ve hiçbir sitemizin o otoriteyi kullanmadığını kontrol ettik. Node.js güvenlik sürümleri yayımladı; bunları kurduk ve Node servislerimizi yeni sürümle yeniden başlattık. Veritabanlarını ve önbellekleri de yalnızca yerel makineye bağladık. Güvenlik duvarı onları zaten engelliyordu; ama işletim sistemi yükseltmesi sırasında güvenlik duvarı kısa bir süre kapalı kaldı ve derinlemesine savunma tek bir katmana dayanmamalı. Ayrıca süreç denetimini açtık. Böylece bir dahaki sefere "PHP çalıştırmaması gereken bir komutu çalıştırdı mı?" diye sorduğumuzda, cevap bir tahmin değil, bir kayıtta olacak.
Çıkardığımız sonuç
Bir düzeltmenin yayımlanmasıyla kurulması farklı anlardır; arada bazen saatler olur. Hazırlık o saatlerde işe yarar: ne çalıştırdığını bilmek, ucuz geçici önlemleri hazır tutmak ve bir yamayı olağan kanalı beklemeden kurabilmek. Envanteri, süreç denetimini ve sunucu kilidini kalıcı değişiklikler olarak koruyoruz.