Veri tabanı ve elektronik kayıt incelemesi

Veri tabanı incelemesi, bir kaydın ne zaman oluşturulduğunu, değiştirildiğini ya da silindiğini ve bu işlemin hangi kullanıcı veya uygulama üzerinden yapıldığını, tablodaki tarih alanlarıyla yetinmeden veri tabanının işlem kayıtlarına, audit kayıtlarına, yedeklere ve uygulama loglarına dayanarak değerlendiren teknik incelemedir.

Güncellendi:

Kısaca

  • created_at ve updated_at gibi alanları çoğunlukla uygulama yazar ve yetkili bir kullanıcı doğrudan değiştirebilir; bağımsız teyit işlem kayıtlarından gelir.
  • MySQL binary log varsayılan ayarda 30 gün sonra silinebilir; PostgreSQL'de VACUUM, SQL Server'da log truncation eski izleri ortadan kaldırır. Zaman aleyhinize işler.
  • Veri tabanındaki kullanıcı çoğu zaman uygulamanın tek bağlantı hesabıdır; işlemi hangi kişinin yaptığını uygulama logları gösterir.
  • Silinen her kaydın geri getirilebileceğini söyleyemeyiz; bu, saklama ayarlarına ve geçen süreye bağlıdır.
Bu sayfada
  1. Kayıttaki tarih alanı neden tek başına yeterli değildir?
  2. Veri tabanları değişiklikleri nerede kaydeder?
  3. NoSQL veri tabanlarında durum farklı mı?
  4. Audit kayıtları ve kullanıcı hareketleri
  5. Silinen ve değiştirilen kayıtlar değerlendirilebilir mi?
  6. Veri bütünlüğü nasıl değerlendirilir?
  7. Veri tabanı ve uygulama logları nasıl karşılaştırılır?
  8. Materyal nasıl alınmalı?
  9. Raporda neler yer alır?

Sipariş ve fatura kayıtları, cari hesap hareketleri, stok girişleri, personel giriş-çıkış (PDKS) kayıtları, randevu ve abonelik onayları, bir ERP'deki onay akışı: hepsi bir veri tabanında satır olarak durur. Uyuşmazlık genellikle dört sorudan biriyle gelir. Bu kayıt iddia edilen tarihte var mıydı? Sonradan değiştirildi mi? Silinen kayıt var mı? Değişikliği kim, hangi yoldan yaptı?

Kayıttaki tarih alanı neden tek başına yeterli değildir?

Çünkü o alan, kaydın geçmişini değil, en son yazılan değeri gösterir ve onu yazan da çoğunlukla incelenen uygulamanın kendisidir.

  • Kim yazar: created_at değeri ya uygulama kodundan ya da veri tabanının varsayılan değerinden (DEFAULT CURRENT_TIMESTAMP) gelir. İlkinde uygulama sunucusunun, ikincisinde veri tabanı sunucusunun saati kullanılmıştır.
  • Değiştirilebilir: Yetkili bir hesapla çalıştırılan tek bir UPDATE ile tarih alanı başka bir değere çekilebilir ve tablo bakıldığında hiçbir şey olağan dışı görünmez.
  • Saat dilimi: MySQL, TIMESTAMP türündeki değerleri bağlantının saat diliminden UTC'ye çevirerek saklar ve okurken geri çevirir; DATETIME için bunu yapmaz. Aynı kayıt, farklı saat dilimi ayarıyla bağlanan iki araçta farklı saat gösterebilir. Ayrıntı: zaman damgaları ve saat dilimi.
  • Birim: Tamsayı sütunlarda saniye, milisaniye ya da mikrosaniye cinsinden Unix zamanı tutulabilir; birim, bilinen bir test kaydıyla doğrulanmadan okunmaz. Tek tek değerleri zaman damgası dönüştürücüyle çevirebilirsiniz.

Bu yüzden tarih alanını bir beyan olarak ele alır, doğrulamasını veri tabanının kendi işlem kayıtlarında ararız.

Veri tabanları değişiklikleri nerede kaydeder?

