Kod değiştirmeden PHP güvenlik açığı kapatmak: backport base image
Desteği bitmiş PHP 7.4 / 8.1 uygulamalarında kritik bir açığı, koda dokunmadan yamalı bir base image ile kapatmak: geçici önlem ve kalıcı çözüm.
Kritik bir PHP açığı yayımlandığında ilk soru "hangi sürüme geçeceğiz?" olur. Güncel sürümler için cevap kolaydır: yamalı minor sürümü çekip yeniden build edersiniz. Zor olan, desteği bitmiş sürümlerde çalışan ve koduna dokunulamayan uygulamalardır.
Tipik tablo şudur: uygulama desteği bitmiş bir PHP sürümünde çalışır, eski bir framework kullanır ve kodu kısa sürede değiştirilemez. Sürüm göçü haftalar sürer; açık ise bugün kapatılmalıdır.
1. Önce envanter
Tahminle değil, ölçerek başlayın. Her container'da çalışan PHP sürümünü ve SAPI'yi çıkarın:
kubectl get pods -A --no-headers \
-o custom-columns=NS:.metadata.namespace,POD:.metadata.name |
while read -r ns pod; do
v=$(kubectl exec -n "$ns" "$pod" -- php -r 'echo PHP_VERSION, " ", PHP_SAPI;' 2>/dev/null) \
&& echo "$ns/$pod $v"
done
Envanter çoğu zaman listeyi kısaltır: "PHP sanılan" bazı servisler başka bir dilde yazılmıştır, bazıları da açığın etkilemediği bir SAPI ile çalışır. Liste kısaldıkça plan netleşir.
Aynı turda basit bir kompromize kontrolü de yapın: /tmp ve /dev/shm altındaki çalıştırılabilir dosyalar, upload klasörlerindeki .php dosyaları, beklenmeyen cron'lar, garip isimli süreçler. Yamadan önce zaten içeride biri var mı, bilmek istersiniz.
2. Aynı gün: geçici önlemler
Yamalı image hazırlanırken saldırı yüzeyi daraltılabilir. Bu tür açıklarda resmi öneriler genellikle istek gövdesi ve upload işlemiyle ilgilidir:
; ConfigMap ile php-fpm havuzuna mount edilir
post_max_size = 32M
upload_max_filesize = 30M
session.upload_progress.enabled = Off
Ingress tarafında da gövde sınırı küçültülür (nginx.ingress.kubernetes.io/proxy-body-size: 32m). Bu değerleri ekiplerle teyit edin: koddaki en büyük dosya doğrulaması kaç MB? Sınırı ona göre koyun.
İki tuzak:
- Bellek limiti olmayan container'lar. Bazı öneriler PHP tarafında bellek sınırının kaldırılmasını içerir; container'ın kendi limiti yoksa bu, node'u riske atar. Önce limit, sonra ayar.
- CI deploy'u ayarları ezer mi? Pipeline sadece kubectl set image yapıyorsa mount'lar ve limitler korunur. Tam manifest apply ediyorsa ezilir; kontrol edin.
3. Kalıcı çözüm: aynı sürümde, yamalı image
PHP güvenlik ekibi bu tür açıklar için eski dallara kaynak backport'ları yayımlayabiliyor. Fikir basit: resmi php:7.4.33-fpm image'ını alın, içindeki PHP kaynağına yamayı uygulayın, aynı configure parametreleriyle yeniden derleyin.
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
ARG PATCH_DIR
COPY ${PATCH_DIR}/ /tmp/patches/
RUN set -eux; \
php -m > /tmp/modules.before; \
opts="$(php-config --configure-options)"; \
docker-php-source extract; cd /usr/src/php; \
for p in /tmp/patches/*.patch; do patch -p1 --fuzz=0 < "$p"; done; \
eval "./configure $opts"; make -j"$(nproc)"; make install; \
php -m | diff /tmp/modules.before -; \
php-fpm -t
Önemli noktalar:
- --fuzz=0: Yama birebir tutmuyorsa build düşsün. "Yaklaşık uydu" kabul edilmez.
- Modül listesi önce ve sonra aynı olmalı. Bir eklenti sessizce kaybolursa uygulama prod'da kırılır.
- Yamalı kaynağı tekrar php.tar.xz olarak paketleyin; sonradan docker-php-ext-install ile derlenen eklentiler de yamalı kaynağı kullansın.
- Image'a bir işaret bırakın (ör. PHP_BACKPORT env değişkeni ve yamaların hash'lerini içeren bir dosya). PHP_VERSION değişmediği için "bu image yamalı mı?" sorusunun cevabı buradan okunur.
4. CI: kanıtsız image basılmaz
Pipeline üç adımlı:
- verify: Yama klasörü boş olamaz; her
.patchdosyasıSHA256SUMSile eşleşmeli. - build + smoke test:
php -v,php-fpm -tve backport işaretinin kontrolü. - push: Yalnız ana dalda ve zamanlanmış çalıştırmada. Bir hareketli etiket (
8.1-fpm) ve bir sabit etiket (8.1.34-fpm-<yama-id>) basılır; sabit etiket geri dönüş içindir.
Uygulama tarafında tek değişiklik base image satırıdır:
-FROM php:8.1-fpm
+FROM registry.example.com/base-images/php-backport:8.1-fpm
Ardından her uygulama test → pasif prod → aktif prod sırasıyla yeniden build edilir ve her ortamda ayrı onay alınır.
5. Unutulmaması gerekenler
- Haftalık zamanlanmış rebuild. Base image'lar ancak Dockerfile değişince build ediliyorsa, alttaki güvenlik güncellemeleri hiç gelmez.
- Image'larla gelen artıklar. Bu tür taramalarda image içine gömülmüş test sayfaları ya da herkesin yazabildiği (777) klasörler gibi hijyen sorunları da çıkabilir. Yama açığı kapatır, hijyeni değil.
- Bu bir köprüdür, varış noktası değil. Desteği bitmiş sürümlerde kalmanın maliyeti artarak sürer. Kalıcı plan, desteklenen PHP ve framework sürümlerine göçtür.
Ders
"Kodu değiştiremiyoruz" cümlesi "açığı kapatamıyoruz" demek değildir. Sorumluluk sınırını doğru çizin: uygulama kodu yazılım ekibinin, çalışma zamanı ve image DevOps'un. Kritik bir açıkta DevOps'un payına düşen kısım, çoğu zaman sanıldığından büyüktür.