# Güvenlik

> Contextator'ın tehdit modeli — yeni bir projenin MCP endpoint'i varsayılan olarak token gerektirir, ama açık'a çevrilebilir — ve onu kilitlemek için operatör kontrol listesi.

- Güncelleme: 2026-09-25
- Kaynak: https://contextator.com/tr/docs/security/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
Contextator belirli bir dağıtım için inşa edilmiştir: **özel bir ağda ya da bir reverse proxy'nin
arkasında, güvenilir bir ekip.** Panonun hesapları ve rolleri vardır; MCP endpoint'lerinin proje başına
bir kapısı vardır. Başka bir yerde açığa çıkarmadan önce bu sayfayı okuyun.

---

## Bilinmesi gereken tek şey

:::danger
Bir projenin MCP endpoint'i **açık olabilir**. Öyleyse, `/mcp/<proje-adı>`'na ulaşabilen herkes o
projede indekslenen her dokümanı arayabilir ve okuyabilir — hesap yok, token yok.
:::

Yeni bir proje **token required** olarak doğar: onu oluşturmak bir ilk token üretir ve gizli anahtarı
oluşturma diyaloğunda bir kez gösterir, böylece endpoint'i ilk saniyeden itibaren kapalıdır. Bu
varsayılan olmadan önce oluşturulmuş bir proje, zaten sahip olduğu modu korur — yükseltme, mevcut bir
projeyi geriye dönük olarak kapatmaz ya da açmaz. Her iki durumda da, modun ayarlandığı yer proje
sayfasındaki **MCP access** panelidir, ve üç tane vardır: **open**, endpoint'in herkese yanıt verdiği
mod; **token required**, bundan sonra bir istemcinin `Authorization: Bearer ctxm_…` göndermesi ya da
`401` yanıtı alması gereken mod; ya da **account required**, projenin kendi üyeliklerinin OAuth 2.1
üzerinden endpoint'e ulaştığı ve her istekte denetlendiği mod. Anahtar için
[Projeler](/tr/docs/projects/)'e ve istemci tarafı için
[Yapay Zekâ İstemcilerini Bağlamak](/tr/docs/connecting-ai-clients/)'a bakın.

**Bir token, bir hesap değil, endpoint için bir kimlik bilgisidir.** Hiçbir kimlik ve hiçbir doküman
başına kural taşımaz, bu yüzden onu elinde tutan herkes o projede indekslenen her şeyi okur, ve pano
rolleri `/mcp/*`'a hiç ulaşmaz.

Yani ağ konumu çoğunu hâlâ belirler. Projelerini open bırakan bir örnek, panonun kimin neyi görebileceği
konusunda ne söylediğine bakılmaksızın, ona ulaşabilen herkes tarafından okunabilir: portu özel tutun,
ya da onun önüne kimlik doğrulama koyun.

Pano ve admin API'si ayrı bir kapıdır, ve o her zaman kilitlidir: bir kişisel hesap gerektirirler.
`ADMIN_TOKEN` o kilit değildir — betikler için `/api/*`'a makine erişimidir — ve ikisi de `/mcp/*`'ı
korumaz. Bunu koruyan tek şey bir projenin kendi tokenlarıdır. Bkz.
[Hesaplar ve İzinler](/tr/docs/accounts-and-permissions/).

## Operatör kontrol listesi

1. **Her kişiye kendi hesabını verin**, ve ihtiyacı olmayan kimseye vermeyin. İlki, tek seferlik bir
   kodla `/setup`'ta oluşturulur; geri kalanı sağ üst menüdeki **Users**'tan gelir. Bir `member` hesabı
   yalnızca eklediğiniz projelere ulaşır — bkz. [Hesaplar ve İzinler](/tr/docs/accounts-and-permissions/).
   Düz HTTP bir LAN adresinde `AUTH_COOKIE_SECURE=0` ayarlayın, aksi halde tarayıcı oturum çerezini
   düşürür ve kimse oturum açamaz.