SQL veri tabanlarının çoğu, veriyi değiştiren işlemleri kurtarma ve replikasyon amacıyla ayrı bir işlem kaydına (transaction log) yazar. Adli açıdan bu kayıt, satırın geçmişine en yakın bağımsız kaynaktır; ne kadar geriye gittiği ise ürüne ve ayarlara bağlıdır.

Sistemİşlem kaydıİnceleme açısından dikkat
MySQLBinary log (mysqlbinlog ile okunur)Yalnızca veriyi değiştiren işlemler; SELECT kaydedilmez. binlog_expire_logs_seconds varsayılanı 2.592.000 saniye (30 gün). Tüm sorguları tutan general query log varsayılan olarak kapalıdır.
PostgreSQLWAL (pg_waldump)Arşivleme (archive_mode) açık değilse gereksiz hâle gelen WAL dosyaları checkpoint sonrasında silinir ya da yeniden adlandırılıp kullanılır; wal_keep_size ve replikasyon yuvaları daha fazlasını tutabilir. UPDATE ve DELETE eski satır sürümünü hemen silmez; VACUUM çalışana kadar fiziksel sayfalarda kalabilir.
SQL ServerTransaction logSimple recovery modelinde checkpoint sonrasında, full modelde log yedeğinden sonra log alanı boşaltılır (truncation). fn_dblog belgelenmemiş bir fonksiyondur; çıktısı dikkatli yorum ister.
OracleRedo ve arşivlenmiş redo (LogMiner, V$LOGMNR_CONTENTS)Supplemental logging açık değilse bazı ayrıntılar eksik kalır.
SQLite (mobil ve masaüstü uygulamalar)-wal dosyasıVeri tabanı -wal ve -shm dosyalarıyla birlikte kopyalanmalı; orijinali açmak checkpoint tetikleyip WAL'ı silebilir. secure_delete genelde kapalıdır, silinen içerik serbest sayfalarda kalabilir.
MongoDBOplog (local.oplog.rs)Replika set üyelerinde tutulur. Boyutla sınırlıdır: Linux ve Windows'ta WiredTiger ile varsayılan boyut boş disk alanının %5'i (en az 990 MB, en fazla 50 GB); dolunca en eski girdiler silinir, ayrıca saat cinsinden asgari saklama süresi tanımlanmış olabilir.

NoSQL veri tabanlarında durum farklı mı?

Çoğu zaman daha zordur, çünkü değişiklik geçmişi ürüne ve kurulum tercihine göre çok değişir. MongoDB'de oplog yalnızca replika set yapısında vardır; tek sunuculu, replika set olarak yapılandırılmamış bir kurulumda bu iz bulunmaz. Belgelerin _id alanındaki ObjectId değerinin ilk 4 baytı, değerin oluşturulduğu anı Unix zamanı cinsinden saniye olarak taşır. Bu yararlı bir işarettir ama iki sınırı var: ObjectId'yi çoğunlukla istemci tarafındaki sürücü üretir (yani uygulama sunucusunun saatini yansıtır) ve uygulama _id alanına istediği değeri yazabilir.

Anahtar-değer depoları, arama motorları ve bulut belge veri tabanlarında hangi geçmişin tutulduğu ürüne ve ayara göre değişir. Ön incelemede ilk sorduğumuz şey, sistemin değişiklik geçmişini nerede ve ne kadar süreyle tuttuğudur.

Audit kayıtları ve kullanıcı hareketleri

Audit kaydı, kimin hangi veriye ne zaman eriştiğini veya onu değiştirdiğini tutan kayıttır; ancak "kim" sorusunun cevabı kaydın hangi katmanda tutulduğuna bağlıdır.

Web ve kurumsal uygulamaların çoğu veri tabanına tek bir hizmet hesabıyla (ör. app_user) bağlanır. Veri tabanının kendi audit kaydında bütün işlemler bu hesapla görünür; işlemi hangi kişinin yaptığını ancak uygulama logları veya uygulamanın kendi tuttuğu işlem tablosu gösterir. Bu yüzden iki katmanı birlikte isteriz.

