İçeriğe geç
IBAN Aracı
Teknik

IBAN Veritabanında Nasıl Saklanmalı? Şema, Şifreleme, Maske

IBAN'ı VARCHAR(34) sütuna elektronik biçimde yazmak işin kolay kısmı. Zor kısım şifreleme anahtarı, loglar, destek ekranı ve saklama süresi.

IBAN Aracı Editör Ekibi5 dk okuma

Sunucu odasında elinde tablet tutan kadın
Görsel: kaynak ve lisans bilgisi

IBAN'ı veritabanında boşluksuz, büyük harfli elektronik biçimde, en fazla 34 karakterlik bir metin olarak saklayın; sayı tipi kullanmayın, yazılı biçimdeki boşlukları kaydetmeyin. Bu kısmı bir dakikada çözersiniz. Asıl tasarım sorusu, IBAN bir gerçek kişiye bağlandığında başlar: kim görebilir, loglara ne düşer, veritabanı yedeği sızarsa ne olur? Aşağıdaki şema ve kod parçalarını SQLite ve Python 3.9 üzerinde çalıştırarak denedik.

Hukuki not: Bu yazı teknik bir rehberdir, hukuki görüş değildir. KVKK kapsamındaki yükümlülükleriniz işlediğiniz verinin türüne, amacına ve kurumunuzun yapısına göre değişir; kararları hukuk danışmanınızla birlikte verin.

Biçim: tek bir kanonik değer

Aynı IBAN, kullanıcı arayüzünde TR14 0001 0050 0123 4567 8901 23, bir CSV'de tr140001005001234567890123 olarak gelebilir. Veritabanına bunlardan yalnızca biri yazılmalı: harf ve rakam dışındaki her şeyi atılmış, büyük harfe çevrilmiş hâl. İki gösterim arasındaki farkı yazılı ve elektronik format rehberinde anlattık. Kanonik değer olmazsa tekrar kontrolü çalışmaz; aynı tedarikçi iki kez kaydolur, ödeme iki kez çıkar.

Sütun tipi için VARCHAR(34) yeterli. ISO 13616 IBAN'ın en fazla 34 karakter olabileceğini tanımlar; Türkiye IBAN'ı 26 karakterdir. BIGINT ya da DECIMAL düşünmeyin bile: IBAN ülke kodu ve bazı ülkelerde hesap bölümünde harf içerir, üstelik sayısal tipte baştaki sıfırlar kaybolur. Sabit uzunluklu CHAR(34) da gereksiz: kısa IBAN'ların sağını boşlukla doldurur ve uygulamaya dönen değeri her seferinde kırpmanızı gerektirebilir.

Uygulama katmanındaki doğrulamaya ek olarak veritabanına basit bir kısıt koymak, başka bir servisin ya da elle yapılan bir düzeltmenin bozuk değer yazmasını engeller:

sql (sqlite)
CREATE TABLE payee_account_plain (
  id    INTEGER PRIMARY KEY,
  iban  VARCHAR(34) NOT NULL
        CHECK (length(iban) BETWEEN 15 AND 34
               AND iban = upper(iban)
               AND iban NOT GLOB '*[^A-Z0-9]*')
);

Bu kısıt boşluklu, küçük harfli, tireli ve 15-34 aralığı dışındaki değerleri reddeder; denemelerimizde bu beş durumun hepsi CHECK constraint failed hatası verdi. MOD 97'yi veritabanında hesaplamaya çalışmayın. SQL'de yazmak mümkün ama okunması ve test edilmesi zor; bu iş uygulama katmanına ait. Kodu için Python ile IBAN doğrulama yazısına bakabilirsiniz. PostgreSQL'de GLOB yerine düzenli ifade operatörüyle aynı kısıtı kurabilirsiniz.

IBAN kişisel veri mi?

6698 sayılı Kanun kişisel veriyi kimliği belirli veya belirlenebilir gerçek kişiye ilişkin her türlü bilgi olarak tanımlar. Bir IBAN tek başına isim içermez, ama müşteri kaydınızda bir ad-soyadın yanında duruyorsa o kişiyle ilişkilendirilmiş bir bilgidir. Pratikte sisteminizdeki bireysel müşteri IBAN'larını kişisel veri gibi ele almak en güvenli varsayımdır.

Kanunun 12. maddesi veri sorumlusuna kişisel verilerin hukuka aykırı işlenmesini ve bunlara hukuka aykırı erişilmesini önlemek, verilerin muhafazasını sağlamak için uygun güvenlik düzeyini temin eden teknik ve idari tedbirleri alma yükümlülüğü getirir. Kanun tek bir teknik reçete vermez. Kişisel Verileri Koruma Kurumu'nun yayımladığı Kişisel Veri Güvenliği Rehberi ise teknik tedbirler arasında yetki matrisi, erişim logları, şifreleme, log kayıtları, veri maskeleme ve anahtar yönetimini sayar. Aşağıdaki bölümler bu başlıkların bir IBAN sütununda somut olarak neye karşılık geldiğini gösteriyor.