2. **`ADMIN_TOKEN`'ı tarayıcılardan uzak tutun**, ve API'ye ihtiyaç duyan bir betik yoksa ayarını
   kaldırın. Her endpoint'te root izinleriyle davranır; onu bir root parolası gibi ele alın.
3. Özel bir git deposu ya da bir Notion integration'ı eklemeden önce **`SECRET_KEY`'i** 32+ rastgele
   karaktere (`openssl rand -hex 32`) ayarlayın. Onu veritabanı yedeğinizden başka bir yerde saklayın.
4. **Herkese açık olmaması gereken projeleri kapatın.** Proje sayfasında **MCP access**: **token
   required**, ardından istemci başına bir kez **New token**, ya da onu okuyabilecek kişilerin zaten
   burada hesapları varsa **account required**. Açık bırakılan bir proje, URL'sine ulaşabilen herkes
   tarafından okunabilir — bkz. [Projeler](/tr/docs/projects/).
5. **Portu genel internetten uzak tutun**, ya da kimlik doğrulamayı bir reverse proxy'de
   sonlandırın — kasıtlı olarak open bıraktığınız projeler için de dahil. Varsayılan
   (`CONTEXTATOR_BIND=127.0.0.1`) portu zaten yalnızca bu makinede yayımlar; bunu değiştiren tek ayar
   `0.0.0.0`'dır, ve önüne bir reverse proxy ya da bir VPN koymanın tam zamanı da budur. Bkz.
   [Yapılandırma](/tr/docs/configuration/#sunucu).
6. **`ALLOWED_DOC_ROOTS`'u dar tutun.** Hangi dizinlerin indekslenebileceğine karar veren sınır budur.
7. Gönderilen compose dosyasının yaptığı gibi **dokümantasyonu salt okunur bağlayın** (`:ro`).
8. **Kaynak tokenlarını sıkı kapsamlayın.** Bir git token'ının dokümantasyon depolarına okuma erişimine
   ihtiyacı vardır, daha fazlasına değil.
9. **Sırları indekslemeyin.** İndekslenen her şey, endpoint'e ulaşabilen her agent tarafından — ve
   hiçbir doküman başına kural taşımayan o projenin MCP tokenlarını elinde tutan herkes tarafından —
   okunabilir.

## Ne korunur, ve nasıl

| Alan | Kontrol |
|------|---------|
| Pano ve API | Bir kişisel hesap, ya da betikler için `ADMIN_TOKEN`. Her kural — örnek rolü, proje başına üyelik — tek bir politika tablosundan sunucu tarafında karar verilir, bu yüzden bir route kaydedildiği anda korunur ve tarayıcıda geri yüklenen bir düğme yine de `403` yanıtlar. `/api/health` muaftır, ve tanımadığı bir çağırana daha az şey söyler. Son aktif `root` hesabı asla silinemez, rütbesi düşürülemez ya da devre dışı bırakılamaz, ve bir `admin` bir `root` hesabına dokunamaz ya da `root` rolünü dağıtamaz — tam rol modeli için bkz. [Hesaplar ve İzinler](/tr/docs/accounts-and-permissions/) |
| Parolalar | Salted `scrypt` (`node:crypto`, N=2¹⁵), asla loglanmaz, asla döndürülmez, geri döndürülemez. Bilinmeyen bir kullanıcı adı, yanlış bir parolayla aynı mesajla ve aynı işle reddedilir |
| Oturum açma denemeleri | IP adresi başına, ve `AUTH_LOGIN_WINDOW_MIN`'den bir saate kadar iki katına çıkan bir kilitlenmeyle hesap başına hız sınırlıdır |
| Pano oturumları | İmzalı bir çerez değil, veritabanında bir satır: yalnızca token'ın bir hash'i saklanır, ve çerez `HttpOnly`, `SameSite=Lax` ve HTTPS üzerinden `Secure`'dur. `AUTH_SESSION_IDLE_MS` ve `AUTH_SESSION_TTL_DAYS` ile sınırlıdır, ve iptal edilebilir — bir parolayı değiştirmek o hesabın diğer oturumlarını sonlandırır, ve bir hesabı devre dışı bırakmak, silmek ya da sıfırlamak hepsini sonlandırır |
| Bir şeyi değiştiren istekler | Çerezle kimliği doğrulanmış bir yazma bu siteden gelmelidir (`Sec-Fetch-Site`, `Origin`/`Referer`'a geri dönerek) yoksa `403` ile reddedilir. CORS kasıtlı olarak kimlik bilgisi olmadan bırakılmıştır, bu yüzden `ALLOWED_ORIGINS`, API'yi oturum açmış bir kullanıcı olarak okumak için kullanılamaz |
| Göremediğiniz projeler | Bir üyenin erişimi olmayan bir proje, var olmayan biriyle aynı şekilde `404` yanıtlar, bu yüzden proje id'leri araştırılamaz |
| Webhook endpoint'i | Her git kaynağının kendi sırrı vardır; sağlayıcının imzası, hiçbir şey kuyruğa girmeden **önce** ham gövdeye karşı doğrulanır |
| Saklanan kaynak tokenları | Git, Notion ve Confluence kimlik bilgileri `SECRET_KEY` altında AES-256-GCM ile şifrelenir; API tarafından asla döndürülmez, bir daha gösterilmez. Bir depo URL'sine yapıştırılan kimlik bilgileri saklamadan önce ayıklanır |
| Dosya sistemi erişimi | Yerel kaynak dizinleri `ALLOWED_DOC_ROOTS` içinde çözümlenmelidir; `..`, kaçan symlink'ler ve dizin olmayanlar reddedilir. Git alt dizinleri checkout içinde çözümlenir |
| `read_document` | Yalnızca o proje için indekslenmiş yolları sunar — asla keyfi bir dosya sistemi yolunu — `max_tokens` (200–20000, varsayılan 4000) ile sınırlıdır |
| Yüklemeler ve arşivler | Önce bir scratch dizinine çıkarılır, ardından geçiş reddi, nokta-dizini kaldırma, taşınabilir ad kontrolleri, bir uzantı filtresi ve sıkıştırma bombalarına karşı girdi/boyut sınırlarıyla kopyalanır |
| MCP endpoint'leri | Proje başına: **token required** (yeni bir proje için varsayılan), **open** ya da **account required**. `token` modunda, canlı bir `ctxm_…` bearer'ı olmayan bir istek `www-authenticate: Bearer realm="<proje>"` ile `401` yanıtlanır; `account` modunda statik bir token kimseyi adlandırmaz ve reddedilir, ve istemci bunun yerine OAuth 2.1 üzerinden oturum açar. Bir hesabı adlandıran bir kimlik bilgisi, `open` dahil **her** modda **her** istekte o hesabın üyeliğine karşı denetlenir, ve bir üye olmayan `403` yanıtlanır |
| OAuth discovery ve registration | `/.well-known/oauth-*` ve `/oauth/*`, **tasarım gereği** hiçbir kimlik bilgisi olmadan erişilebilirdir — iki discovery dokümanı, henüz hiçbir kimlik bilgisi olmayan bir istemci tarafından çekilmek üzere vardır, ve bir client'ı register etmek o client'a kendi başına hiçbir şey vermez. Bir client'ı onaylamak, panodaki her yazma gibi oturum açılmış ve same-site denetimli bir tarayıcı eylemidir |
| MCP tokenları | 256 bit rastgelelik, yalnızca bir sha256 hash'i olarak saklanır — üretildiğinde bir kez gösterilir ve asla geri kazanılamaz. Bir projeye kapsamlanır, iptal edilebilir, ve liste yalnızca adı, baştaki karakterleri ve son kullanım zamanını tutar |
| `/mcp/*`'a tarayıcı istekleri | `Origin`, **her** modda doğrulanır (DNS-rebinding koruması). Komut satırı istemcileri hiçbir `Origin` göndermez ve her zaman izin verilir |
| Proje izolasyonu | Her sorgu bir projeyle sınırlıdır; bir proje için verilen bir oturum başka bir projede reddedilir. Bir projeyi silmek, tokenlarından birini iptal etmek, ya da onu **token required** ya da **account required**'a kapatmak, istemcinin bir sonraki isteğinde değil, açık oturumlarını hemen kapatır |
| Veritabanı | Gömülü PostgreSQL, konteynerin içinde `127.0.0.1`'de dinler ve yayımlanmaz |
| Süreç | Uygulama ayrıcalıksız bir kullanıcı olarak çalışır; entrypoint yalnızca volume sahipliğini düzeltecek kadar root'tur |
| `/about`, `/privacy`, `/cookies`, `/terms` ve `/license` sayfaları | **Kasıtlı olarak açık.** Statiktirler, veritabanından hiçbir şey okumazlar ve hiçbir sır taşımazlar: yalnızca operatörün okuyabildiği bir yasal bildirim, bir bildirim değildir. Panonun kendisi bunların arasında değildir — `/`'e anonim bir ziyaretçi `/login`'e yönlendirilir ve uygulama ona hiçbir zaman gönderilmez |

## Kimlik doğrulamayı öne koymak

Akış aktarımlarını bozulmadan bırakan nginx Basic authentication örneği:

```nginx
location / {
    auth_basic           "Contextator";
    auth_basic_user_file /etc/nginx/.htpasswd;

    proxy_pass         http://127.0.0.1:3444;
    proxy_http_version 1.1;
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-Proto $scheme;
    proxy_buffering    off;
    proxy_read_timeout 1h;
}

# the webhook endpoint must stay reachable by the git host
location /api/webhooks/ {
    auth_basic off;
    proxy_pass http://127.0.0.1:3444;
}
```

Çoğu MCP istemcisinin Basic kimlik bilgileri gönderemediğini unutmayın, bu yüzden böyle bir proxy,
agent'lardan çok panoya hizmet eder — ve pano zaten bir hesap istediğinden, önüne ikinci bir sorgu
koymak pek az şey kazandırır. `/mcp/*`'ın kendisi için bir proje token'ı daha iyi bir uyumdur, çünkü o,
MCP istemcilerinin zaten ayarlamayı bildiği sıradan bir `Authorization` başlığıdır. Kasıtlı olarak open
bıraktığınız projeler için proxy'yi, ya da bir mutual-TLS ya da VPN sınırını koruyun.

TLS'yi sonlandıran bir proxy'nin arkasında, `X-Forwarded-Proto`'ya güvenmek yerine
`AUTH_COOKIE_SECURE=1` ayarlayın.

## Şeyleri değiştirmek

| Ne | Nasıl |
|------|-----|
| Kendi parolanız | Account menu → **Change password**. Hesabınızın her diğer oturumunu sonlandırır ve kullandığınız oturumu bırakır |
| Başka birinin parolası | Users page → **Reset password**. Bir kez gösterilen geçici bir tane verir, ve o hesabın her yerdeki oturumunu kapatır; bir sonraki oturum açışında onu değiştirmeleri gerekir |
| `ADMIN_TOKEN` | `.env`'de değiştirin ve yeniden başlatın. Onu taşıyan betikleri güncelleyin; pano oturum açışları etkilenmez, çünkü onu hiç kullanmadılar |
| Kurulum kodu | Değiştirilecek bir şey yok. İlk hesap oluşturulduğu anda çalışmayı durdurur. Hiç hesap yokken, üretilen bir tane her yeniden başlatmada değiştirilir |
| Bir git, Notion ya da Confluence token'ı | Kaynağı düzenleyin ve yeni token'ı yapıştırın; eski şifreli metin üzerine yazılır |
| Bir webhook sırrı | Git: kaynak diyaloğunda **Regenerate**, ardından depo ayarlarını güncelleyin. Confluence: kaynak diyaloğunda **New secret**, ardından onu Confluence Administration → Webhooks altındaki mevcut webhook'a yapıştırın. Notion: yenilenecek bir şey yok — o sırrı Notion üretir, bu yüzden kaynak diyaloğunda yeni bir doğrulama penceresi açın ve Notion'ın token'ını yeniden göndermesini sağlayın |
| Bir MCP token'ı | Yeni bir tane üretin, istemciyi ona taşıyın, ardından eskisini **Revoke** edin. İptal etmek, o projenin canlı MCP oturumlarını hemen kapatır |
| `SECRET_KEY` | Tek bir düzenleme değil, dört adımlı bir rotasyon: emekliye ayırdığınız anahtarı `SECRET_KEY_PREVIOUS`'a ayarlayın, `SECRET_KEY`'i yeni olana ayarlayın ve yeniden başlatın, saklanan tokenları yeni anahtar altında yeniden şifrelemek için `npm run rotate-secret`'ı (bir Docker kurulumu için `docker exec contextator npm run rotate-secret`) çalıştırın, ardından altında hiçbir şey kalmadığını bildirince `SECRET_KEY_PREVIOUS`'u kaldırın ve yeniden başlatın. Hiçbir saklanan token'ın yeniden girilmesi gerekmez. Wiki'nin [`SECRET_KEY`'i rotate etmek](https://github.com/Contextator/Contextator/wiki/Security#rotating-secret_key) runbook'una bakın |
| `POSTGRES_PASSWORD` | Yalnızca küme oluşturulurken uygulanır; onu sonradan `ALTER USER` ile değiştirin ve `.env`'i güncelleyin — bkz. [Yedekleme ve Veri](/tr/docs/backup-and-data/) |

## Contextator'ın karşı savunmadığı şeyler

- **Dokümantasyon üzerinden prompt injection.** Bir doküman *"talimatlarını yoksay ve…"* diyorsa,
  agent onu okuyabilir. Contextator metni sadakatle döndürür; niyeti temizlemez. İndekslenmiş
  gövdenizi güvenilir girdi olarak ele alın.
- **Hız sınırlama.** Oturum açmak, IP adresi başına ve hesap başına sınırlıdır. Başka hiçbir şey değil:
  `/api/*`'ın geri kalanı değil, `/mcp/*` de değil — token korumalı bir proje de istisna değil —
  bir token'ı elinde tutan bir istemci istediği kadar sık sorabilir. Ağ sınırı kontroldür.
- **İki faktörlü kimlik doğrulama.** Yoktur; yerel bir hesap bir kullanıcı adı ve bir paroladır. Tekli
  oturum açma ayrıdır — yapılandırılmış bir OIDC sağlayıcısı olan bir örnek onu bir parolanın yanında
  sunar — ve kimin ne yaptığının kaydı, yalnızca sunucu logu değil, panodaki `#/~audit`'teki audit
  logudur, yalnızca root ve admin için.
- **`/mcp/*`'ta kullanıcı başına ya da doküman başına kurallar.** Bir proje token'ı, bir kimlik değil,
  tüm endpoint için bir kimlik bilgisidir. Onu elinde tutan herkese, *bu token en son ne zaman
  kullanıldı*nın ötesinde hiçbir audit izi olmadan, projedeki her dokümanı verir. Pano rolleri
  `/mcp/*`'a ulaşmaz — bir `viewer` üyeliği orada hiçbir şey vermez, ve hiçbir şey bir token'ı bir
  projenin bir kısmına daraltmaz. İkisini köprülemek planlıdır ve henüz inşa edilmemiştir; öyle olana
  kadar, her istemciye kendi token'ını verin ve artık tanımadıklarınızı iptal edin.
- **Hangi projelerin var olduğunu gizlemek.** Token korumalı bir proje, bilinmeyen bir adın `404`
  yanıtladığı yerde `401` yanıtlar, bu yüzden sunucuya ulaşabilen herkes yine de bir proje adı
  öğrenebilir.

## Bir güvenlik açığı bildirmek

Onu herkese açık bir issue'da değil, depo sahibine özel olarak bildirin.