Audit türüÖrnekZayıf yanı
Veri tabanına yerleşikSQL Server Audit, PostgreSQL için pgAudit, MySQL Enterprise AuditKurulumda açılmamışsa hiç yoktur; açıksa bile uygulama hesabını gösterir
Tetikleyici (trigger) tabanlıHer değişiklikte bir audit_log tablosuna satır yazan tetikleyiciYetkili bir hesap tetikleyiciyi kapatabilir ya da audit tablosunu düzenleyebilir
Uygulama düzeyindeKullanıcı kimliğiyle tutulan işlem geçmişi tablosuUygulamayı atlayan doğrudan SQL değişikliklerini görmez

Audit tablosunun kendisini de inceleriz: sıra numaralarında ve zamanda boşluk var mı? Burada dikkatli olmak gerekir. MySQL InnoDB'de geri alınan (rollback) bir işlemin ürettiği AUTO_INCREMENT değerleri yeniden kullanılmaz; yani ID dizisindeki bir boşluk tek başına silme göstermez.

Silinen ve değiştirilen kayıtlar değerlendirilebilir mi?

Çoğu zaman bir ölçüde; hangi ölçüde olduğunu saklama ayarları ve geçen süre belirler. Baktığımız kaynaklar, güvenilirlik sırasına göre kabaca şunlardır:

  1. İşlem kaydı: MySQL 8.4'ün varsayılan ayarlarında (binlog_format=ROW, binlog_row_image=full) binlog, değişen satırın bütün sütunlarının önceki ve sonraki hâlini içerir. WAL ve transaction log da değişikliğin izini taşır, ama okunmaları daha çok uzmanlık ister.
  2. Yedekler: Farklı günlere ait iki yedek karşılaştırıldığında hangi satırın ne zaman eklendiği, değiştiği ya da kaybolduğu dar bir aralıkta tarihlenir.
  3. Kopyalar: Replikalar, raporlama ve veri ambarı kopyaları, dışa aktarılmış dosyalar, sistemin gönderdiği e-posta bildirimleri.
  4. "Yumuşak silme": Birçok uygulama satırı silmez, deleted_at ya da is_deleted alanını işaretler. Bu durumda silme zamanı zaten tabloda durur.
  5. Fiziksel izler: PostgreSQL'de VACUUM öncesindeki eski satır sürümleri, SQLite'ta serbest sayfalar. En az güvenilir ve en çok uzmanlık isteyen kaynaktır.

Bir kaydın silindiğini göstermek, silinen içeriği geri getirmekle aynı şey değildir. Raporda ikisini ayrı yazarız.

Veri bütünlüğü nasıl değerlendirilir?

Veri bütünlüğü iki ayrı soruyu kapsar: incelenen kopyanın alındığı andan beri değişmediği ve verinin kendi içinde tutarlı olduğu. Birincisi hash değerleriyle gösterilir (hash ve delil bütünlüğü). İkincisi daha çok şey anlatır, çünkü elle yapılan müdahaleler çoğu zaman bir tutarsızlık bırakır. Baktığımız tipik işaretler:

  • Sıra numarası (AUTO_INCREMENT, sequence) büyük olan bir kaydın created_at değeri, kendinden önceki kayıtlardan saniyeler değil günlerce eskiyse. Kayıt sonradan eklenip tarihi geriye çekilmiş olabilir; toplu içe aktarma da aynı görüntüyü verir.
  • updated_at değeri created_at'ten önceyse.
  • Fatura toplamı kalemlerle, stok bakiyesi hareketlerle, cari bakiye hesap hareketleriyle tutmuyorsa.
  • Başka tabloda karşılığı kalmamış (yetim) kayıtlar: silinmiş bir siparişin ödemesi, silinmiş bir kullanıcının oturumları.
  • Ana tablodaki bir değişikliğin tetikleyiciyle tutulan audit tablosunda karşılığı yoksa.

Tutarsızlık tek başına müdahale kanıtı değildir; hatalı bir migration ya da yazılım hatası da aynı izi bırakır. Raporda gözlenen tutarsızlığı, olası açıklamalarıyla ve işlem kaydında doğrulanıp doğrulanamadığıyla birlikte yazarız.

Veri tabanı ve uygulama logları nasıl karşılaştırılır?