Önce sorun: tam IBAN'ı gerçekten saklamanız gerekiyor mu?

OWASP'ın kriptografik saklama rehberinin ilk tavsiyesi, hassas veriyi en iyi korumanın onu hiç saklamamak olduğudur. Bir iade akışında IBAN'ı yalnızca talimat oluşturulurken kullanıp saklamıyorsanız, şifreleme ve anahtar yönetimi derdinin tamamından kurtulursunuz. Kullanıcıya "kayıtlı hesabınız" göstermek için tam IBAN değil, ülke kodu ile son dört hane yeter. Tam IBAN gerekiyorsa, örneğin düzenli tedarikçi ödemesinde, saklama süresini de baştan belirleyin. Kanun, işlenme sebebi ortadan kalkan kişisel verinin silinmesini, yok edilmesini veya anonim hâle getirilmesini öngörüyor; tedarikçi ilişkisi bittiğinde IBAN'ın ne olacağına dair bir kural yazın.

Şifreleme: disk şifrelemesi yetmez mi?

Disk, dosya sistemi ya da veritabanı düzeyindeki şifreleme (SQL Server'daki TDE gibi) çalınan bir diske veya yedek dosyasına karşı korur. Ama SQL enjeksiyonu ya da fazla yetkili bir raporlama kullanıcısına karşı hiçbir şey yapmaz; veritabanı sorguya cevap verirken veri zaten açıktır. OWASP rehberi de şifrelemenin hangi katmanda yapılacağının tehdit modeline bağlı olduğunu vurgular. IBAN için makul bir hedef, uygulama katmanında alan bazında şifreleme ve anahtarın veriden ayrı tutulmasıdır.

python
import os

from cryptography.hazmat.primitives.ciphers.aead import AESGCM


def encrypt_iban(iban: str, key: bytes, customer_id: int) -> bytes:
    nonce = os.urandom(12)
    aad = str(customer_id).encode()  # şifreli değer başka müşterinin satırına taşınamasın
    return nonce + AESGCM(key).encrypt(nonce, iban.encode("ascii"), aad)


def decrypt_iban(blob: bytes, key: bytes, customer_id: int) -> str:
    nonce, ciphertext = blob[:12], blob[12:]
    return AESGCM(key).decrypt(nonce, ciphertext, str(customer_id).encode()).decode("ascii")

Örnek, cryptography paketinin AES-GCM uygulamasını kullanıyor. GCM kimlik doğrulamalı bir moddur: şifreli metin değiştirilirse çözme işlemi hata verir. Ek doğrulanmış veri olarak müşteri kimliğini bağladık; böylece bir müşterinin şifreli IBAN'ı veritabanında başka bir müşterinin satırına kopyalanırsa çözülemez. Testte 42 numaralı müşteri için şifrelenmiş değeri 43 ile çözmeye çalıştığımızda InvalidTag aldık. Her şifrelemede yeni rastgele nonce üretildiği için aynı IBAN iki kez şifrelendiğinde farklı bayt dizisi çıkar.

Anahtarı veritabanında, aynı sunucudaki bir yapılandırma dosyasında ya da kaynak kodda tutmayın. Bulut sağlayıcınızın anahtar yönetim servisi veya ayrı bir sır yönetim aracı kullanın ve yalnızca ödeme servisinin o anahtara erişebildiğinden emin olun. Anahtar değişimini de ilk günden düşünün: şifreli değerin başına bir anahtar sürümü eklemek, eski kayıtları kademeli olarak yeni anahtara taşımayı mümkün kılar.

Şifreli sütunda arama: anahtarlı parmak izi

Şifreli sütunun bedeli, WHERE iban = ? sorgusunun artık çalışmamasıdır; aynı IBAN her seferinde farklı şifrelenir. Tekrar kontrolü için yanına anahtarlı bir özet (HMAC) sütunu eklenir:

sql (sqlite)
CREATE TABLE payee_account (
  id            INTEGER PRIMARY KEY,
  customer_id   INTEGER NOT NULL,
  iban_cipher   BLOB    NOT NULL,          -- uygulamada şifrelenmiş tam IBAN
  iban_hmac     TEXT    NOT NULL,          -- arama/tekrar kontrolü için anahtarlı özet
  country_code  TEXT    NOT NULL CHECK (length(country_code) = 2),
  iban_last4    TEXT    NOT NULL CHECK (length(iban_last4) = 4),
  created_at    TEXT    NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE (customer_id, iban_hmac)
);
python
import hashlib
import hmac
import os


def iban_fingerprint(iban: str, key: bytes) -> str:
    """Normalize edilmiş IBAN için anahtarlı özet (HMAC-SHA256).

    Aynı IBAN her zaman aynı özeti verir; böylece şifreli kolonu
    çözmeden tekrar kontrolü ve eşitlik araması yapılabilir.
    """
    return hmac.new(key, iban.encode("ascii"), hashlib.sha256).hexdigest()


def mask_for_log(iban: str) -> str:
    """Log ve destek ekranı için: ülke kodu + son 4 hane."""
    if len(iban) < 8:
        return "****"
    return f"{iban[:2]}** **** {iban[-4:]}"


key = bytes.fromhex(os.environ["IBAN_HMAC_KEY"])  # anahtar veritabanında değil
iban = "TR140001005001234567890123"
print(mask_for_log(iban))
print(iban_fingerprint(iban, key) == iban_fingerprint("TR140001005001234567890123", key))

Neden düz SHA-256 değil de HMAC? IBAN'ın yapısı tahmin edilebilirdir: ülke kodu sabit, banka kodları kamuya açık bir listede, kontrol haneleri de hesaplanabilir. Anahtarsız bir özet, yeterince işlem gücü olan birinin olası IBAN'ları özetleyip eşleştirmesine açıktır. HMAC anahtarı, şifreleme anahtarından ayrı tutulduğunda bu saldırıyı anahtara erişime bağlar. SQLite testimizde aynı müşteri için aynı IBAN'ı ikinci kez eklemek UNIQUE constraint failed hatasıyla reddedildi ve özet üzerinden yapılan aramayla kayıt bulunup çözüldü.

Loglar, hata izleme ve destek ekranı

IBAN'ların en sık sızdığı yer veritabanı değil, loglardır. İstek gövdesini olduğu gibi yazan bir HTTP logu, bir hata izleme servisine giden istisna mesajı ya da "debug için" eklenmiş bir print satırı, şifrelediğiniz sütunu anlamsız kılar. OWASP'ın loglama rehberi banka hesabı verisini doğrudan loglanmaması gerekenler arasında sayar ve bu verinin çıkarılmasını, maskelenmesini, özetlenmesini veya şifrelenmesini önerir.

Pratikte üç kural işe yarar. Log formatlayıcınıza IBAN kalıbını yakalayıp maskeleyen bir filtre ekleyin. İstisna mesajlarına IBAN koymayın, yerine kayıt kimliği yazın. Destek ekibinin ekranında yukarıdaki mask_for_log çıktısı gibi kısmi bir gösterim kullanın; tam IBAN'ı görmek ayrı bir yetki ve kayıt altına alınan bir işlem olsun. Maskelemenin hangi kısımları açık bıraktığı, amaca göre değişir; IBAN nasıl maskelenir? rehberinde seçenekleri karşılaştırdık.

Erişim kontrolü ve yedekler

Şifreleme anahtarına erişebilen servis sayısı, IBAN'ı görebilen kişi sayısını belirler. Raporlama veritabanına, analitik ambarına ya da test ortamına üretim verisi kopyalanıyorsa şifreli sütun ve özet oraya gidebilir, ama anahtar gitmemeli. Test ortamında gerçek IBAN yerine IBAN oluşturucu ile üretilmiş, biçimsel olarak geçerli sahte değerler kullanın. Yedekler de aynı kurala tabi: yedeği geri yükleyen kişi anahtara sahip değilse şifreli sütun onun için anlamsız bayt dizisidir. Bu düzen, olası bir ihlalde neyin gerçekten açığa çıktığını değerlendirmeyi de kolaylaştırır.

Son olarak, doğrulanmış biçimde saklanan bir IBAN'ın hâlâ yanlış kişiye ait olabileceğini unutmayın. Saklama katmanı verinin bütünlüğünü korur, doğruluğunu kanıtlamaz. Tedarikçiden gelen bir IBAN değişikliği talebini veritabanına yazmadan önce bilinen bir kanaldan teyit etmek, en iyi şifrelemeden daha çok para korur.

Kaynaklar

  1. KVKK: Veri Güvenliğine İlişkin Yükümlülükler (6698 s. Kanun md. 12)
  2. KVKK: Kişisel Veri Güvenliği Rehberi (Teknik ve İdari Tedbirler)
  3. OWASP Cheat Sheet Series: Cryptographic Storage
  4. OWASP Cheat Sheet Series: Logging
  5. cryptography belgeleri: AEAD (AESGCM)
  6. Swift: IBAN (ISO 13616) ve IBAN Registry

Yazıların nasıl hazırlanıp güncellendiğini metodoloji sayfasında anlatıyoruz. Hata gördüyseniz bize yazın.

Sık sorulan sorular

Hayır. Şifreleme, Kurum rehberinde sayılan teknik tedbirlerden yalnızca biridir. Aydınlatma, işleme şartları, saklama süresi, erişim yetkileri ve ihlal bildirimi gibi yükümlülükler ayrıca değerlendirilir. Kendi durumunuz için hukuk danışmanına başvurun.

İlgili yazılar

İlgili araçlar