# VC-100 · Yönetim işlemleri ve rol değişiklikleri kalıcı denetim izi bırakmıyor

Bir üyenin yetkisi değişiyor ama değişikliği kimin ve ne zaman yaptığı kaydedilmiyor. Son durum doğru görünse bile olay sonrası inceleme için gerekli geçmiş yeniden kurulamıyor.

- Önem: ORTA. Etkisi sınırlı. Her gün, sıradan kullanımda tetiklenir.
- Önem notu: Yönetim değişikliği her gerçekleştiğinde geçmişin kaybolması esas alınır. Kayıt yokluğu tek başına yetkisiz işlem kanıtı değildir. Para ve geniş erişim sağlayan roller için iş etkisi yükselir.
- Eksen ve kategori: Sağlamlık, 15 Gözlem, yedek ve kurtarma
- Yığın: Her yığın
- Yapay zekâ kodunda: ölçülmedi. Dayanak: uzman görüşü.
- Ne zaman bakılır: İlk yayından önce, Her ay
- CWE: CWE-778
- OWASP Top 10:2025: A09:2025 Security Logging & Alerting Failures
- Checklist ifadesi: Yönetim işlemleri ve rol değişiklikleri kalıcı bir denetim izi bırakıyor.
- Son inceleme: 4 Ekim 2026, Komünite editörlüğü
- Adres: https://vibecheck.komunite.com.tr/madde/yonetim-ve-yetki-degisiklikleri-iz-birakmiyor

## 60 saniyelik kontrol

Yalnız kendi uygulamanda ya da yazılı izin aldığın sistemde dene. Bu bir sızma testi değildir.

1. Son rol değişikliğinin yapan kişi, hedef ve zaman bilgisini bul.
2. İşlem sonucu ile denetim kaydının birlikte kalıcılaştığını kontrol et.
3. Kayıtların token, parola veya tam istek gövdesi taşımadığını incele.
4. Normal çalışma kimliğinin denetim geçmişini silebildiği yolları araştır.

## Ne oluyor

Bir üyenin rolü değişmiş. Veritabanında yeni rol görünüyor ve uygulama buna göre davranıyor. Fakat değişikliği kimin, ne zaman ve hangi hedef üzerinde yaptığı kaydedilmemiş. Son durumdan geçmişi geri çıkaramazsın. Mevcut rol doğru olsa bile bunun beklenen bir yönetim işlemi mi, yanlış seçim mi yoksa kötüye kullanım mı olduğunu anlamak zorlaşır.

Denetim izi, yönetim işlemini sonradan incelemeyi sağlayan yapılandırılmış kayıttır. Yapan kimlik, hedef, işlem türü, zaman ve sonuç birlikte anlam taşır. Bir kullanıcıya verilen rol için önceki ve sonraki değer de yararlı olabilir. Ancak tam istek gövdesini kaydetmek bu ihtiyacı doğru karşılamaz. Parola, token veya gereksiz kişisel veri günlüğe taşınabilir.

İzle veri değişikliği arasındaki ilişki de önemlidir. Rol güncellenip denetim yazısı başarısız olursa yönetim işlemi geçmişsiz kalır. Önce başarı kaydı yazıp sonra rol güncellemesi düşerse günlük gerçekleşmemiş bir değişiklik anlatır. Aynı veritabanında bu iki yazıyı ortak transaction içinde tutmak belirli bir tutarlılık sınırı sağlar. Dış kayıt hizmeti kullanıldığında ayrıca kalıcı teslim tasarımı gerekir.

## Gerçek olay

Bu madde yeni bir AI olayına dayanmıyor. OWASP Logging Cheat Sheet, kullanıcı yönetimi, yetki değişiklikleri ve yönetici işlemlerini güvenlik açısından anlamlı kayıt alanları arasında sayıyor. Aynı rehber sırların ve gereksiz hassas verinin günlükten uzak tutulmasını ele alıyor. Burada rehberin metnini çevirmek yerine, rol değişikliği üzerindeki somut sonucu inceliyoruz. [Uygulama günlükleri](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html)

