Database Engineering

PostgreSQL ile Adli Denetim İzleri Oluşturma

Veritabanı mühendisliği alanında güven opsiyonel değildir; temel taştır. Finansal işlemler, sağlık kayıtları veya yasal belgelerle uğraşırken, neyin, ne zaman ve kim tarafından yapıldığını kanıtlama yeteneği kritik öneme sahiptir. İşte adli düzeyde denetim izleri burada devreye girer. Geleneksel günlük (log) yöntemleri genellikle yetersiz kalır çünkü günlükler değiştirilebilir ve kolayca manipüle edilebilir. Gerçek bütünlüğü sağlamak için, sağlam bir ilişkisel veritabanı olan PostgreSQL tarafından desteklenen değişmez olay kaynağı (event sourcing) modelini benimsememiz gerekir.

Bu yazı, denetim verilerinizin manipülasyona karşı kanıtlanabilir ve sıkı düzenleyici standartlara uygun olmasını sağlayarak PostgreSQL kullanarak değişmez bir olay deposunun nasıl uygulanacağını inceler.

Uyumluluk İçin Değişmezliğin Önemi

GDPR, HIPAA ve SOC 2 gibi uyumluluk çerçeveleri, kuruluşların veri bütünlüğünü korumasını gerektirir. Denetim günlüğündeki standart bir güncelleme veya silme işlemi, bir yazım hatası olsa bile bir örtbas girişimi olarak yorumlanabilir. Denetim izini değişmez hale getirerek, bir olay kaydedildikten sonra değiştirilememesi veya kaldırılamamasını sağlarız. Bu, veri değişiklikleri için bir "sorumluluk zinciri" oluşturarak geliştiricilere ve denetçilere tek bir doğruluk kaynağı sağlar.

Bunu PostgreSQL'de uygulamak, geleneksel CRUD (Oluştur, Oku, Güncelle, Sil) işlemlerinden Tek-Yönlü (Append-Only) stratejiye bir geçiş gerektirir. AFTER tetikleyicileri ve UNLOGGED tablolar gibi PostgreSQL özelliklerini dikkatli bir şekilde kullanacağız, ancak adli amaçlar için sıkı kısıtlamalara sahip standart, kalıcı tablolarla kalacağız.

Olay Deposu Şemasının Tasarımı

Çözümümüzün kalbinde, olayları depolamak için ayrılmış bir tablo bulunur. Her olay, iş mantığındaki bir durum değişikliğini temsil eder. İzlenebilirliği sağlamak için her kayıt, benzersiz bir tanımlayıcı, bir zaman damgası, sorumlu kullanıcı veya sistem ve değişikliğin yükünü (payload) içermelidir.

CREATE TABLE audit_events (
    event_id BIGSERIAL PRIMARY KEY,
    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    user_id UUID NOT NULL,
    action VARCHAR(50) NOT NULL,
    entity_type VARCHAR(50) NOT NULL,
    entity_id VARCHAR(100) NOT NULL,
    old_state JSONB,
    new_state JSONB,
    metadata JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Bu tabloda güncelleme veya silmenin mümkün olmadığını sağlayın
ALTER TABLE audit_events 
  DROP CONSTRAINT IF EXISTS audit_events_pkey,
  ADD CONSTRAINT audit_events_pkey PRIMARY KEY (event_id);

-- Standart roller için tüm güncelleme ve silme izinlerini devre dışı bırakın
REVOKE UPDATE, DELETE ON audit_events FROM PUBLIC;
GRANT INSERT, SELECT ON audit_events TO app_user;

UPDATE ve DELETE ayrıcalıklarını veritabanı düzeyinde geri alarak, uygulama mantığı başarısız olsa bile değişmezliği zorunlu kılıyoruz. Bu, adli bütünlük için kritik olan derinlemesine savunma stratejisidir.

Denetim Tetikleyicisinin Uygulanması

Her denetim olayı için manuel olarak ekleme (insert) ifadeleri yazmak yerine, değişiklikleri otomatik olarak yakalamak için PostgreSQL tetikleyicilerinden yararlanabiliriz. Bu, tekrarlayan kodu azaltır ve insan hatasını en aza indirir. Örneğin, bir kullanıcı users tablosundaki bir kaydı güncellediğinde, bir tetikleyici otomatik olarak ateşlenebilir ve eski ve yeni durumları yakalayabilir.

CREATE OR REPLACE FUNCTION capture_user_changes()
RETURNS TRIGGER AS $$
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        INSERT INTO audit_events (user_id, action, entity_type, entity_id, old_state, new_state)
        VALUES (
            NEW.updated_by,
            'UPDATE',
            'users',
            NEW.id::TEXT,
            row_to_json(OLD)::jsonb,
            row_to_json(NEW)::jsonb
        );
        RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        INSERT INTO audit_events (user_id, action, entity_type, entity_id, old_state)
        VALUES (
            NEW.updated_by,
            'DELETE',
            'users',
            OLD.id::TEXT,
            row_to_json(OLD)::jsonb
        );
        RETURN OLD;
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER audit_user_changes
    AFTER UPDATE OR DELETE ON users
    FOR EACH ROW
    EXECUTE FUNCTION capture_user_changes();

Değişmez Günlüğün Sorgulanması

PostgreSQL'in güçlü yönlerinden biri JSONB desteğidir. Tüm satırları taramadan, belirli değişiklikler için denetim izimizi verimli bir şekilde sorgulayabiliriz. Örneğin, belirli bir alandaki tüm değişiklikleri bulmak basittir:

SELECT *
FROM audit_events
WHERE entity_type = 'users'
  AND entity_id = '123e4567-e89b-12d3-a456-426614174000'
  AND new_state->>'email' != old_state->>'email';

Bu sorgu, uyumluluk görevlilerinin bir e-posta adresinin ne zaman, kim tarafından ve önceki değerin ne olduğunu hızla belirlemesini sağlar.

Sonuç

Adli düzeyde denetim izleri oluşturmak sadece veri depolamakla ilgili değildir; güven tesis etmekle ilgilidir. PostgreSQL'in sağlam işlemsel (transactional) yeteneklerini değişmez olay kaynağı deseniyle birleştirerek, geliştiriciler manipülasyona karşı dirençli ve en sıkı düzenlemelere uygun sistemler oluşturabilir. Bu yaklaşım yazma işlemlerine karmaşıklık katarken, güvenlik ve uyumluluk açısından sağladığı huzur paha biçilemezdir. Veri gizliliği endişeleri arttıkça, değişmez günlükler sadece bir iyi uygulama değil, standart bir gereklilik haline gelecektir.

Share: