Bilgi Güvenliği

KVKK Uyumlu Yazılım Geliştirme: .NET Geliştiricisi İçin Kontrol Listesi

Ürfet Demirtaş 4 dk okuma

KVKK uyumu bir gizlilik politikası sayfasıyla bitmez. Geliştiricinin işi, kişisel verinin hangi tabloda durduğunu, loga düşüp düşmediğini ve silme isteğinde gerçekten silinip silinmediğini bilmektir.

Bu yazı hukuk danışmanlığı değildir. 6698 sayılı Kanun'da veri sorumlusunun hukuki dayanağını, açık rızanın gerekip gerekmediğini ve VERBİS yükümlülüğünü avukat ile veri sorumlusu seçer. Aşağıdaki liste, o karar verildikten sonra .NET uygulamasında teknik olarak kontrol edilmesi gereken yerlerdir. "Gizlilik sayfamız var" cümlesi bu listenin hiçbir maddesini kapatmaz.

Veri nerede duruyor

İlk iş envanter. Tabloları dolaşın ve şu alanları işaretleyin: ad, soyad, T.C. kimlik numarası, telefon, e-posta, adres, IBAN, doğum tarihi, IP, cihaz kimliği, konum, personel özlük, sağlık. Serbest metin alanını atlamayın. "Not" ve "açıklama" kolonuna kimlik numarası yapıştırıldığını canlı sistemde görürsünüz.

Toplanmayan veri korunmuş veridir. Ekranda kullanmadığınız telefonu, "ileride lazım olur" diye tabloya yazmayın. Amaç değişince eski amaçla toplanmış veri yeni amaç için kullanılmaz. Bunu kodda karşılığı şudur: analitik olayına kimlik numarası, sipariş loguna kart numarası, hata kaydına şifre koymazsınız.

Özel nitelikli veri ayrıdır. Sağlık, biyometrik veri, sendika, din gibi alanlar aynı tabloda "bir kolon daha" değildir. Uygulama bunları tutmuyorsa hiç açmayın. Tutuyorsa erişim, genel müşteri kaydından daha dar olsun.

Teknik kontrol listesi

  • Her kişisel veri alanı için tablo, amaç ve saklama süresi yazılı olsun. Süreyi kodda da uygulayın. "Süresiz tutuyoruz" bir süre değildir.
  • Ortamlar ayrılsın. Production dump'ı geliştirici makinesine inmesin. Test verisi maskeli veya sentetik olsun.
  • Veritabanı kullanıcısı db_owner olmasın. Uygulamanın ihtiyacı olan tablo ve işlemle sınırlı kalsın. Bunu ayrı yazıda kurmuştum.
  • Yönetim ekranında kim, hangi kaydı, ne zaman gördü. Bu iz, sonradan "veri sızdı mı" sorusunun tek cevabıdır.
  • Yedeklerin de süresi olsun. Canlı tablodan silinen satır, üç yıllık yedekte duruyorsa silinmiş sayılmaz.
  • Şifreleme, listenin tamamı değildir. Alan şifreli diye amaçsız toplama meşru olmaz. Yine de T.C. kimlik numarası ve IBAN gibi alanlar düz metin durmamalı. Algoritma tarafı AES ve SHA yazısında, dosya tarafı .NET ile şifreleme yazısında.

Loga düşen veri, sistemde duran veridir

En sık kaçan yer logdur. Exception middleware'i istek gövdesini, Serilog bağlantı cümlesini, health check yanıtı içindeki kullanıcıyı basar. Log sunucusu, uygulamanın kopyası haline gelir. Üstelik erişimi daha gevşektir.

public static class LogRedaction
{
    private static readonly HashSet<string> Sensitive =
        new(StringComparer.OrdinalIgnoreCase)
        {
            "tckn", "tcKimlikNo", "password", "sifre",
            "cardNumber", "pan", "cvv", "iban"
        };

    public static string Scrub(string key, string? value)
    {
        if (string.IsNullOrEmpty(value))
            return value ?? "";

        return Sensitive.Contains(key) ? "***" : value;
    }
}

Anahtar listesi yetmez. Gövdeyi olduğu gibi LogInformation("{Body}", requestBody) diye yazarsanız, alan adı listesi hiç çalışmaz. Ödeme, kimlik ve üyelik uçlarında gövdeyi loglamayın. Hata ayıklamak için gereken şey, gövdenin kendisi değil, korelasyon numarası ve iş kodudur.