Karşılaştırmanın amacı, her veri tabanı değişikliğinin arkasında bir uygulama isteği olup olmadığını görmektir. Adımlar:

  1. Her kaynağın saat ayarını belirleriz: veri tabanı sunucusu (MySQL'de @@global.time_zone ve @@session.time_zone, PostgreSQL'de SHOW timezone), uygulama sunucusu, log biçimi. Hepsini UTC'ye çeviririz.
  2. Sorudaki kaydın işlem kaydındaki bütün olaylarını çıkarırız: oluşturma, her güncelleme, silme.
  3. Uygulama logunda aynı aralıktaki istekleri, kullanıcı kimliklerini ve istek kimliklerini buluruz.
  4. Olayları eşleştiririz ve eşleşmeyenleri ayırırız. Uygulama isteği olmayan bir değişiklik; doğrudan SQL istemcisi, yönetim aracı, zamanlanmış görev ya da migration betiği olabilir. Hangisi olduğunu bağlantı ve audit kayıtlarından anlamaya çalışırız.
  5. Sonucu tek bir kronolojide birleştiririz: her satırda UTC ve Türkiye saati, kaynak, olay ve kanıt.
Örnek

Bir e-ticaret veri tabanında 1042 numaralı siparişin tutarı iki kez değişmiş olsun. Binlog'da 11:02:11 ve 20:47:05 UTC'de iki UPDATE görünür. İlki aynı saniyede PUT /api/orders/1042 isteğiyle ve bir müşteri hizmetleri kullanıcısının oturumuyla eşleşir. İkincisinin karşılığı uygulama logunda yoktur; audit kaydına göre dbadmin hesabıyla 203.0.113.40 adresinden açılmış bir bağlantıdan yapılmıştır. Rapor bu iki değişikliği ayrı ayrı, kanıtlarıyla anlatır; ikincisini kimin yaptığı sorusunu ise o hesabı kimlerin kullanabildiği bilgisine bağlı olarak açık bırakır.

Uygulama, API ve kimlik doğrulama loglarının kendisine ilişkin sorular için log analizi, bu kayıtları üreten kodun davranışı için kaynak kod incelemesi sayfalarımıza bakabilirsiniz.

Materyal nasıl alınmalı?

Canlı veri tabanında ilk iş sorgu çalıştırmak olmamalı

Önce izler korunur, sonra sorulur. Günlük bakım işleri (log temizliği, VACUUM, yedek rotasyonu) inceleme süresince incelenecek kayıtları silebilir; bunları sistem yöneticinizle birlikte gözden geçiririz.

  • Tutarlı bir yedek veya dump; mümkünse ek olarak fiziksel yedek.
  • İşlem kayıtları: binlog dosyaları, WAL arşivi, transaction log yedekleri. Süreleri dolmadan.
  • Yapılandırma: my.cnf, postgresql.conf, recovery modeli, saat dilimi ve audit ayarları.
  • Uygulama logları, şema ve migration geçmişi.
  • Veri tabanı hesaplarının ve yetkilerinin listesi: hangi hesapla kimler bağlanabiliyor.
  • Mevcut yedeklerin listesi, tarihleriyle.

Her dosyanın SHA-256 değeri alınır ve inceleme kopyalar üzerinde yapılır; SQLite dosyaları ise -wal ve -shm dosyalarıyla birlikte, salt okunur olarak açılır. Dosyaların hash değerini hash hesaplama aracıyla kendiniz de kaydedebilirsiniz.

Raporda neler yer alır?

Kayıt bazında kronoloji tablosu, uygulama isteğiyle eşleşen ve eşleşmeyen değişiklikler, silme göstergeleri ve kurtarılabilen içerik, kullanılan yöntem ve araç sürümleri, erişilemeyen kayıtlar ve bunların hangi soruyu cevapsız bıraktığı. Rapor, kayıtların teknik güvenilirliğine ilişkin tespitleri içerir; bunların hukuki değerlendirmesi avukatınıza ve mahkemeye aittir. Raporumuz avukatınız tarafından uzman görüşü olarak dosyaya sunulabilir.

Sık sorulan sorular

Veri tabanından silinen bir kayıt geri getirilebilir mi?

