
Fri Sep 11 2026

Selam! Hazırsan konuya direk dalalım 🚀
MCP (Model Context Protocol) son zamanlarda AI uygulamaları dünyasında giderek daha çok duyduğumuz bir terim. Kısaca: LLM'lerin dış dünyayla (dosya sistemi, veritabanı, API'ler, vs.) güvenli ve standart bir yolla konuşmasını sağlayan bir protokol.
Peki, "güvenli" kelimesi gerçekten ne kadar gerçekçi? 🤔
İşte tam bu noktada durup düşünmemiz gerekiyor. MCP, AI ajanlarına sistemlerimize erişim anahtarı veriyor. Ve her anahtar gibi, yanlış ellerde olursa — ya da yanlış yapılandırılırsa — ciddi riskler yaratıyor.
Bu yazıda MCP güvenlik açılarını pratik ve sıralı bir şekilde ele alacağız:
Her başlıkta: saldırı vektörü → PoC mantığı → nasıl önlenir? akışını takip edeceğiz. Kod örnekleri olacak, ama "kopyala-yapıştır yap, hackle" değil; "anla, savun" odaklı olacaklar 🛡
Küçük bir hatırlatma: Bu yazı saldırı rehberi değil, savunma rehberidir. Ama savunmak için saldırıyı anlamak zorundayız — o yüzden bazı "kötü" senaryoları da gözden geçireceğiz.
Hadi başlayalım mı? İlk durağimiz: Prompt Injection ve MCP Tool'ları 🎯
Hazırsanız, araştırmacıların eline geçirdiği verilerin ne anlama geldiğini bir başka bakış açısıyla inceleyelim 🔍
Ne oluyor burada? Güvenlik araştırmacılarının yayınladığı raporlarda sıkça gördüğümüz "%20'si exploit edilemiyor" veya "%35'i false positive" gibi ifadeler, aslında bize şunu söylüyor: tarama araçları her şeyi bulsa da, hepsi gerçek tehdit değil ⚠️
Hadi pratik bir örnek üzerinden anlayalım:
| Metrik | Değer | Ne Anlama Gelir? |
|---|---|---|
| Toplam Tespit Edilen CVE | 12.847 | Tarama aracının bulduğu ham veri |
| Gerçekten Exploit Edilebilen | 2.569 (%20) | Saldırganın gerçekten kullanabileceği |
| Yama Mevcut Ama Uygulanmamış | 4.112 (%32) | "Biliyorduk ama halletmedik" kuyruğu |
| False Positive / Yanlış Alarm | 3.854 (%30) | Zaman kaybı, gürültü |
| Risk Skoru Yüksek (CVSS ≥ 7) | 1.927 (%15) | Öncelik sırası en üstte olmalı |
Bu tablo bize ne anlatıyor? 🤔
Küçük bir hatırlatma: Bu oranlar ortalama değerlerdir. Sektörünüze, kullandığınız teknolojilere ve ağ mimarinize göre büyük farklılıklar gösterebilir. Kendi ortamınızı bilmeden "ortalamaya" göre karar vermek tehlikelidir ⛔
Peki ya biz ne yapmalıyız? Bir sonraki bölümde, bu ham veriyi aksiyon planına nasıl çevireceğimizi — yani risk tabanlı önceliklendirmeyi — konuşacağız 🛠
Hazırsan bu üç "sessiz tehlike"yi tek tek inceleyelim. Her biri kendi başına bir yazı konusu ama pratikte birlikte görüldüğünde en çok zarar veriyorlar 🎯
Token elinize ulaştı mı, hemen userId çekip işe başlıyorsunuz mu? 🤔 Bu, en yaygın ve en tehlikeli hata.
Neden eksik kalıyor?
exp) atlanıyoriss (issuer) / aud (audience) claim'leri kontrol edilmiyorTipik hata: "Public key" yerine "public token" paylaşımı 😅 Evet, yaşandı. Bir servis, doğrulama anahtarını gizli sanıp token olarak dağıtıyor, saldırgan o anahtarla kendi token'ını imzalıyor.
Ne yapmalı?
jsonwebtoken / jose), manuel split('.') yapmayınScope, token'ın ne yapmaya yetkili olduğunu söyler. Ama çoğu kodda: if (token) { allow() } — scope'a bakılmıyor.
Neden eksik kalıyor?
sub / userId var)Hadi pratik bir örnek üzerinden anlayalım 🛠
Şu token'ı ele alalım — scope claim'i eksik:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Base64 decode edince payload şöyle:
{
"sub": "1234567890",
"name": "Alice",
"iat": 1516239022
}
Ne oluyor burada? ❗️
scope ya da scopes claim'i yokGET /admin/users de çağrılabilir, POST /payments deDüzeltme: Token payload'ına mutlaka scope ekleyin:
{
"sub": "1234567890",
"name": "Alice",
"scope": "read:profile write:posts",
"iat": 1516239022
}
Ve her endpoint'te:
// Basit scope middleware örneği
function requireScope(required: string) {
return (req, res, next) => {
const scopes = req.user?.scope?.split(' ') ?? []
if (!scopes.includes(required)) {
return res.status(403).json({ error: 'Yetkisiz: scope eksik' })
}
next()
}
}
// Kullanım
app.get('/admin/users', requireScope('admin:read'), handler)
✅ Bu sayede token geçerli olsa bile, scope yoksa erişim reddedilir.
"Admin oldum, her şeye girebilirim" — yanlış. RBAC genelde rol tabanlı yapılır ama pratikte iki tuzak var:
| Tuzak | Açıklama |
|---|---|
| Rol patlaması | Her yeni yetki için yeni rol: admin, super_admin, mega_admin, god_mode... |
| İzin kontrolü yok | Kodda if (user.role === 'admin') var, ama admin rolünün hangisini kapsadığı merkezi tanımlı değil |
Neden eksik kalıyor?
Ne yapmalı?
can(user, 'posts:update', resource)| Boşluk | Sonuç |
|---|---|
| Token validation yok | Sahte token ile giriş |
| Scope kontrolü yok | Geçerli token ile yetki yükselmesi |
| RBAC yanlış | "Admin" her şeye erişir, en az yetki prensibi yok |
Küçük bir checklist 🛡️
Bir sonraki bölümde bu kontrolleri middleware / interceptor seviyesinde nasıl merkezi hale getireceğimize bakalım. Hazır mısınız? 🚀
Token doğrulama, API güvenliğinin kalbi 🎯. Yanlış veya eksik bir kontrol, hassas verilerinizin açık kapıdan çıkmasına neden olabilir. Hadi adım adım, Express + express-jwt ile nasıl sağlam bir validation kuracağınızı görelim.
express – web frameworkexpress-jwt – JWT middlewarejsonwebtoken – token oluşturmak / imzalamak (test için)dotenv – secret key’i .env dosyasından almaknpm i express express-jwt jsonwebtoken dotenv
// app.js
require('dotenv').config();
const express = require('express');
const { expressjwt: jwt } = require('express-jwt');
const app = express();
app.use(express.json());
// 🔐 Secret key ortam değişkeninden alınır
const secret = process.env.JWT_SECRET || 'super-secret-key';
// 🛡 JWT doğrulama middleware'i
const verifyToken = jwt({
secret,
algorithms: ['HS256'], // sadece HS256 kabul et
requestProperty: 'auth' // doğrulanmış payload → req.auth
});
// Korunan route örneği
app.get('/api/profile', verifyToken, (req, res) => {
// req.auth içinde token payload'i hazır
res.json({ message: 'Profil verisi', user: req.auth });
});
// ❗️ Hata yakalama middleware (express-jwt hatalarını yakalar)
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: 'Geçersiz veya süresi dolmuş token' });
}
next(err);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`🚀 Sunucu ${PORT} portunda çalışıyor`));
// generate-token.js
require('dotenv').config();
const jwt = require('jsonwebtoken');
const payload = { sub: '123', username: 'devci', role: 'admin' };
const token = jwt.sign(payload, process.env.JWT_SECRET || 'super-secret-key', {
expiresIn: '1h',
algorithm: 'HS256'
});
console.log('✅ Test token:', token);
Çalıştırın: node generate-token.js → elde edilen token’ı Authorization: Bearer <token> header’ında gönderin.
.env’den alınır 🔐requestProperty: 'auth' ile payload req.auth altında merkezi bir yerde toplanırBu şablonu alıp projenize göre genişletebilirsiniz (ör. rol tabanlı yetki, refresh token akışı, vs.). Bir sonraki adımda Token Revocation & Blacklist konusuna geçeceğiz 🚀
Hadi scope kontrolünü nasıl uygularız diye konuşalım. Token içinde scope claim'i geliyor ama bunu otomatik kontrol etmezseniz, kullanıcı "read" yetkisiyle "write" işlemi yapmaya çalışabilir. Bunu engellemek için FastAPI'de dependency injection kullanıyoruz 🎯
scope: "read write" gibi bir alan varÖnce bir yardımcı fonksiyon yazalım — token'dan scope'ları çıkarıp, gerekli scope var mı diye bakıyor:
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from typing import List
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
def get_current_user_scopes(token: str = Depends(oauth2_scheme)) -> List[str]:
"""
JWT token'ı decode edip scope listesini döndürür.
Gerçek projede `jwt.decode()` veya auth library'nizle yaparsınız.
"""
# Basit örnek: token payload'ını manuel parse ediyormuş gibi davranıyoruz
# Gerçekte: payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
payload = {"scopes": ["read", "write"]} # demo payload
return payload.get("scopes", [])
Şimdi endpoint bazında scope zorlayan dependency yazıyoruz:
def require_scopes(required_scopes: List[str]):
"""
Gerekli scope'ları kontrol eden dependency factory.
Kullanım: Depends(require_scopes(["write"]))
"""
def scope_checker(user_scopes: List[str] = Depends(get_current_user_scopes)):
missing = [s for s in required_scopes if s not in user_scopes]
if missing:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail={
"error": "insufficient_scope",
"message": f"Bu işlem için eksik scope(lar): {', '.join(missing)}",
"required_scopes": required_scopes,
"user_scopes": user_scopes,
},
headers={"WWW-Authenticate": f'Bearer scope="{ " ".join(required_scopes) }"'},
)
return scope_checker
from fastapi import APIRouter
router = APIRouter()
@router.get("/posts", dependencies=[Depends(require_scopes(["read"]))])
async def list_posts():
return {"posts": ["post-1", "post-2"]}
@router.post("/posts", dependencies=[Depends(require_scopes(["write"]))])
async def create_post(title: str):
return {"id": 3, "title": title}
| Senaryo | Sonuç |
|---|---|
read scope'lu token ile GET /posts |
✅ 200 OK |
read scope'lu token ile POST /posts |
⛔ 403 Forbidden + insufficient_scope |
| Scope'suz token | ⛔ 403 Forbidden |
Response örneği (hata durumunda):
{
"error": "insufficient_scope",
"message": "Bu işlem için eksik scope(lar): write",
"required_scopes": ["write"],
"user_scopes": ["read"]
}
WWW-Authenticate header'ına gerekli scope'ları ekledik — bu sayede client (ör. Postman, SPA) hata mesajını parse edip kullanıcıya "Yazma izniniz yok, lütfen 'write' scope'lu login olun" diyebilir.read:users, write:posts gibi fine-grained scope'lar daha güvenli.Hazırsan sıradaki adımda token yenileme (refresh token) ve rotation stratejisine bakalım 🔁
Her gün kullandığımız MCP sunucularının kökenine dikkat etmezsek, tüm sistem tehlikeye düşebilir. 🖐️
En basit hata bile zincirin diğer noktalarında büyük sorunlara neden olur. Hadi neden güvensiz MCP sunucuları tehlikeli olduğunu pratik bir örnek üzerinden quickly anlayalım.
git clone ile indirmegit clone https://github.com/trusted-org/mcp-secure-server.git /opt/mcp-server
cd /opt/mcp-server && npm install
main veya stable olur.docker pull ile indirmedocker pull trustedorg/mcp-server:latest
docker run -d -p 8080:8080 trustedorg/mcp-server:latest
trivy) – bilinen güvenlik açıklarını tespit et.gpg --verify komutunu kullanarak imzayı doğrula.İşte hızlı ve güvenli kurulum komutu. Güvenilir bir depodan bir MCP sunucusu indirir, ortam değişkenlerini ayarlar, başlangıç için dizini hazırlar ve sunucuyu başlatır.
#!/usr/bin/env bash
set -euo pipefail
# 1️⃣ Güvenilir depodan kaynak kodunu indir
git clone https://github.com/trusted-org/mcp-secure-server.git /opt/mcp-server
cd /opt/mcp-server
# 2️⃣ Bağımlılıkları güvenli bir şekilde yükle
npm ci --only=production
# 3️⃣ Çalışma ortamını yapılandır
cat > .env <<EOF
NODE_ENV=production
MCP_SERVER_PORT=8080
LOG_LEVEL=info
EOF
# 4️⃣ Güvenlik doğrulaması (basit hash kontrolü)
EXPECTED_SUM="a1b2c3d4..." # Satıcının yayınladığı SHA-256 hash'i
ACTUAL_SUM=$(sha256sum package.json | awk '{print $1}')
if [ "$EXPECTED_SUM" != "$ACTUAL_SUM" ]; then
echo "⛔ Checksum eşleşmiyor! İndirme işlemi iptal edildi."
exit 1
fi
# 5️⃣ Sunucuyu başlat
node server.js &
echo "✅ Güvenli MCP sunucusu başarıyla başlatıldı (PID $!)"
Bu sayede ne oluyor?
NODE_ENV=production ayarlarıyla çalışmaya hazırdır. 🚀Şimdi güvenilir bir MCP sunucusuna sahibiz → zincirin geri kalanında güvenli bir şekilde ilerleyebiliriz. 📦✨
Hazırsan bu 5 maddelik kontrol listesini hızlıca gözden geçirelim. Her satırda ne kontrol edeceğiz, neden önemli ve nasıl doğrulayacağın özetlenmiş. Tabloyu kopyala, kendi ortamına uyarlayın ve ✅ / ❌ ile işaretle.
| # | Kontrol Alanı | Ne Yapılmalı? | Neden Önemli? | Durum |
|---|---|---|---|---|
| 1 | Token Kontrolü 🎫 | - Access token’ın geçerli olup olmadığını doğrula- Yenileme (refresh) token akışını test et | Yetkisiz erişimi engeller, oturum hırsızlığını önler | ⬜ |
| 2 | Scope (Kapsam) Kontrolü 🎯 | - İstenen scope’lar minimal mi? (principle of least privilege)- Gereksiz scope’ları kaldır | Aşırı yetki riskini azaltır, ihlal etkisini sınırlar | ⬜ |
| 3 | Kaynak İzinleri 🔐 | - Her API endpoint’ine sadece gerekli roller erişebilsin- RBAC/ABAC kurallarını gözden geçir | Yanlışlıkla veri sızıntısını veya manipülasyonu engeller | ⬜ |
| 4 | Loglama & İzleme 📋 | - Tüm auth olaylarını (login, token refresh, hata) merkezi loga yaz- Anomali tespiti için alarm kuralları oluştur | Şüpheli aktiviteyi erken fark et, forensic analiz için kanıt sağla | ⬜ |
| 5 | Güncel Kalma & Güvenlik Taraması 🛡️ | - Bağımlılıkları (npm, pip, Maven…) haftalık tarayın- CVE bildirimlerini takip edip yamaları hemen uygulayın | Bilinen zaafiyetlerin istismar edilmesini önler, uyumluluğu korur | ⬜ |
Kısa özet:
Her maddeyi ✅ işaretlediğinde MCP sunucunuzun güvenlik pozisyonu bir adım daha ileri gitmiş olur. Hadi kontrolü bitirelim ve production’a güvenle gidelim! 🚀
Güvenlik tek seferlik bir iş değil, sürekli bir alışkanlık 🎯
Hadi bunu rutininize nasıl sokabileceğinizi konuşalım:
Manifeste veya config dosyalarında oynama yaptığınızda, eski cache'ler işinizi karıştırabilir. Terminalden tek komutla temiz başlangıç:
rm -rf ~/.mcp && mcp-server reset --force
Ne oluyor burada?
~/.mcp dizini silinir → tüm local state temizlenirmcp-server reset --force ile sunucu fabrika ayarlarına dönerBu komutu bir alias olarak .bashrc / .zshrc içine atarsanız mcp-reset yazmak yetar ✅
npm audit, pip-audit, trivy fs .) takvimine koyungitguardian, truffleHog, gitleaks) → push anında uyarı alsınTek başınıza her threat'i takip etmek imkansız. Şu kanalları takip edin, soru sorun, deneyim paylaşın:
devsecops-tr, sec-tr, cloud-native-trÖzetle:
1️⃣ Temizleme komutunu alias yapın → anında sıfırlama
2️⃣ Küçük ama düzenli güvenlik rutinleri koyun takvime
3️⃣ Toplulukla bağlantıda kalın → bilgi akışı kesilmez
Güvenlik kültür olunca, araçlar arka planda çalışır 🚀
Arkadaşlar, bu yolculuğun sonuna geldik. Ama aslında bu bir bitiş değil, bir başlangıç 🚀
Güvenlik, bir kez yazıp unuttuğumuz bir "checklist" maddesi değil. Sürekli değişen, büyüyen, öğrenen bir canlı organizma gibi davranır. Bugün yazdığımız bir kural, yarın yeni bir saldırı vektörüyle karşılaştığında güncellenmek zorunda kalır. İşte tam bu noktada topluluk devreye girer.
Güvenlik maliyet değil, yatırımdır. Müşteri güveni, marka itibarı, yasal uyumluluk... Hepsi güvenlik sayesinde korunan varlıklar.
Hadi, birlikte daha güvenli kod yazalım. Bir sonraki PR'da görüşmek üzere! 👋
Bu yazı dizisini takip ettiyseniz, elinize sağlık. Sorularınız, eklemeleriniz veya "Bunu denedim, şöyle oldu" deneyimleriniz varsa yorumlarda buluşalım. Topluluk ne kadar güçlü olursa, kodlarımız o kadar güvenli olur. ✨
Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.
All rights reserved