Sağlık verisiyle çalışmak: güveni mimariye gömmek
En hassas veri sınıfında ürün geliştirmenin kuralları: veri minimizasyonu, erişim katmanları ve denetim izi.
Sektöre otuz yılda öğrendiğimiz en pahalı ders şu oldu: güven, arayüze yazdığınız bir cümle değildir. "Verileriniz bizimle güvende" yazan bir banner, altındaki mimari bunu zorunlu kılmıyorsa en iyi ihtimalle bir temenni, en kötü ihtimalle bir yalandır. Güven, bir kişinin en mahrem bilgisini size teslim ettiği anda devreye giren ve o bilginin sisteminizde geçirdiği her milisaniyede yeniden kanıtlanması gereken bir yapıdır. Kanıtlanamayan güven, güven değildir.
PsyBank'ı kurarken işe bu dersle başladık. Platform bugün 6.443'ü aşkın uzman terapisti 101.446'dan fazla danışanla buluşturuyor, danışan memnuniyeti %98. İşlediğimiz verinin niteliği ise belirleyici: birinin hangi terapistle, ne zaman, hangi konuda görüştüğü. Bu, KVKK'nın "özel nitelikli kişisel veri" dediği en hassas sınıf. Bir e-ticaret sepetinin sızması can sıkıcıdır; bir insanın terapi aldığı bilgisinin sızması geri alınamaz. Bu iki durum arasındaki fark, mimarinin tamamını değiştirir.
Bu yazı, en hassas veri sınıfında ürün geliştirmenin kurallarını anlatıyor — ama anlatacaklarımızın neredeyse tamamı sağlık dışı her sektöre uyarlanır. Veriyi sınıflandırma, minimizasyon, alan bazlı şifreleme, erişim katmanları ve denetim izi; bunlar bir fintech'te de, bir İK yazılımında da, sıradan bir e-ticarette de aynı şekilde işler. Sağlık verisi sadece, bu ilkeleri atlamanın bedelini herkesten daha çabuk ve daha acı gösterir.
Gizlilik sonradan eklenmez
En sık gördüğümüz hata, gizliliği bir "sonraki sürüm" işi olarak ertelemek. Ürün önce çalışsın, kullanıcı gelsin, güvenliği sonra sıkılaştırırız denir. Yıllar bize bunun neden imkânsız olduğunu tek tek gösterdi: gizlilik, ürünün üzerine sonradan sürülen bir cila değil, taşıdığı iskeletin biçimidir. Biçimi sonradan değiştiremezsiniz, ancak kırıp yeniden dökebilirsiniz.
Neden sonradan eklenemez? Çünkü ertelediğiniz her gün, geri alınamayan kararlar birikir. Hassas veriyi düz metin tutacak şekilde kurulmuş bir tabloyu sonradan şifrelemek, milyonlarca satırı taşıyan riskli bir migrasyon demektir. İlk günden kişisel veriyi loglayan bir sistemde o loglar çoktan yedeklere, arşivlere ve üçüncü parti izleme araçlarına kopyalanmıştır — hepsini geri çağıramazsınız. Analitik aracına gönderdiğiniz e-posta adresleri artık sizin kontrolünüzde değil. Sonradan gizlilik eklemek, dökülmüş sütü şişeye geri koymaya çalışmaktır.
Gizlilik bir özellik değil, bir kısıttır. Özellikler sonradan eklenir; kısıtlar en baştan konur. Bir sistemi kurduktan sonra "keşke şu veriyi hiç toplamamış olsaydık" diyemezsiniz — o veri çoktan her yere sızmıştır.
Bu erteleme alışkanlığının altında yatan yanlış bir varsayım var: tehdidin hep dışarıdan, kötü niyetli bir saldırgandan geleceği. Oysa deneyimimiz tersini söylüyor — ihlallerin çoğu meraklı bir iç kullanıcıdan, yanlış yapılandırılmış bir yedekten ya da aceleyle eklenmiş bir üçüncü parti entegrasyondan doğar. Gizliliği mimariye gömmek, işte bu sıradan, gündelik hataların bedelini küçültmektir. Dış saldırgana karşı bir kaleyi geç de olsa inşa edebilirsiniz; ama içeriden gelen kazayı ancak baştan kurduğunuz sınırlar durdurur.
Bunun pratik karşılığı şuydu: 14 haftalık geliştirme sürecinde gizlilikle ilgili kararların çoğunu ilk iki haftada, tek satır ürün kodu yazmadan önce verdik. Hangi verinin toplanacağı, nerede duracağı, kimin göreceği, ne kadar saklanacağı — bunlar şema tasarımının parçasıydı, sonuna iliştirilecek bir güvenlik sprint'i değil. Bu ön yatırım pahalı görünür; oysa alternatifi, ürün büyüdükçe katlanarak artan ve bir gün ihlalle patlayan bir teknik borçtur.
Veriyi sınıflandırmadan koruyamazsınız
Her şeyi eşit korumaya çalışmak, hiçbir şeyi doğru korumamaktır. Bütçeniz, dikkatiniz ve mimari karmaşıklığınız sınırlı; bunları en hassas veriye yoğunlaştırmak için önce neyin ne kadar hassas olduğunu bilmeniz gerekir. Bu yüzden ilk yaptığımız iş, tuttuğumuz her veri alanını bir hassasiyet sınıfına yerleştirmek oldu. Dört sınıf kullanıyoruz:
- Genel: herkese açık bilgi — terapistin uzmanlık alanı, yaklaşımı, ilan ettiği ücret. Sızması bir zarar doğurmaz, şifreleme veya erişim kısıtı gerektirmez.
- Dahili: operasyonel ama kişiyle ilişkilendirilemeyen veri — sistem metrikleri, anonim kullanım istatistikleri. Dışarı açılmamalı ama tek başına kimseyi tanımlamaz.
- Kişisel: bir kişiyi tanımlayan veri — ad, e-posta, telefon, ödeme geçmişi. KVKK'nın koruması bu sınıfta başlar.
- Özel nitelikli: sağlık verisi — kimin terapi aldığı, seans kayıtları, atanan psikolojik ölçek sonuçları. En yüksek koruma, en dar erişim ve en sıkı denetim burada uygulanır.
Bu sınıflandırma bir doküman değil, kodda yaşayan bir etiket. Her veritabanı sütununun bir hassasiyet etiketi var; şifreleme, maskeleme ve denetim kuralları bu etikete bağlı otomatik uygulanıyor. Bir geliştirici yeni bir alan eklerken sınıfını belirtmek zorunda — belirtmezse şema doğrulaması geçmiyor. Böylece "bu alan ne kadar hassas?" sorusu, unutulabilecek bir dokümantasyon işi olmaktan çıkıp kodun zorunlu bir parçası oluyor. İnsan hafızasına bıraktığınız her kural, bir gün unutulur; şemaya gömdüğünüz kural unutulamaz.
Sınıflandırmanın hemen ardından gelen soru minimizasyon: bu alanı gerçekten tutmak zorunda mıyız? Yıllar bize en güçlü güvenlik önleminin şifreleme değil, hiç toplamamak olduğunu öğretti. Her yeni alan için ekibe tek soru sorduruyoruz: "Buna ihtiyacımız var mı, yoksa ileride lazım olur diye mi istiyoruz?" Cevap ikinciyse alan açılmıyor. En radikal kararı da mimaride verdik: seansın ses ve görüntüsü uçtan uca şifreli akar, içerik sunucuya hiç uğramaz. Sunucu yalnızca "kim, kiminle, hangi odada" bilgisini bilir. Konuşmanın kendisi, yani en değerli veri, bizim sistemimizde hiçbir zaman bir kayıt olarak var olmaz. Var olmayan veriyi korumak, taşımak, silmek veya bir ihlalde açıklamak zorunda kalmazsınız. Bu tek karar, en hassas verimizle ilgili sorumluluğun büyük kısmını daha doğmadan ortadan kaldırdı — ve rakiplerinden en net ayrıştığımız nokta oldu.
Alan bazlı şifreleme ve erişim katmanları
Disk şifrelemesi tek başına bir güvenlik önlemi değildir; yalnızca diskin fiziksel olarak çalınmasına karşı korur. Uygulamanız veritabanına bağlandığı anda veri yine düz metin olarak akar. Özel nitelikli veri için bu yetmez — çünkü tehdit her zaman dışarıdan gelmiyor. Veritabanına meşru erişimi olan bir hesabın ele geçirilmesi, en sık görülen ihlal senaryosudur. Bu yüzden en hassas alanları, veritabanının hiç bilmediği anahtarlarla, uygulama katmanında şifreliyoruz.
Kullandığımız desen zarf şifreleme (envelope encryption): her hassas alan kendi tek kullanımlık veri anahtarıyla şifrelenir, o veri anahtarı da bir ana anahtarla — KMS'te tutulan, uygulamaya hiçbir zaman inmeyen kök anahtarla — şifrelenir. Veritabanına doğrudan erişen biri yalnızca şifreli baytları ve sarmalanmış veri anahtarını görür; çözmek için KMS'e ayrı bir yetkiyle gitmesi gerekir. Bu ayrım, "veritabanını gören her şeyi görür" varsayımını kökten kırar.
type DanisanKaydi struct {
ID string
AdSoyad Sifreli // özel nitelikli: alan bazlı şifreli
Email Sifreli // kişisel: alan bazlı şifreli
Sehir string // dahili: düz metin yeterli
}
// Sifreli tip, veritabanına yazılırken zarf şifreleme uygular
func (s Sifreli) Value() (driver.Value, error) {
veriAnahtari := kms.YeniVeriAnahtari() // tek kullanımlık
sifreli := aes.Sifrele(s.acikMetin, veriAnahtari)
sarili := kms.AnahtariSarmala(veriAnahtari) // ana anahtarla
return birlestir(sarili, sifreli), nil
}
Şifreleme verinin durağan hâlini korur; asıl trafiği ise erişim katmanları yönetir. İlkemiz en az yetki: her rol yalnızca işini yapmak için gereken veriyi, gerektiği kadar görür. Bunu üç katmanda uyguladık. Danışan yalnızca kendi verisine erişir. Terapist yalnızca kendisiyle eşleşmiş danışanların, yalnızca ilgili alanlarını görür — başka bir terapistin danışanı onun için yok hükmündedir. Destek ekibi bir kaydı açtığında e-posta ve telefonun yalnızca maskelenmiş hâlini görür; tam veri ancak belgelenmiş bir gerekçeyle ve iz bırakarak açılır. Yönetici hesapları iki faktörlü doğrulama olmadan hiç çalışmaz.
Bu katmanları veritabanı seviyesine dağıtmak yerine tek bir erişim kapısında topladık. Hassas veriye giden her yol bu kapıdan geçer; şifre çözme, maskeleme ve denetim kaydı burada, tek noktada uygulanır. Erişim mantığını yüzlerce sorguya dağıtırsanız, bir gün birinin bir yerde unutması kaçınılmazdır. Tek kapı, hem tutarlılığı hem de denetlenebilirliği garanti eder — ve neyi denetleyeceğinizi tam olarak bilmenizi sağlar.
Şifrelemeyi kurmak yetmez; anahtarların da bir yaşam döngüsü var. Anahtar rotasyonunu baştan planlamayan ekipler, bir gün anahtar sızdığında tüm veriyi üretim altında, panikle yeniden şifrelemek zorunda kalır. Zarf şifreleme burada da işimizi kolaylaştırdı: yalnızca ana anahtarı döndürüp veri anahtarlarını yeniden sarmalamak, milyonlarca satırı tek tek yeniden şifrelemekten çok daha ucuzdur. Anahtarları verinin durduğu yerden ayrı ve kısıtlı bir serviste tutmak, düzenli döndürmek ve rotasyonu bir kriz anına değil rutine bağlamak — bu tedbirin görünmeyen ama can alıcı yarısıdır. Şifreleme, yönetmediğiniz anahtar kadar güçlüdür.
Denetim izi: kanıtlanamayan güven yoktur
Hassas veriye kimin, ne zaman, neden eriştiğini kaydetmiyorsanız güvenliğiniz bir iddiadan ibarettir. Bir ihlal şüphesinde "o veriye kimse erişmedi" diyebilmeniz için, her erişimin değiştirilemez bir kaydı olması gerekir. Denetim izi, güvenin kanıtlanabilir hâlidir; onsuz en iyi mimari bile yalnızca bir umuttur. Bu yüzden denetim kaydını sonradan eklenen bir log değil, birinci sınıf bir mimari bileşen olarak tasarladık.
İyi bir denetim izinin tasarımında birkaç ilkeye titizlikle uyduk. Birincisi, kayıt yalnızca eklenebilir (append-only) olmalı — uygulama denetim kayıtlarını güncelleyemez veya silemez, yalnızca yeni satır ekleyebilir. İkincisi, kurcalamaya karşı korunmalı: her kaydı bir öncekinin özetiyle (hash) zincirliyoruz, böylece araya sonradan bir kayıt sokmak veya birini değiştirmek tüm zinciri kırar ve fark edilir. Üçüncüsü, kaydın kendisi hassas veri taşımamalı — neye erişildiğini takma kimlikle (pseudonym) tutuyoruz, kaydın içine gerçek veriyi koymuyoruz; aksi hâlde denetim izi kendisi bir sızıntı kaynağına dönüşür.
type DenetimKaydi struct {
Aktor string // erişen kullanıcı kimliği
Eylem string // oku / guncelle / sil
KayitTakma string // erişilen kaydın takma kimliği
Gerekce string // zorunlu: neden erişildi
Zaman time.Time
OncekiOzet string // bir önceki kaydın hash'i (zincir)
}
func (d *Denetim) Yaz(k DenetimKaydi) error {
if k.Gerekce == "" {
return errors.New("erişim gerekçesiz olamaz")
}
k.OncekiOzet = d.sonOzet // zinciri bağla
d.sonOzet = hesapla(k) // bir sonraki halka için
return d.depo.Ekle(k) // yalnızca ekle, asla güncelleme
}
Gerekçe alanının zorunlu olması küçük ama belirleyici bir karar. Bir destek çalışanı bir danışanın kaydını açacaksa, neden açtığını yazmak zorunda. Bu, hem sonradan denetim için kanıt bırakır hem de erişim anında insanı bir an durdurup düşündürür: gerçekten bakmam gerekiyor mu? Gerekçesiz erişimi teknik olarak imkânsız kıldık — gerekçe boşsa çağrı en baştan reddedilir. Caydırıcılık, çoğu zaman en ucuz güvenlik önlemidir.
Bir uyarı da yerinde olur: denetim izini herkesin her hareketini kaydeden bir gözetim aracına dönüştürmeyin. Amaç, hassas veriye erişimi izlemek — kullanıcının her tıklamasını değil. Aşırı loglama iki sorun doğurur: kayıt yığını içinde gerçek sinyali bulmak imkânsızlaşır ve denetim izinin kendisi devasa bir kişisel veri deposuna dönüşerek koruması gereken şeyi tehdit eder. Neyin kaydedileceğine, tam da neyi korumak istediğinizi düşünerek karar verin: özel nitelikli veriye her dokunuş kaydedilir, genel bir sayfanın görüntülenmesi kaydedilmez.
Denetim izinin gerçek değeri kötü günde ortaya çıkar. Bir ihlal şüphesi doğduğunda "hangi kayıtlara, kim, ne zaman erişti?" sorusunu dakikalar içinde yanıtlayabilmek ile günlerce log karıştırmak arasındaki fark, KVKK'nın 72 saatlik ihlal bildirim süresine yetişip yetişememektir. Kayıtları uygulamanın güncelleyemeyeceği, yalnızca üzerine ekleyebileceği ayrı bir depoda tutmak da bu güvencenin son halkasıdır. Denetim izi, ihlal anında panikle geçen günlerle, kontrollü bir müdahale arasındaki farktır.
KVKK'yı pratiğe dökmek: aydınlatma, silme, envanter
Şimdiye kadar anlattıklarımız mimariydi; ama KVKK uyumu aynı zamanda birkaç somut yükümlülüğü ürünün içinde yaşatmayı gerektirir. Bunları bir hukuk metni olarak değil, çalışan özellikler olarak ele aldık. Uyum, bir avukatın onayladığı belge değil, kullanıcının her gün etkileşime girdiği davranıştır.
Aydınlatma yükümlülüğü. Kanun, veriyi toplamadan önce kişiyi hangi verinin, hangi amaçla, ne kadar süre işleneceği konusunda bilgilendirmenizi ister. Bunu kayıt ekranında bir kez gösterilip geçilecek uzun bir metin olarak değil, her veri toplama noktasına bağlı canlı bir bilgilendirme olarak tasarladık. Danışan bir psikolojik ölçek doldururken de, ödeme yaparken de o an hangi verinin neden istendiğini görür. Açık rıza da aynı disiplinle: önceden işaretlenmiş kutucuk yok; rıza belirli, bilgilendirmeye dayalı ve özgür iradeyle verilir ve verildiği an denetim kaydına yazılır. Bir gün "bu kişi neye rıza verdi?" diye sorulduğunda, cevabı tahmin etmiyoruz, gösteriyoruz.
Silme talepleri. Veri sahibinin silinme hakkı bir e-posta kuyruğunda beklememeli. Kullanıcı hesap ayarlarından verisini indirebiliyor ve hesabını kapatma talebini kendisi başlatabiliyor. Ama silme, göründüğünden zordur: veri tek bir tabloda durmaz; yedeklerde, analitik toplamlarda, üçüncü parti servislerde izleri olabilir. Bu yüzden silmeyi kademeli bir süreç (cascade) olarak tasarladık — hangi sistemin hangi veriyi ne zaman sileceğini tanımlayan bir harita. İstatistik için gereken yerlerde kaydı silmek yerine kişiyle bağını geri dönülemez şekilde koparıp yalnızca anonim toplamı bırakıyoruz. Tersine çevrilebilen bir maskeleme anonimleştirme değildir ve sizi korumaz; gerçek anonimleştirme geri dönüşü imkânsız kılar.
Veri işleme envanteri. Belirli ölçekteki veri sorumluları VERBİS'e kaydolmak ve işleme envanterini güncel tutmakla yükümlü. Biz bu envanteri bir uyum zorunluluğu olarak değil, geliştiricinin elindeki en yararlı belge olarak görüyoruz: hangi veriyi, neden, ne kadar süre tuttuğumuzun canlı haritası. Yeni bir özellik yeni bir veri toplayacaksa, envanter güncellenmeden yayına çıkmaz. Bu belge minimizasyon tartışmalarında da, bir ihlalin kapsamını çıkarırken de elimizdeki ilk kaynaktır. İyi tutulan bir envanter, uyumu bir denetim kâbusu olmaktan çıkarıp rutin bir bakım işine dönüştürür.
Saklama süreleri: veriyi ne zaman yok edeceğinizi bilmek
Veriyi toplamak kadar, ondan ne zaman kurtulacağınızı bilmek de mimarinin parçası. Kanun, işleme amacı ortadan kalktığında verinin silinmesini, yok edilmesini veya anonim hâle getirilmesini ister. Bunu iyi niyete bırakmak en yaygın hatalardan biri: "gerektiğinde temizleriz" diyen ekipler, yıllar sonra hiç dokunulmamış, amacı çoktan yitmiş dev veri yığınlarının üzerinde oturur. Elle yapılan temizlik unutulur; unutulan veri de bir gün ihlalin yakıtı olur.
Bu yüzden her veri türü için bir saklama süresi tanımladık ve bu süreyi otomatik işleyen bir imha görevine bağladık. Ödeme kayıtları yasal zorunluluk gereği belli bir süre tutulur; oturum verisi günlerle ölçülür; pazarlama izni geri çekildiğinde ilgili veri hemen düşer. Süre dolduğunda kayıt sessizce ve otomatik silinir — kimsenin bir takvim hatırlatmasına uyup elle temizlik yapmasını beklemeyiz. Saklama süresini de hassasiyet etiketinin bir parçası hâline getirdik: bir alanı tanımlarken sınıfını girer gibi ömrünü de girersiniz; ömrü belirtilmemiş bir hassas alan şema doğrulamasından geçmez.
Saklama disiplininin görünmeyen bir faydası daha var: veriniz küçük kalır. Yalnızca aktif olarak gereken veriyi tutan bir sistem hem korunması daha kolay, hem sorgusu daha hızlı, hem de bir ihlalde kaybı daha sınırlı bir sistemdir. Az veri, az risk. Otuz yılda gördük ki en sağlıklı veritabanları en çok veriyi biriktirenler değil, gereksiz veriyi en disiplinli atanlardır. Silme, bir bakım işi değil, sürekli işleyen bir güvenlik önlemidir.
Üçüncü parti servis seçerken sorduğumuz sorular
Hiçbir ürün tek başına çalışmaz. Bulut sağlayıcısı, e-posta servisi, ödeme kuruluşu, hata izleme aracı — her biri sizin adınıza veri işleyen bir taraf ve her biri güven zincirinizin bir halkası. Zincir, en zayıf halkası kadar güçlüdür. Bir servisi kendi mimariniz kadar sıkı elemezseniz, onca emeği verdiğiniz iç güvenlik bir dış kapıdan sızar. Yeni bir servis eklemeden önce sorduğumuz sorular şunlar:
- Bu servise kişisel veri göndermek zorunda mıyız? İlk soru her zaman budur. Çoğu zaman veriyi hiç göndermeden, takma kimlikle veya toplulaştırarak işleyebiliyoruz. Göndermediğiniz veriyi o servisin korumasına emanet etmek zorunda kalmazsınız.
- Veri işleyen sözleşmesi imzalıyor mu? Servis, veriyi yalnızca sizin talimatınızla ve belirlenen amaç için işleyeceğini, aynı güvenlik önlemlerini alacağını yazılı olarak taahhüt etmeli. Bu sözleşme yoksa o servis bir seçenek değildir.
- Veri fiziksel olarak nerede duruyor? Verinin hangi ülkede saklandığı, yurt dışına aktarım kurallarını doğrudan etkiler. Hassas veri için altyapı bölgesini rastgele değil, veri yerleşimi kurallarını gözeterek seçiyoruz.
- Bağımsız bir güvenlik denetiminden geçmiş mi? Sağlayıcının güvenlik iddialarını kendi pazarlama sözüne değil, bağımsız denetim raporlarına dayandırmasını bekliyoruz. Söz ucuzdur, rapor değil.
- Çıkış maliyeti ne? Bir servise bağlanmadan önce ondan nasıl ayrılacağımızı, verimizi nasıl geri alıp taşıyacağımızı biliyoruz. Kilitlenme (lock-in), bir gün güvenlik gerekçesiyle ayrılmanız gerektiğinde bir tuzağa dönüşür.
Özellikle hata izleme ve analitik araçları sinsi bir risk taşır; farkında olmadan kişisel veriyi dışarı sızdırabilirler. Bir istisna yakalandığında hata raporuna eklenen istek gövdesi, içinde bir e-posta veya kimlik bilgisi taşıyabilir. Bu yüzden dışarı giden her veriyi, kişisel bilgiyi ayıklayan bir süzgeçten geçiriyoruz. Amaç değişmez: üçüncü taraf yalnızca işini yapmak için gereken en az veriyi görür, bir bayt fazlasını değil.
Sonuç: bu ilkeler yalnızca sağlık için değil
PsyBank'ı sağlık verisiyle çalışan bir platform olarak kurduk, ama bu yazıda anlattığımız hiçbir ilke sağlığa özgü değil. Veriyi sınıflandırın, ihtiyacınız olmayanı hiç toplamayın, en hassasını alan bazında şifreleyin, en az yetkiyle ve tek bir kapıdan açın, her erişimi değiştirilemez şekilde kaydedin, süresi dolanı otomatik silin ve kullanıcıya kendi verisi üzerinde gerçek kontrol verin. Bu liste bir fintech için de, bir İK yazılımı için de, sıradan bir e-ticaret için de aynen geçerli. Sağlık verisi sadece, bu ilkeleri atlamanın bedelini daha çabuk ve daha acı gösterdiği için iyi bir öğretmen.
Eğer bu ilkeleri var olan bir ürüne sonradan eklemeye çalışıyorsanız, size dürüst olalım: pahalı ve eksik olacak. Ama yeni bir şeye başlıyorsanız, bunların neredeyse tamamı bedelsizdir — yalnızca ilk günden karar vermeyi gerektirir. Güveni bir pazarlama vaadi olarak değil, bir mimari kısıt olarak ele alın. Çünkü kullanıcı en hassas verisini, ona layık olduğunu her erişimde yeniden kanıtlayan bir sisteme emanet eder; ve bir kez kırılan güven, hiçbir sürümde geri gelmez.