Bazen. Satır tabanlı işlem kaydı (ör. MySQL binlog) silinen satırın içeriğini hâlâ taşıyorsa, silme öncesine ait bir yedek varsa ya da kayıt bir raporlama kopyasına aktarılmışsa içerik bulunabilir. PostgreSQL'de VACUUM, SQLite'ta secure_delete ve SQL Server'da log truncation, fiziksel izlerin ömrünü belirler. Kurtarma imkânını ön incelemede değerlendiririz; her silinen kaydın geri geleceğini söylemeyiz.

Bir kaydın sonradan değiştirildiği nasıl anlaşılır?

Tablodaki tarih alanına bakılarak değil. Değişiklik; işlem kaydında (binlog, WAL, transaction log) bir UPDATE olayı olarak, audit tablosunda bir satır olarak ya da iki yedek arasındaki fark olarak görünür. Bu kayıtlar uygulama loglarıyla karşılaştırılır: değişikliğin arkasında bir kullanıcı isteği var mı, yoksa doğrudan veri tabanına bağlanılarak mı yapıldı? Kayıtlar saklanmamışsa değişiklik iddiası çoğu zaman teknik olarak cevapsız kalır.

Excel veya CSV çıktısı inceleme için yeterli mi?

Başlangıç için işe yarar, inceleme için yetmez. Excel ya da CSV çıktısı, bir sorgunun o anki sonucudur: kaydın geçmişini, silinen satırları, işlem kayıtlarını ve hangi sorguyla üretildiğini taşımaz, üstelik kolayca düzenlenebilir. Çıktıyı kullanmak zorundaysanız, hangi sorguyla, kim tarafından ve ne zaman alındığını yazılı olarak kaydedin ve dosyanın hash değerini hemen hesaplayın.

Veri tabanı incelemesi için sistemi durdurmak gerekir mi?

Çoğu zaman hayır. Tutarlı bir yedek, çalışan sistemi durdurmadan alınabilir; işlem kayıtları ve yapılandırma dosyaları da kopyalanabilir. Durdurmadan önce dikkat edilmesi gereken, canlı veri tabanında ilk iş olarak sorgu çalıştırmamak ve günlük bakım işlerinin (log temizliği, VACUUM, yedek rotasyonu) incelenecek izleri silmesini önlemektir. Edinim planını sistem yöneticinizle birlikte yaparız.

Veri tabanı logları ne kadar süre saklanır?

Tek bir süre yoktur; ürüne ve ayara bağlıdır. MySQL 8.4'te binary log dosyaları varsayılan olarak 30 gün sonra otomatik silinebilir. PostgreSQL'de WAL dosyaları arşivleme açık değilse checkpoint sonrasında silinir ya da yeniden kullanılır. SQL Server'da log alanı recovery modeline göre boşaltılır, MongoDB oplog'u ise boyutla sınırlıdır ve dolunca en eski girdileri siler. Bu yüzden uyuşmazlık başlar başlamaz log dosyalarının kopyalanmasını öneririz.

Elektronik kayıtlar mahkemede delil olarak kullanılabilir mi?

HMK m.199, elektronik ortamdaki verileri belge sayar; bir kaydın somut dosyada ne değer taşıyacağını mahkeme takdir eder. Teknik inceleme bu değerlendirmeye kayıtların nereden geldiğini, nasıl korunduğunu ve değişikliğe karşı ne kadar güvenilir olduğunu göstererek katkı sunar. Log kayıtlarının güvenilirliğini etkileyen etkenleri log kayıtlarının delil değeri yazımızda anlattık.

Kaynaklar

  1. MySQL 8.4: Binary Logging Options and Variables — Oracle
  2. MySQL 8.4: The DATE, DATETIME, and TIMESTAMP Types — Oracle
  3. PostgreSQL: WAL Configuration — PostgreSQL Global Development Group
  4. PostgreSQL: Routine Vacuuming — PostgreSQL Global Development Group
  5. The transaction log (SQL Server) — Microsoft Learn
  6. SQLite: Write-Ahead Logging — SQLite
  7. Replica Set Oplog — MongoDB Docs
  8. 6100 sayılı Hukuk Muhakemeleri Kanunu — mevzuat.gov.tr