Ekranda da tam kimlik göstermeyin. Liste ekranında maske yeter.

public static string MaskNationalId(string? value)
{
    if (string.IsNullOrWhiteSpace(value) || value.Length < 5)
        return "***";

    var hidden = new string('*', value.Length - 5);
    return value[..3] + hidden + value[^2..];
}

Ofiste kâğıt imha kutusuna dolmuş ince kâğıt şeritleri

Silme isteği bir bayrak değildir

IsDeleted = 1 silmek değildir. Satır durur, yedek durur, arama indeksinde durur, e-posta sağlayıcısının listesinde durur, obje deposundaki PDF durur, replikada durur. Silme işini bir uygulama servisi yapsın ve şu hedefleri tek tek kapatsın: canlı tablo, ilişkili adres ve özlük satırı, arama indeksi, dosya deposu, önbellek. Yedek için ayrı bir cümle kurun: yedek şu kadar gün tutulur, o günden sonra imha edilir. Bunu söyleyemiyorsanız silme talebine dürüst cevap veremezsiniz.

İlişkili kayıtta mali yükümlülük varsa satırın tamamı her zaman uçmaz. Fatura, kanunen saklanan bir belgedir. O zaman kimlik alanları ayrılır, mali satır kalır, kişiye bağlayan alan silinir veya ayrıştırılır. Bunu "hepsini silelim" diye kodlamak da, "hiçbirini silmeyelim" diye kodlamak da yanlıştır. Veri sorumlusu hangi kaydın hangi süreyle kaldığını söyler. Siz o süreyi job'a yazarsınız.

Dış servis ve yurt dışı

SMTP, analitik, hata izleme, canlı destek, ödeme kuruluşu ve genel amaçlı bir LLM, veriyi sizin sürecinizden çıkaran taraflardır. Her biri için şu üç soru cevaplanmadan SDK eklemeyin: hangi alan gidiyor, sunucu hangi ülkede, sözleşme veri işleyen sıfatını kapsıyor mu.

2024'teki değişiklikten sonra yurt dışına aktarım, "açık rıza kutusunu işaretlettik" diye kapanmıyor. Yeterlilik kararı, standart sözleşme veya bağlayıcı şirket kuralları gibi bir yol gerekir. Geliştirici olarak sizin somut işiniz, baytın hangi hosta gittiğini bilmektir. SDK'nın varsayılan uç noktası çoğu zaman ABD veya AB bölgesidir. Bölgeyi seçmeden paketi eklemek, aktarımı fark etmeden yapmaktır.

Müşteri sözleşmesini, personel özlüğünü veya destek ekine gelen kimlik fotoğrafını genel bir sohbete yapıştırmak bu aktarımın ta kendisidir. Şirket belgesi şirket içinde kalacaksa model de şirket içinde çalışır. Kurulumu local LLM ve Türkçe RAG yazısında anlattım. Editöre yapıştırılan production ayarı da aynı kapıya çıkar. Cursor ve Claude yazısındaki sınır buradan gelir.

Kart verisi KVKK'dan dar bir alandır

Kart numarası kişisel veridir. CVV'yi saklamak ise PCI-DSS tarafında ayrıca yasaktır. "Şifreli kolon" bunu meşru yapmaz. Ödeme geçidinde kartı siz tutmazsınız. Bankanın veya ödeme kuruluşunun verdiği referans ve maskeli son dört hane tutulur. Tam PAN ve CVV loga, tabloya, kuyruğa, hiçbiri yazılmaz. Geçidin kendisini kurarken asıl işin kod değil güven olduğunu ödeme geçidi yazısında anlatmıştım. OWASP listesindeki veri sızıntısı maddesi de aynı yere bağlanır: OWASP Top 10.

İhlalde geliştiriciye sorulan soru

Kurul'a bildirim, veri sorumlusunun işidir. Süre kısadır. O sürede geliştiriciden gelen cevap net olmalı: hangi alan, hangi tarihten beri, hangi sistemde, kim erişmiş, yedekte hâlâ var mı. Bu dört cümleyi log ve erişim izi olmadan kuramazsınız. Listeyi bu yüzden politika dosyasına değil, tabloya, loga ve silme job'ına yazın.

Tüm yazılar →