Güvenli Kod Geliştirme: SAST ve DAST Araçları ile Uygulama Güvenliği
Yazılım geliştirme süreçlerinde güvenlik, artık “sonradan düşünülecek bir şey” olmaktan çıktı. Üretim ortamına geçtikten sonra bulunan bir açığın maliyeti, geliştirme aşamasında tespit edilenin onlarca katı olabiliyor. Bunu bir kez ciddi bir incident yaşadıktan sonra anlıyorsunuz, maalesef. SAST ve DAST araçları tam bu noktada devreye giriyor: biri kodu henüz çalışmadan tararken, diğeri çalışan uygulamayı gerçek bir saldırgan gözüyle test ediyor.
SAST ve DAST Arasındaki Farkı Gerçekten Anlamak
Çoğu zaman bu iki kavram birbirine karıştırılıyor ya da “ikisi de güvenlik taraması yapar” diye geçiştirilip sadece biri kullanılıyor. Oysa bunlar birbirini tamamlayan, birbirinin yerine geçemeyen iki farklı yaklaşım.
SAST (Static Application Security Testing), kaynak kodu, bytecode veya binary’yi çalıştırmadan analiz eder. Beyaz kutu testidir. Geliştirici ne yazdıysa onu görür, SQL injection’a açık bir string concatenation, hardcoded credentials, güvensiz bir kriptografi kullanımı… Bunları kod derlenmeden, hatta çalışmadan bile bulabilirsiniz.
DAST (Dynamic Application Security Testing) ise tam tersi. Uygulamayı çalıştırır, dışarıdan HTTP istekleri gönderir, yanıtları analiz eder, fuzzing yapar. Siyah kutu testidir. Kaynak kodunuza erişimi yoktur, uygulamanızın dış yüzüne bakarak zafiyetleri tespit eder.
İkisinin de körü körüne güvenilmemesi gereken bir gerçek var: SAST’ın yüksek false positive oranı var, DAST ise bazı logic flaw’ları asla bulamaz. CI/CD pipeline’ınıza ikisini de entegre etmek, gerçek anlamda “Shift Left Security” demektir.
SAST Araçları ve Kurulum
Semgrep ile Başlamak
Semgrep, benim gördüğüm en kullanılabilir SAST araçlarından biri. Hem açık kaynak rule set’leri var, hem de kendi kurallarınızı kolayca yazabiliyorsunuz. Python, JavaScript, Go, Java, C/C++ dahil onlarca dili destekliyor.
# pip ile kurulum
pip install semgrep
# Temel tarama - mevcut dizinde tüm dosyaları tara
semgrep --config=auto .
# Sadece güvenlik odaklı kurallarla tara
semgrep --config=p/security-audit .
# Belirli bir dil için
semgrep --config=p/python .
# JSON çıktısı almak için (CI/CD entegrasyonunda işe yarar)
semgrep --config=auto --json -o results.json .
Örnek bir senaryo: Bir Python web uygulamasında geliştirici şöyle bir kod yazmış:
# Kötü örnek - SQL injection açığı
query = "SELECT * FROM users WHERE id = " + user_input
cursor.execute(query)
Semgrep bunu anında yakalar. Çıktı şuna benzer bir şey olur:
# Semgrep çıktı örneği simülasyonu
semgrep --config=p/python ./app/
# Çıktı:
# app/db.py:14: [ERROR] sql-injection: Possible SQL injection
# Found string concatenation with a non-literal in SQL query
# Severity: ERROR | Rule: python.lang.security.audit.sqli
Bandit: Python Projeleri İçin
Python yazıyorsanız Bandit’i ihmal etmeyin. Özellikle eski Python kod tabanlarında hardcoded password, insecure deserialization ve benzeri sorunları bulmakta çok başarılı.
# Kurulum
pip install bandit
# Temel kullanım
bandit -r ./myapp/
# Sadece yüksek severity sorunları göster
bandit -r ./myapp/ -l -i
# HTML rapor üret
bandit -r ./myapp/ -f html -o bandit_report.html
# Belirli test ID'lerini atla (false positive yönetimi)
bandit -r ./myapp/ --skip B101,B601
Gerçek bir projede Bandit çalıştırdığınızda şöyle sonuçlarla karşılaşabilirsiniz:
bandit -r ./webapp/ -ll
# >> Issue: [B106:hardcoded_password_funcarg] Possible hardcoded password: 'admin123'
# Severity: Low Confidence: Medium
# Location: webapp/config.py:8
#
# >> Issue: [B501:request_with_no_cert_validation] Requests call with verify=False
# Severity: High Confidence: High
# Location: webapp/api_client.py:23
B501 bulgusuna dikkat edin. verify=False kullanımı production kodunda ciddi bir sorun. MITM saldırılarına kapı aralıyor.
SonarQube: Kurumsal Ortamlar İçin
Büyük ekiplerde, birden fazla projeyi takip etmek gerektiğinde SonarQube tercih edilebilir. Docker ile kurulumu hızlı:
# Docker Compose ile SonarQube
cat > docker-compose.yml << 'EOF'
version: "3"
services:
sonarqube:
image: sonarqube:community
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
db:
image: postgres:13
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgresql:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_logs:
postgresql:
EOF
docker-compose up -d
Kurulum sonrası http://localhost:9000 adresine gittiğinizde admin panelini göreceksiniz. İlk login admin/admin ile yapılıyor, hemen değiştirin.
DAST Araçları ve Kullanımı
OWASP ZAP: Herkesin Bilmesi Gereken Araç
ZAP (Zed Attack Proxy), OWASP’ın ücretsiz ve açık kaynak DAST aracı. Hem GUI hem de headless modda çalışıyor. CI/CD entegrasyonu için headless mod çok değerli.
# Docker ile ZAP kurulumu ve temel tarama
docker pull owasp/zap2docker-stable
# Hızlı tarama (Baseline Scan)
docker run -t owasp/zap2docker-stable zap-baseline.py
-t https://hedef-uygulama.local
-r zap_baseline_report.html
# Tam tarama (Full Scan) - daha uzun sürer
docker run -t owasp/zap2docker-stable zap-full-scan.py
-t https://hedef-uygulama.local
-r zap_full_report.html
-J zap_report.json
# API taraması için (OpenAPI/Swagger desteği)
docker run -t owasp/zap2docker-stable zap-api-scan.py
-t https://hedef-uygulama.local/api/openapi.json
-f openapi
-r zap_api_report.html
ZAP’ın baseline scan’i, false positive oranı düşük, hızlı bir tarama sunar. Her PR’da çalıştırılabilecek bir seviye. Full scan ise gece çalıştırıp sabah raporunu okuduğunuz türden.
Nikto: Hızlı Web Sunucu Güvenlik Taraması
Nikto DAST kategorisine tam olarak girmez ama web sunucu konfigürasyonundaki açıkları bulmakta yeri doldurulamaz. X-Frame-Options eksikliği, eski TLS sürümleri, gereksiz HTTP metodları…
# Temel Nikto taraması
nikto -h https://hedef-uygulama.local
# Belirli portları tara
nikto -h hedef-uygulama.local -p 8080,8443
# Çıktıyı dosyaya kaydet
nikto -h https://hedef-uygulama.local -o nikto_report.html -Format htm
# SSL sorunlarına odaklan
nikto -h https://hedef-uygulama.local -ssl
# Taramayı belirli testlerle sınırla
nikto -h https://hedef-uygulama.local -Tuning 1,2,3,4
Tuning parametresi önemli:
- 1: Interesting File / Seen in logs
- 2: Misconfiguration / Default File
- 3: Information Disclosure
- 4: Injection (XSS/Script/HTML)
CI/CD Pipeline Entegrasyonu: Gerçek Dünya Senaryosu
İşte burada işler ilginçleşiyor. Araçları tek tek çalıştırmak yeterli değil, bunları developer workflow’una gömmek gerekiyor. Bir GitHub Actions örneği:
# .github/workflows/security-scan.yml
name: Security Scanning Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
sast-scan:
name: SAST - Semgrep
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
bandit-scan:
name: SAST - Bandit (Python)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Bandit
run: pip install bandit
- name: Run Bandit
run: |
bandit -r ./src/
-f json
-o bandit-output.json
-ll || true
- name: Upload Bandit results
uses: actions/upload-artifact@v3
with:
name: bandit-results
path: bandit-output.json
dast-scan:
name: DAST - OWASP ZAP
runs-on: ubuntu-latest
needs: [sast-scan]
steps:
- name: Start test application
run: docker-compose up -d
- name: Wait for app to be ready
run: sleep 15
- name: ZAP Baseline Scan
uses: zaproxy/[email protected]
with:
target: 'http://localhost:8080'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
Bu pipeline’da dikkat edilmesi gereken bir nokta: DAST taraması için uygulamanın ayağa kaldırılması gerekiyor. Staging ortamınızda değil, CI runner’da ephemeral bir ortam kurmak daha güvenli.
False Positive Yönetimi: En Can Sıkıcı Kısım
Araçları kurdunuz, pipeline’a entegre ettiniz, harika. Şimdi 300 tane bulgu var önünüzde ve yarısı false positive. Geliştiriciler şikayet etmeye başlıyor: “Bu araç sürekli yanlış alarm veriyor.” O noktada insanlar alarm yorgunluğuna giriyor ve tüm bulguları görmezden gelmeye başlıyor.
Bunun önüne geçmek için bir strateji oluşturun:
# Semgrep'te false positive yönetimi
# Kod satırına inline comment ekleyerek belirli bir kuralı atla
result = eval(user_input) # nosemgrep: python.lang.security.audit.eval-detected
# Bandit'te dosya bazlı ignore
# .bandit dosyası oluştur
cat > .bandit << 'EOF'
[bandit]
exclude_dirs = ['tests', 'docs']
skips = ['B101']
EOF
# Semgrep konfigürasyon dosyası ile yönetim
cat > .semgrep.yml << 'EOF'
rules:
- id: custom-sql-injection-check
patterns:
- pattern: |
$QUERY = "..." + $USER_INPUT
cursor.execute($QUERY)
message: SQL injection riski tespit edildi
languages: [python]
severity: ERROR
metadata:
category: security
technology:
- python
EOF
semgrep --config=.semgrep.yml ./src/
Özel Kural Yazımı: SAST’ı Projenize Uyarlamak
Generic kurallar başlangıç için iyi, ama projenize özel iş mantığı açıklarını yakalamak istiyorsanız kendi kurallarınızı yazmanız gerekiyor. Örneğin, şirketinizin dahili API’sinin belirli bir şekilde çağrılması gerekiyorsa ve bu kural çiğnendiğinde güvenlik açığı oluşuyorsa, Semgrep ile bunu otomatize edebilirsiniz:
# .semgrep/custom-rules.yml
rules:
- id: internal-api-auth-bypass
pattern: |
requests.get("https://internal-api.company.com/...", ...)
pattern-not: |
requests.get("https://internal-api.company.com/...",
headers={"Authorization": ...}, ...)
message: |
Internal API çağrısında Authorization header eksik.
Bu endpoint authentication gerektiriyor.
languages: [python]
severity: ERROR
- id: hardcoded-jwt-secret
patterns:
- pattern: |
jwt.encode($PAYLOAD, "$SECRET", ...)
- metavariable-regex:
metavariable: $SECRET
regex: '^[a-zA-Z0-9]{8,}$'
message: JWT secret hardcoded görünüyor, environment variable kullanın
languages: [python]
severity: WARNING
Raporlama ve Takip
Bulguları raporlamak ve takip etmek için birkaç pratik yaklaşım:
# Semgrep bulgularını JSON'dan okunabilir bir özete çevir
semgrep --config=auto --json . | python3 - << 'EOF'
import json, sys
data = json.load(sys.stdin)
results = data.get("results", [])
severity_counts = {}
for r in results:
sev = r.get("extra", {}).get("severity", "UNKNOWN")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
print("=== Güvenlik Tarama Özeti ===")
for sev, count in sorted(severity_counts.items()):
print(f"{sev}: {count} bulgu")
print(f"nToplam: {len(results)} bulgu")
EOF
# ZAP raporunu JSON'dan parse et ve kritik bulguları filtrele
cat zap_report.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
alerts = data.get('site', [{}])[0].get('alerts', [])
critical = [a for a in alerts if int(a.get('riskcode', 0)) >= 3]
print(f'Kritik bulgular: {len(critical)}')
for a in critical:
print(f' - {a["name"]}: {a["url"]}')
"
Güvenli Geliştirme Kültürü Oluşturmak
Araçlar tek başına yeterli değil. Geliştiricilerin bu araçları düşman olarak değil, yardımcı olarak görmesi gerekiyor. Bunun için birkaç öneri:
Developer ortamına entegrasyon: Pre-commit hook’ları ile Semgrep ve Bandit’i commit aşamasında çalıştırın. Böylece CI’ya göndermeden önce developer kendi bilgisayarında bulguyu görür.
# pre-commit hook kurulumu
pip install pre-commit
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
- repo: https://github.com/returntocorp/semgrep
rev: v1.45.0
hooks:
- id: semgrep
args: ["--config=p/security-audit", "--error"]
EOF
pre-commit install
Severity bazlı gate’ler: Her bulguyu pipeline’ı durduracak şekilde ayarlamayın. Sadece HIGH ve CRITICAL bulgular için build’i fail edin, LOW bulguları ise raporlayın ama engellemeyin.
Periyodik tam taramalar: CI/CD’deki hızlı taramaların yanı sıra, haftada bir geceyarısı tam ZAP taraması çalıştırın ve sonuçları bir Slack kanalına gönderin.
Sonuç
SAST ve DAST araçlarını doğru şekilde kullanmak, güvenlik ekibi ile geliştirme ekibinin kavga etmesini önlüyor. Developer kodu yazarken zaten uyarı alıyor, CI/CD’de tekrar kontrol ediliyor, staging ortamında DAST taranıyor. Bu üç katmanlı yaklaşım, hiçbir katmanın tek başına sunamayacağı bir güvenlik derinliği sağlıyor.
Başlangıç için gözünüzü korkutmasın. Mevcut bir projeniz varsa, önce Bandit veya Semgrep’i sadece raporlama modunda çalıştırın, pipeline’ı durdurmadan. Bulguları inceleyin, false positive’leri temizleyin, gerçek sorunları düzeltin. Sonra yavaş yavaş severity eşiklerini koyun. Kültür değişikliği araç kurulumundan çok daha uzun sürer, sabırlı olun.
Son bir not: Bu araçların çıktılarına kör güvenmeyin. Bir SAST aracı “temiz” dedi diye kod güvenlidir demek değil, ya da “100 bulgu var” dedi diye kod felaket demek değil. Bu araçlar bir güvenlik uzmanının yerini tutmuyor, onu destekliyor.