CWE-778 yetersiz günlük tutmanın olay tespitini ve incelemeyi zorlaştırmasını tanımlar. [CWE-778](https://cwe.mitre.org/data/definitions/778.html) Bu eşleme kayıt bulunmamasını yetkisiz erişim yaşandığının kanıtına dönüştürmez. Bir işlem yetkili olabilir ve yine de geçmişi eksik kalabilir. Örnekte yetki denetimi her iki sürümde de korunur. Böylece gösterilen arıza, yönetim değişikliğinin denetim iziyle ilişkilendirilmemesidir.

## Yapay zekâ bunu neden üretiyor

**Model görünür durum değişikliğine odaklanır.** İstek üyeyi yönetici yapmaksa ajan rol alanını günceller ve arayüzü yeniler. Kullanıcının gördüğü iş tamamlanmıştır. Fakat aylar sonra bu değişikliğin nedenini araştıracak kişinin ihtiyacı görevde görünmeyebilir. Kalıcı denetim olayı kabul ölçütüne eklenmediyse çözüm yalnız güncel durumla sınırlı kalabilir. Sonradan geçmiş üretmek çoğu durumda mümkün olmaz.

**Hata günlüğü denetim izi sanılabilir.** Ajan yalnız başarısız işlemleri kaydeden bir yardımcı işlev bulduğunda bütün gözlem ihtiyacının karşılandığını düşünebilir. Oysa başarılı yetki değişiklikleri de incelenmeye ihtiyaç duyabilir. Hata mesajıyla kimliği, hedefi ve sonucu belirli olay kaydı farklı amaçlara hizmet eder. Aynı altyapıyı kullanmaları bu veri sözleşmelerini kendiliğinden eşitlemez.

**İkinci yazı sonradan eklenir.** Yönetim işlemi tamamlandıktan sonra eklenen günlük çağrısı ayrı bir adım olabilir. Normal testte iki yazı da geçtiği için tutarsızlık görünmez. Denetim yazısını bilerek başarısız kılan kontrollü test, rolün tek başına değişip değişmediğini gösterir. Bu deney yalnız hata mesajına bakmamalı, kalıcı veri durumunu da karşılaştırmalıdır.

**Daha çok veri daha iyi kanıt sanılabilir.** Model incelemeyi kolaylaştırmak için bütün istek gövdesini veya oturum nesnesini kayda alabilir. Bu yaklaşım sırları ikinci bir depoya yayar ve erişim kapsamını büyütür. Olay için gerekli alanları açıkça seçmek daha ölçülebilir bir sözleşme kurar. Bunlar olası üretim açıklamalarıdır. Belirli bir modelin davranış sıklığı veya bütün AI kodlarına ilişkin genelleme değildir.

## Etki

Geçmiş bulunmayınca yanlış rolü düzeltmek mümkün olsa bile olayın kapsamını anlamak zorlaşır. Aynı kişinin başka değişiklik yapıp yapmadığını veya bir otomasyonun hangi kayıtları etkilediğini araştırmak daha uzun sürer. Yönetim işlemini yapan kişi de kendi eylemini doğrulayacak kanıttan yoksun kalabilir.

Öte yandan sınırsız günlük tutmak yeni veri ve erişim yükü oluşturur. Saklama süresi ve okuyabilen roller iş ihtiyacına göre belirlenmelidir. Bu madde belirli bir hukuki saklama süresi önermez. Kişisel veri ve saklama yükümlülüğü gereken durumda hukuk desteği al.

## Nasıl anlarsın

Son rol değişikliğini seçip kalıcı kaydını ara. Yapan kimlik, hedef, zaman, eski değer ve yeni değer birbirine bağlanabiliyor mu? Sadece genel bir başarılı yanıt varsa olayın ayrıntısı kaybolmuş olabilir. Yönetim paneli dışındaki API ve zamanlanmış işler de aynı denetim yoluna girmelidir.

Örnekte aktörün rolü veritabanından okunur. Normal üye yetki değiştiremez. Test başarılı değişikliğin olayını denetler, ardından denetim tablosuna yazmayı bir trigger ile başarısız kılar. İyi sürümde rol eski halinde kalır. Kötü sürüm başarılı rol değişikliği için hiç olay üretmediğinden testi geçemez.

## Nasıl düzeltirsin

1. **Olayı tanımla.** Yapan, hedef, zaman ve sonuç alanlarını açıkça seç. Parola, token ve tam istek gövdesini bu sözleşmeye ekleme.
2. **Kalıcı yazıyı bağla.** Aynı veritabanındaysa değişiklik ve başarı olayını ortak transaction içinde yaz. [SQLite transaction](https://www.sqlite.org/lang_transaction.html)
3. **Hata yolunu ayır.** Reddedilen deneme ve teknik başarısızlık için ayrı güvenlik günlüğü kullan. Geri alınan transaction içindeki başarı kaydını başarısız deneme kaydı sanma.
4. **Erişimi sınırla.** Günlük okuyabilen ve silebilen kimlikleri belirle. Dış denetim deposuna geçiyorsan kalıcı outbox veya benzer teslim yolu kur.
5. **Veriyi karşılaştır.** Testte hem rolün hem olayın durumunu kontrol et. Yalnız günlük işlevinin çağrıldığını saymak yeterli değildir.

## Bir daha olmasın

Yeni yönetim işlemine denetim olayı ve saklama kararı ekle. Rol değiştiren her yolun aynı sözleşmeyi kullandığını gözden geçir.

## Sınır

Denetim izi yetki kontrolünün veya anlık uyarının yerini tutmaz. Aynı veritabanındaki tablo, geniş yönetici yetkisine karşı kendiliğinden değiştirilemez kayıt sağlamaz. Örnek yalnız başarılı rol değişikliğini tutarlı kaydeder. Reddedilen denemeleri, merkezi aramayı, saklama süresini ve dış denetim hizmetini kurmaz. Bunlar uygulamanın ayrı işletim kararlarıdır.

## Düzeltme kodları

### Node.js: Transaction içinde denetim izi

Önce:

```js
// yonetim/rol.js, açıklama amaçlı. actorId doğrulanmış sunucu oturumundan gelir.
export function changeRole(db, actorId, targetId, role) {
  if (!['member', 'admin'].includes(role)) throw new Error('Geçersiz rol');
  db.exec('BEGIN IMMEDIATE');
  try {
    const actor = db.prepare('SELECT role FROM members WHERE id = ?').get(actorId);
    if (actor?.role !== 'admin') throw new Error('Yetki yok');
    const old = db.prepare('SELECT role FROM members WHERE id = ?').get(targetId);
    if (!old) throw new Error('Hedef yok');
    db.prepare('UPDATE members SET role = ? WHERE id = ?').run(role, targetId);
    // Başarılı değişikliğin kalıcı geçmişi yok.
    db.exec('COMMIT');
  } catch (error) { db.exec('ROLLBACK'); throw error; }
}
// Yetki denetimi doğru olsa da geçmiş son durumdan geri çıkarılamaz.
```

Sonra:

```js
// yonetim/rol.js, açıklama amaçlı. actorId doğrulanmış sunucu oturumundan gelir.
export function changeRole(db, actorId, targetId, role) {
  if (!['member', 'admin'].includes(role)) throw new Error('Geçersiz rol');
  db.exec('BEGIN IMMEDIATE');
  try {
    const actor = db.prepare('SELECT role FROM members WHERE id = ?').get(actorId);
    if (actor?.role !== 'admin') throw new Error('Yetki yok');
    const old = db.prepare('SELECT role FROM members WHERE id = ?').get(targetId);
    if (!old) throw new Error('Hedef yok');
    db.prepare('UPDATE members SET role = ? WHERE id = ?').run(role, targetId);
    db.prepare(`INSERT INTO audit(actor, target, old_role, new_role, at)
      VALUES (?, ?, ?, ?, ?)`).run(actorId, targetId, old.role, role, new Date().toISOString());
    db.exec('COMMIT');
  } catch (error) { db.exec('ROLLBACK'); throw error; }
}
// Başarılı rol değişikliği izi. Reddedilen denemeler ayrı güvenlik günlüğüne gider.
// Aynı veritabanındaki bu tablo kendiliğinden değiştirilemez kayıt sağlamaz.
```

Düzeltmeyi kanıtlayan test:

```js
// yonetim/rol.test.mjs, açıklama amaçlı. Denetim yazısının hatası zorlanır.
import test from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';
const { changeRole } = await import(process.env.ORNEK_DOSYA);
test('başarılı değişiklik iz bırakır, iz yazılamazsa rol değişmez', () => {
  const db = new DatabaseSync(':memory:');
  try {
    db.exec(`CREATE TABLE members(id TEXT PRIMARY KEY, role TEXT);
      CREATE TABLE audit(actor TEXT, target TEXT, old_role TEXT, new_role TEXT, at TEXT);
      INSERT INTO members VALUES('owner','admin'),('user','member'),('other','member');`);
    changeRole(db, 'owner', 'user', 'admin');
    const events = db.prepare('SELECT * FROM audit').all();
    assert.equal(events.length, 1);
    assert.deepEqual({ ...events[0], at: undefined }, { actor: 'owner', target: 'user', old_role: 'member', new_role: 'admin', at: undefined });
    assert.ok(Number.isFinite(Date.parse(events[0].at)));
    assert.throws(() => changeRole(db, 'other', 'user', 'member'), /Yetki yok/);
    db.exec(`CREATE TRIGGER block_audit BEFORE INSERT ON audit BEGIN SELECT RAISE(ABORT, 'AUDIT_TEST'); END;`);
    assert.throws(() => changeRole(db, 'owner', 'user', 'member'), /AUDIT_TEST/);
    assert.equal(db.prepare("SELECT role FROM members WHERE id='user'").get().role, 'admin');
    assert.equal(db.prepare('SELECT count(*) AS n FROM audit').get().n, 1);
  } finally { db.close(); }
});
```

## Ajan kuralı (AGENTS.md)

```md
## Yetki değişikliği iz bırakmıyor (vibecheck VC-100)
- Yetki ve yönetim değişikliklerini yapılandırılmış olay olarak kaydet.
- Yapan kişi, hedef, zaman ve sonucu ilişkilendir.
- Sırları ve gereksiz kişisel veriyi kayda alma.
- Başarılı değişiklik ile kalıcı izi birlikte güvenceye al.
- Denetim kaydının erişim ve saklama sınırını belirle.
```

## Kaynaklar

1. [Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html), OWASP
2. [CWE-778 Insufficient Logging](https://cwe.mitre.org/data/definitions/778.html), MITRE
3. [SQLite Transactions](https://www.sqlite.org/lang_transaction.html), SQLite
4. [Node.js SQLite parameter binding](https://nodejs.org/api/sqlite.html), Node.js

---

vibecheck · Komünite editörlüğü. Metin CC BY 4.0, prompt ve kural parçaları MIT-0. Kaynak: https://vibecheck.komunite.com.tr/madde/yonetim-ve-yetki-degisiklikleri-iz-birakmiyor
