Birkaç ay önce bir müşterimiz için devreye almadan önce kontrol ettiğimiz bir web uygulamasını izlemeye aldık. Uygulama daha duyurulmamıştı, adresini bizden ve müşteriden başka kimse bilmiyordu. İlk otomatik tarama botu 40 dakika içinde geldi. Kırk dakika. Wordpress açığı arıyordu, phpMyAdmin arıyordu, .env dosyası arıyordu — bulamayınca gitti, ertesi gün üç farklı IP'den arkadaşları geldi.
Bunu neden anlatıyorum? Çünkü hâlâ "bizim siteyi kim hackleyecek canım" cümlesini duyuyoruz. Kimse sizi hedef almıyor; botlar interneti halı gibi tarıyor ve açığı olan herkes hedef. Bu gerçeğin karşısındaki en iyi başlangıç haritası da OWASP Top 10 — dünyada en yaygın görülen web güvenlik açıklarının, düzenli güncellenen resmi listesi. 500'ün üzerinde projede biriken tecrübemizle bu listeyi saha diline çevirmeye çalışayım.
Listeyi yanlış anlamamak için bir not: OWASP Top 10 bir tavan değil, taban. "Bu onunu hallettik, güvenliğiz" diyemezsiniz; ama bu onunu halletmemişseniz gerisini konuşmanın da anlamı yok. Güncel liste 2021'de yenilendi ve sıralamadaki değişimler, saldırganların ilgisinin nereye kaydığını gösteren iyi bir barometre — enjeksiyon geriledi ama ölmedi (ona ayrı bir yazı borçluyuz), yetki hataları ve tasarım kusurları öne çıktı.
Zirvedeki açık artık enjeksiyon değil
Yıllarca listenin tepesinde SQL injection oturdu; güncel listede zirve Broken Access Control'ün, yani yetki kontrolü hatalarının. Ve açık konuşayım, sahada gördüklerimiz bunu sonuna kadar doğruluyor.
En klasik biçimi şu: kullanıcı kendi siparişini /siparis/1453 adresinde görüntülüyor. URL'deki sayıyı 1454 yapıyor — ve başkasının siparişi ekrana geliyor. Adı IDOR (Insecure Direct Object Reference), ama adının havalı olması yaygınlığını değiştirmiyor. Geçen yıl güvenlik gözden geçirmesi yaptığımız projelerin neredeyse yarısında bir biçimde bu açık vardı. Yarısında.
Kural aslında tek cümle: sunucu, her istekte "bu kullanıcının bu kaynağa erişme hakkı var mı?" sorusunu sormalı. Menüde linki gizlemek, butonu disable etmek, ID'yi tahmin edilmez yapmak — bunların hiçbiri güvenlik değil. İstemcide yapılan her kontrol, saldırgan için sadece bir öneridir.
Listenin üst sıralarındaki bir diğer kalem kriptografik hatalar — ve burada da tablo iç açıcı değil. Hâlâ MD5 ile hash'lenmiş şifre tabloları görüyoruz; hâlâ "şifrenizi hatırlatalım" diye düz metin şifre e-postalayan sistemler geliyor önümüze (şifreniz size geri gönderilebiliyorsa, düz metin veya çözülebilir biçimde saklanıyor demektir — bu kadar basit). Şifreler bcrypt ya da argon2 gibi bu iş için tasarlanmış algoritmalarla saklanmalı, aktarımda TLS pazarlık konusu bile olmamalı. Bunlar 2023'te hâlâ yazılması gereken cümlelerse, biraz da aynaya bakmamız gerekiyor demektir.
Kod hatası değil, tasarım hatası
Listede bizi en çok düşündüren madde "Insecure Design" — güvensiz tasarım. Çünkü bu, satır satır kod incelemesiyle yakalanmaz; akışın kendisi hatalıdır.
Sahadan bir örnek: devraldığımız bir projede şifre sıfırlama akışı, e-posta adresinin gerçekten kullanıcıya ait olduğunu doğrulamadan sıfırlama token'ı üretiyordu. Kod tertemizdi — prepared statement'lar yerinde, çıktılar kaçışlanmış, her şey ders kitabı gibi. Ama akışın mantığı çürüktü ve saldırgana hesap ele geçirme kapısı açıyordu. Bu tür hataları yakalamanın tek yolu, daha kod yazılmadan tasarımı kötü niyetli gözle gözden geçirmek. Biz buna içeride "kötü adam provası" diyoruz: bu akışı ben kötüye kullanmak istesem nereden girerdim?
Bir müşterimizin ödeme akışında bu prova sayesinde, indirim kuponunun sınırsız kez kullanılabildiğini canlıya çıkmadan üç gün önce fark ettik. Kod hatasız çalışıyordu; tasarım deliydi.
Yapılandırma hataları da ayrı bir dert. Canlıda açık unutulan debug modu, internete açık admin paneli, varsayılan şifresiyle duran yönetim arayüzü, herkese okunabilir bırakılmış bulut depolama alanı... Bunların hiçbiri kod yazmayı gerektirmiyor; bir kontrol listesi ve devreye alma disiplini yetiyor. Bizim devreye alma prosedürümüzde "üretim öncesi güvenlik turu" diye on beş dakikalık bir madde var — debug kapalı mı, hata sayfaları sade mi, admin erişimi kısıtlı mı. On beş dakika. Atlandığı her seferinde bir gün pişmanlık olarak geri döndü.
Eskiyen kütüphaneler ve görmeyen loglar
İki madde daha var ki, gösterişsiz oldukları için hep ihmal ediliyorlar ama vakaların önemli kısmı buralardan çıkıyor.
Birincisi bileşen eskimesi. Uygulamanız sizin yazdığınız koddan çok, başkalarının yazdığı kütüphanelerden oluşuyor — framework, ORM, PDF üreteci, resim işleyici... Bunlardan birinde bilinen bir açık (CVE) yayınlandığında, saldırganlar için hazır anahtar üretilmiş demektir. Yıllardır composer update veya npm audit yüzü görmemiş projeler, kapısı açık bırakılmış depo gibidir. Kaç kere gördük: müşteri "sistem yıllardır sorunsuz çalışıyor, dokunmayalım" diyor; sonra o dokunulmayan sistemin beş yıllık kütüphanesindeki açıktan içeri giriliyor.
İkincisi loglama ve izleme yetersizliği. Saldırıyı engellemek kadar, olduğunu fark etmek de önemli — ve ortalama bir ihlalin fark edilmesi sektör genelinde aylar alıyor. Başarısız giriş denemelerini kaydetmeyen bir sistem, haftalardır süren brute force saldırısını göremez. Kaydedip kimsenin bakmadığı log da aynı kapıya çıkar; log, uyarı üretmiyorsa dekordur.
Pazartesi sabahı yapılacaklar
Teori güzel ama somut bir başlangıç planı olmadan bu yazı da rafta kalır. Bize sorarsanız sıra şöyle: önce bağımlılıklarınızı tarayın ve güncelleyin — bu, harcanan saat başına en çok risk kapatan iştir. Sonra tüm endpoint'lerinizde sunucu taraflı yetki kontrolü olduğunu tek tek doğrulayın; özellikle ID ile kaynak çeken her yeri. Üçüncü adım girdi-çıktı hijyeni: veritabanı sorgularında prepared statement, HTML çıktısında kaçışlama. Dördüncüsü loglama ve uyarı düzeni: en azından kimlik doğrulama olayları, yetki hataları ve 500'ler bir yerde toplansın ve eşik aşımında birine haber gitsin.
Bu dördü sizi sıfır riske indirmez — öyle bir şey yok — ama otomatik botların ve fırsatçı saldırganların kolay hedef listesinden çıkarır. Saldırganların çoğu kilitli kapıyla uğraşmaz; yandaki açık kapıya gider.
Bir de işin ekip boyutu var. Güvenlik, yalnızca güvenlikçinin işi olarak kaldığı sürece kaybediyorsunuz; kod yazan herkesin temel refleksleri edinmesi gerekiyor. Biz bunu code review'a yedirdik: her incelemede yetki kontrolü ve girdi işleme özellikle sorgulanır, "bunu ben kötüye kullansam nasıl kullanırdım" sorusu şablonda hazır bekler. Yeni katılan her arkadaş OWASP listesini ilk haftasında okur — üniversitede öğretilmiyor çünkü, hâlâ.
Beşinci adımı da söyleyeyim: dışarıdan bir göz. Kendi uygulamanıza kendi ekibinizle ne kadar bakarsanız bakın, kör noktalarınız sizinle birlikte bakıyor. Belirli aralıklarla bağımsız bir güvenlik gözden geçirmesi yaptırın — bizden ya da başkasından, ama yaptırın. Kendi projeleriniz için nereden başlayacağınızı konuşmak isterseniz buradan ulaşabilirsiniz.
Güvenlik bir ürün değil, alışkanlık. Ve alışkanlıklar küçük, sıkıcı, düzenli adımlarla kurulur.