# Norden — Güvenlik denetimi, R2 ve alan adı durumu

Tarih: 15 Eylül 2026  
İncelenen uygulama: `99e5b8f` üzerindeki kaynak kod ve bu kaynakla yeniden üretilen production build  
Karar: **İki uygulama güvenlik kusuru yerelde düzeltildi ve regresyon testine bağlandı. Canlı yayın güvenlik kabulü; alan adı, edge, VPS, PostgreSQL, MFA, yedek ve staging doğrulamaları tamamlanmadığı için henüz verilmedi.**

## 1. Kapsam ve yöntem

Kaynak kod, kilitli bağımlılıklar, Git/Docker dosya sınırları, yönetici izinleri, oturumlar, dosya yükleme, HTTP başlıkları, deployment dosyaları, Cloudflare R2/alan adı ekranları, GitHub güvenlik ekranı ve alan adının kamuya açık RDAP/DNS kayıtları incelendi.

Bugünkü HTTP ve veri değiştiren güvenlik testleri yalnız `127.0.0.1:3100` ve `tmp/security-audit-20260915.db` üzerinde yapıldı. Test veritabanı public katalog seed'inden üretildi; gerçek yönetici hesabı veya mevcut içerik veritabanı değiştirilmedi. Denetim, mevcut internetteki eski siteye saldırı/yük testi içermedi.

Docker Linux motoru bu turda çalışmıyordu; sandbox dışındaki salt okunur sorguda da motorun named pipe'ı bulunamadı. Bu nedenle bugünkü dinamik testler SQLite ile yapıldı. PostgreSQL ve container içi testleri bugünkü doğrulama gibi göstermemek gerekir.

14 Eylül PostgreSQL göç ve restore sonuçları `15-SQLITE-POSTGRESQL-GUVENLI-TASIMA.md` ve `16-POSTGRESQL-YEDEK-VE-RESTORE-PROVASI.md` kayıtlarında mevcut; bu turda yeniden çalıştırılmadı.

## 2. Doğrulanan korumalar

| Alan | Bugünkü kanıt | Sınır |
|---|---|---|
| Bağımlılıklar | `npm audit --json`: tüm bağımlılıklarda 0 bilinen açık | İş mantığı kusurlarını ve container/OS açıklarını taramaz |
| Build | `npm run build` ve build içi TypeScript kontrolü geçti; Next.js 16.3.4 | Production sunucu kabulü değildir |
| GitHub gizlilik | Web arayüzünde `Quivermist/norden-katalog` Private; güvenlik düzeltmesi `dfd8c68` ve Quality Gate #7 başarılı | Hesap MFA, kurtarma ve erişim üyeleri ayrıca denetlenmeli |
| GitHub bağımlılık güvenliği | Dependency Graph, Dependabot zafiyet uyarıları ve kötü amaçlı paket uyarıları açık | Otomatik güvenlik/sürüm PR'ları bilinçli olarak kapalı; güncelleme insan onayıyla yapılmalı |
| GitHub dal koruması | Classic branch protection ekranı incelendi; kural oluşturulmadı | GitHub, mevcut kişisel/private planda kuralların uygulanmayacağını ve Team/Enterprise organizasyonu gerektiğini açıkça bildirdi |
| Git sınırı | Mevcut 474 dosyada depo güvenliği denetimi geçti; `.env` ve `.env.docker` takip edilmiyor | Tarama belirli yüksek güvenli anahtar kalıplarını kapsar; bütün sır türlerini bulma garantisi yok |
| Git geçmişi | `.env`, `.env.docker`, `*.db`, `*.pem`, `backups/*`, `source-assets/*` için geçmişte kayıt bulunmadı | Bütün tarihçedeki tüm metinler için kapsamlı secret taraması yapılmadı |
| Tarayıcıya sır sızması | Yerel ortamdan 4 hassas değer, üretilen 35 istemci JS/JSON dosyasında arandı: eşleşme yok | Değerler rapora veya çıktıya yazılmadı |
| Oturum | 32 rastgele byte token; veritabanında SHA-256 özeti; 8 saat süre; HttpOnly, production Secure, SameSite=Strict | Hesap MFA ve ek yönetici erişim katmanı yok |
| Parola | bcrypt maliyet 12, parola yenileme ve geçici parola kapısı; tüm giriş/oluşturma/sıfırlama/değiştirme yollarında 72 UTF-8 byte üstü reddediliyor | Kullanıcı parolaları için MFA ve production kimlik politikası ayrıca tamamlanmalı |
| Yetki | Güncellenen admin RBAC testinde 26 kontrol geçti; doğrudan Server Action denemesinde düzenleme yetkisi arşivlemeye dönüştürülemedi | Seri ve galeri için aynı ortak kontrol kaynakta uygulanıyor; ürün dışındaki geçişler ayrıca HTTP ile üretilmedi |
| CSRF | Upload Origin kontrolü ve SameSite oturum; farklı Origin yüklemesi 403 | Next.js Server Actions Origin/Host kontrolüne ek olarak gerçek proxy başlıkları doğrulanmalı |
| Oturum iptali | Kapalı kullanıcı ve iptal edilmiş oturumla erişim engellendi | Canlı ortamda HTTPS üzerinden aynı test gerekli |
| Upload | Oturumsuz 401; izin verilmeyen modülde 403; sahte PNG, sahte MP4, SVG ve 11 dosyalı istekler 400 | Bunlar içerik/biçim doğrulamasıdır; antivirüs/CDR veya tüm video içeriği analizi değildir |
| Dosya sınırları | Görsel 15 MiB, video 150 MiB, 1–10 dosya, toplam 300 MiB; SHA-256 dosya adı | Toplam depolama kotası, kullanıcı başına yükleme hızı ve eşzamanlılık sınırı yok |
| XSS/SQL yaklaşımı | React kaçışlama; JSON-LD `<` kaçışlaması; uygulama sorgularında Prisma; taranan uygulama kodunda raw SQL/exec/eval görülmedi | Tek başına bütün XSS/SQL saldırılarının imkânsız olduğunu göstermez |
| HTTP | nosniff, SAMEORIGIN, Referrer-Policy, Permissions-Policy ve production HSTS başlıkları mevcut | HTTP localhost testinde HSTS'nin tarayıcı koruması sınanmaz; gerçek TLS testi gerekli |
| CSP | Production yanıtında engelleyici `Content-Security-Policy`; istek başına 122-bit nonce, `strict-dynamic`, script tarafında `unsafe-inline` yok; 20/20 script aynı nonce ile eşleşti | Çok sayıdaki React/Framer inline stil nedeniyle `style-src 'unsafe-inline'` sürüyor; gerçek R2 alan adı ve staging tarayıcı kabulü gerekli |
| Admin cache | Admin ve admin API no-store; CDN no-store; admin noindex | robots/noindex erişim kontrolü değildir |
| Özel dosyalar | `.env`, `.env.production`, `.git/config`, `dev.db`, `package.json`, kaynak ve backup yolları 404 | Gerçek Nginx/Cloudflare üzerinden de sınanmalı |
| Docker tasarımı | Web `USER node`, cap_drop ALL, no-new-privileges; DB port publish yok, iç DB ağı | Yalnız rehearsal Compose mevcut; canlı OS/firewall/TLS kurulmadı |
| PostgreSQL tasarımı | Uygulama rolü superuser/createdb/createrole değil | Uygulama rolü veritabanı sahibi ve migration rolüyle aynı; production'da ayrı migration ve kısıtlı runtime rolü tercih edilmeli |
| Yedek aracı | `umask 077`, PGPASSFILE, custom dump, SHA-256, arşiv okunabilirliği kontrolü | Şifreli off-site otomasyon ve Hostinger üzerinde geri yükleme kabulü bekliyor |

## 3. Bulunan ve yerelde kapatılan uygulama kusurları

### S-01 — P1: Düzenleme üzerinden arşivleme yetkisi aşımı — kapatıldı

Kaynak: `app/admin/actions.ts` → `saveProduct`, özellikle durum seçimi ve yayın izni kontrolü (yaklaşık satır 190).

- Test hesabı editor; `canEdit=true`, `canArchive=false`, `canPublish=false`.
- Yalnız test için oluşturulan taslak ürün, gerçek Server Action isteğiyle `status=archived` olarak kaydedildi.
- Yanıt HTTP 200 ve `success=true`; veritabanında `draft -> archived` geçişi doğrulandı.
- İlk testte ayrı `deleteProduct` işlemi arşivleme iznini kontrol ederken `saveProduct` aynı durum geçişini kontrol etmiyordu.
- Düzeltmede mevcut ve hedef durum birlikte denetleniyor. Yayına alma/yayından çıkarma `publish`; arşive alma/arşivden çıkarma `archive` yetkisi gerektiriyor. Ortak kontrol ürün, seri ve galeri kaydetme işlemlerine uygulandı.
- Düzeltme sonrası aynı doğrudan Server Action isteği HTTP 200 dönse de `success` üretmedi ve ürün `draft` kaldı; yetki sınırı korundu.
- Bu senaryo `scripts/audit-admin-rbac.mjs` içine başarısız olduğunda kalite kapısını durduracak kalıcı regresyon olarak eklendi.

Kalan kabul: Ürün testi gerçek HTTP/Server Action yolundan geçti. Seri ve galeri aynı ortak politika ile korunuyor; staging kabulünde bu iki modülün doğrudan istekleri de ayrıca sınanmalı.

### S-02 — P1: Paralel hatalı girişlerde sayaç kaybı — kapatıldı

Kaynak: `lib/admin-rate-limit.ts` → `registerAdminLoginFailure` (yaklaşık satır 37–40).

- Ardışık 5 hata doğru şekilde engelleniyor.
- Aynı anahtarlara eşzamanlı 8 hata kaydı gönderildiğinde her iki sayaç da 1 olarak kaldı; `allowed=true` döndü.
- Eski kod önce sayıyı okuyor, sonra hesapladığı değeri yazıyordu. Eşzamanlı çağrılar birbirinin artışını silebiliyordu.
- Düzeltmede pencere temizleme, atomik `increment` ve blok süresi güncellemesi tek veritabanı işlemi grubuna alındı.
- Düzeltme sonrası aynı anda gönderilen 8 hata iki anahtarda da 8 olarak saklandı ve `allowed=false` döndü.
- Eşzamanlı test `scripts/audit-admin-rbac.mjs` içine kalıcı regresyon olarak eklendi. Test yalnız yerel dosya veritabanı ve localhost hedefini kabul ederek yanlışlıkla canlı sisteme karşı çalışmayı reddediyor.
- Bugünkü doğrulama izole SQLite veritabanıyla yapıldı; üretim PostgreSQL eşzamanlılık kabulü ayrıca gerekli.
- Anahtarlar IP ve IP+kullanıcı birleşimine bağlı; IP değiştirerek denemeye karşı hesap düzeyinde ek sınır yok.

Kalan kabul: Güvenilir proxy IP zinciri, PostgreSQL üzerinde paralel test ve yönetici MFA/Access katmanı. Proxy'de giriş hızının ayrıca sınırlanması CPU ve veritabanı yükünü azaltır.

Kod düzeltmeleri `npm run lint`, `npx tsc --noEmit`, `npm run build`, izole saldırı tekrarı ve güncellenen 26 maddelik admin denetiminden geçti. Bu sonuç yerel uygulama kusurlarını kapatır; canlı altyapı kabulü yerine geçmez.

## 4. Canlıya geçişte tamamlanacak güvenlikler

| Kod | Öncelik | Açık iş | Kabul ölçütü |
|---|---|---|---|
| S-03 | P1 | **Yerelde kapatıldı:** Report-Only kaldırıldı; engelleyici CSP, istek başına nonce ve `strict-dynamic` uygulandı. Tema/JSON-LD/Next scriptleri nonce taşıyor; 6 kritik rota, 432 public rota, PDF/harita, SEO ve performans kontrolleri geçti | R2 custom domain eklendiğinde CDN kaynağıyla ve staging tarayıcılarında yeniden kabul; inline React/Framer stilleri nedeniyle kalan `style-src 'unsafe-inline'` riskini azaltma planı |
| S-04 | P0 yayın kapısı | Cloudflare hesabında hiç alan adı yok | Yetkili DNS geçişi, active zone, gerekli web kayıtları proxied, WAF/DDoS ve cache kabulü |
| S-05 | P0 admin kapısı | Admin MFA/VPN/Cloudflare Access kurulumu yok | Yönetici kimliği için ek doğrulama; doğrudan API ve origin üzerinden atlanamadığının testi |
| S-06 | P0 yayın kapısı | Production reverse proxy ve VPS güvenliği henüz doğrulanmadı | SSH anahtarı, uygun firewall, 5432/3000 dışarı kapalı, HTTPS, origin yalnız Cloudflare veya Tunnel, doğru IP/Host başlıkları |
| S-07 | P1 | Public/login/upload için gerçek edge/proxy hız ve eşzamanlılık sınırları yok | Normal kullanımı bozmayacak limitler, 429/413 kabulü; upload body streaming/erken kesme |
| S-08 | P1 | R2 maliyet saldırısı/kapasite sınırı hazır değil | Custom domain cache, yalnız gereken yöntemler, uygun cache key, kota/uyarılar, büyük yükleme sınırı |
| S-09 | P1 | Dependency Graph, Dependabot zafiyet ve malware uyarıları açıldı; otomatik PR kapalı. Code scanning ve zorunlu dal koruması mevcut private hesap planında uygulanamıyor | Uyarıları düzenli inceleme sorumlusu; gerekirse GitHub Team/Enterprise organizasyonuna planlı geçiş. Ücretli hizmet otomatik alınmaz |
| S-13 | P1 | **Yerelde kapatıldı:** bcrypt'in 72-byte sonrası yok sayma davranışı tüm admin parola akışlarında sunucu tarafında engellendi; ASCII ve çok baytlı sınır testleri eklendi | Production/staging üzerinde gerçek hesapla giriş, parola değiştirme ve kurtarma kabulü |
| S-10 | P0 işletim kapısı | Otomatik şifreli dış yedek ve farklı sunucuda restore yok | Günlük yedek, ayrı anahtar, saklama planı, restore ve başarısız yedek bildirimi |
| S-11 | P1 | Hesap 2FA, recovery, ekip erişimi, log alarmları doğrulanmadı | GitHub/Cloudflare/Hostinger/DNS hesap sahipliği, MFA, kurtarma, en az yetki ve olay müdahale akışı |
| S-12 | P1 | Container güvenlik taraması/resource limitleri/OS güncelleme planı doğrulanmadı | Production imaj taraması, kaynak sınırları, log rotation, güncelleme/geri dönüş prosedürü |

Rehearsal Nginx şu an admin yollarını 403 ile kapatacak şekilde tasarlanmış. S-01/S-02/S-13 yerelde kapandı; bu dış sınır, Cloudflare Access/VPN, production proxy ve MFA kabulü tamamlanana kadar korunmalı.

`req.formData()` boyut ve modül denetimlerinin bir bölümünden önce bütün formu okuyor. Content-Length kontrolü tek başına güvenilir toplam okuma sınırı sağlamaz. Uygulama katmanına ulaşmadan gerçek proxy/streaming sınırı uygulanması gerekli.

Admin işlem geçmişi mevcut, fakat bütün login/upload olayları ayrı bir güvenlik olay sistemine aktarılmıyor; audit kaydı yazılamadığında işlem devam ediyor. Logların değiştirilemez dış kopyası ve alarm hedefleri henüz kurulmadı.

## 5. R2: Bu turda tamamlanan sonraki adım

Kullanıcının R2 aboneliğini etkinleştirmesinden sonra:

| Depo | Sınıf | Yerleşim | Public erişim | Veri |
|---|---|---|---|---|
| `norden-public-media` | Standard | Eastern Europe (EEUR) | Disabled | 0 B |
| `norden-private-backups` | Standard | Eastern Europe (EEUR) | Disabled | 0 B |

Her iki depo oluşturuldu ve panelde doğrulandı. Medya deposunda custom domain yok; `r2.dev` kapalı. İsmin public olması erişimi kendiliğinden açmaz. Web dosyaları ve yedekler henüz yüklenmedi; uygulama R2'ye bağlanmış sayılmaz. Yeni API anahtarı oluşturulmadı.

Hedef medya adresi `https://media.norden-haus.com`. Hesapta `norden-haus.com` zone'u bulunmadığından custom domain kurulumu alan adı yetkisine bağlı. Yedek deposu herkese kapalı kalmalı; oraya yalnız şifreli yedek gönderilmeli.

Bütçe uyarısı formu incelendi; alıcı alanı boş ve kullanıcı tarafından bildirim alıcısı belirtilmediği için bildirim kaydedilmedi. İleride 1 USD ve 5 USD eşikleri öneriliyor. **Bütçe uyarısı harcamayı durdurmaz.** İki bucket aynı hesaptaki ücretsiz kullanım kotasını paylaşır.

R2 aboneliği depolama hizmetidir. Public dosyalara yönelen geçerli istekler, depolama 10 GB altında olsa bile işlem maliyeti doğurabilir. DDoS filtreleri bütün istenmeyen trafiği veya bütün maliyet artışlarını engelleme garantisi vermez. Kullanıcı tarafından gelen Referrer bilgisi de tek başına güvenilir yetkilendirme değildir.

## 6. Alan adı: Resmi kayıt ve yönetim erişimi

15 Eylül tarihli Verisign RDAP sorgusu:

- Alan adı: `NORDEN-HAUS.COM`.
- Registrar: **ODTÜ Geliştirme Vakfı Bilgi Teknolojileri Sanayi ve Ticaret A.Ş. / METUnic**; IANA ID **3871**.
- İlk kayıt: **2 Kasım 2022**.
- Registry expiration: **2 Kasım 2026, 10:23 UTC**. Otomatik yenileme durumu yalnız hesap panelinden doğrulanabilir.
- Registry isim sunucuları: `NS1.ELSEZBILISIM.COM`, `NS2.ELSEZBILISIM.COM`.
- Registry DNSSEC delegationSigned: false.
- Yerel recursive DNS, NS olarak `ns1.cprapid.com` / `ns2.cprapid.com` döndürdü. Bu farkın nedeni doğrulanmadı; registrar ve mevcut DNS panelindeki kayıtlar taşınmadan karşılaştırılmalı. Doğrudan .com DNS sorgusu bu ortamda sunucu hatası verdi.
- MX kayıtları Google sunucularına yöneliyor. SPF kaydı Google ve mevcut sunucu yetkilendirmesi içeriyor. Bu, mevcut e-posta DNS'inin korunmasını gerektirir; Google aboneliği veya mailbox durumu bu DNS sorgusundan doğrulanamaz.

METUnic resmi kayıt firmasıdır; alan adı patron tarafından bir bayi/ajans üzerinden de alınmış olabilir. İsim sunucularındaki Elsez Bilişim adı, erişimin bu firma üzerinden olabileceğine işaret eder; bunu hesap/fatura olmadan kesin satıcı olarak sunmamak gerekir. Kamuya açık sorgu patronun hesap giriş bilgilerini veya tam mülkiyet kaydını sağlamaz.

### Patronundan gerekli bilgiler

1. Alan adını yöneten METUnic/bayi/ajans hesabının hangi firmada olduğu ve yetkili kişi.
2. Varsa sana ayrı yönetici/davet hesabı; yoksa gerekli değişiklikleri patron veya mevcut teknik sorumlu uygular.
3. Mevcut DNS zone dışa aktarımı: A, AAAA, CNAME, MX, TXT, SPF, DKIM, DMARC, CAA ve diğer özel kayıtlar. Otomatik tarama tüm kayıtları bulamaz.
4. Google e-postasının mevcut yönetim sorumlusu ve değiştirilmeden korunacak kayıtlar.
5. Alan adı yenileme ve şirket adına Cloudflare hesap sahipliği kararı.

Alan adını kullanmak için sana satması, mülkiyeti değiştirmesi veya kayıt firmasını Hostinger/Cloudflare'a transfer etmesi şart değildir. DNS/isim sunucusu yönetiminin yetkili kişi tarafından yapılması yeterlidir. EPP transfer kodu normal DNS yönlendirmesi için gerekli değildir.

Şirket varlıklarının yönetimi için şirketin kontrolündeki hesap ve sana ayrı erişim tercih edilmeli. Patronun bütün hesabının şifresini sohbetten göndermesi gerekmez.

### Kesintiyi önleyen sıra

1. Mevcut DNS'nin tam kopyasını al ve e-posta kayıtlarıyla karşılaştır.
2. Şirketin onayladığı Cloudflare hesabına `norden-haus.com` ekle; kayıtları ve Cloudflare'ın atadığı isim sunucularını hazırla.
3. Patron/yetkili kişi registrar panelinde isim sunucularını uygulasın. İlk aşamada web ve e-posta hedefleri mevcut sistemde kalsın.
4. DNS/posta doğrulaması ve Cloudflare activation sonrası `media.norden-haus.com` R2 bağlantısını kur.
5. Dar kapsamlı sunucu tarafı R2 anahtarı, medya yükleme/hash/HTTP testi ve cache kurallarını tamamla.
6. Uygulama kusurları, VPS güvenliği, MFA, yedek ve staging kabulü tamamlandıktan sonra ana web kaydını yeni sunucuya yönlendir.

Bu turda registrar, canlı DNS, e-posta kayıtları veya eski web sitesi değiştirilmedi. Alan adı geçişi yetkili kişinin erişimi/koordinasyonu olmadan tamamlanamaz.

## 7. İş sırası

1. **Tamamlandı:** S-01, S-02 ve S-13 düzeltildi; 26 maddelik admin kalite kapısına durum geçişi, paralel sayaç ve bcrypt byte-sınırı regresyonları eklendi.
2. Patron/ajans üzerinden alan adı yetkisini ve tam DNS kaydını edin.
3. Cloudflare DNS, R2 custom domain, en az yetkili API erişimi ve harcama uyarısını tamamla.
4. Production VPS/TLS/proxy, admin MFA, kaynak limitleri, şifreli yedek ve bağımsız restore.
5. PostgreSQL ve staging üzerinde uygulama ile altyapı birlikte sınandıktan sonra yayın kararı.

Tespit edilmiş saldırı veya ele geçirilme bulgusu raporlanmıyor; bu çalışma hazırlık denetimidir. S-01 ve S-02 test ortamında üretilmiş, ardından yerelde düzeltilmiş güvenlik kusurlarıdır. Sıfır bağımlılık açığı sonucu, iş mantığı kusurlarının başlangıçta bulunmuş olmasıyla çelişmez.

## Kaynaklar

- [Alan adının Verisign RDAP kaydı](https://rdap.verisign.com/com/v1/domain/norden-haus.com)
- [IANA kayıt firmaları](https://www.iana.org/assignments/registrar-ids)
- [METUnic DNS yönetimi](https://metunic.com.tr/tr-dns/)
- [Cloudflare alan adı ekleme ve DNS kayıtlarını doğrulama](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/)
- [Cloudflare DDoS koruması](https://developers.cloudflare.com/ddos-protection/)
- [R2 public bucket ve custom domain](https://developers.cloudflare.com/r2/buckets/public-buckets/)
- [R2 cache](https://developers.cloudflare.com/cache/interaction-cloudflare-products/r2/)
- [Bütçe uyarıları](https://developers.cloudflare.com/billing/manage/budget-alerts/)
- Yerel Next.js 16.3.4 rehberi: `node_modules/next/dist/docs/01-app/02-guides/data-security.md`.
- İzole tekrar üretim aracı: `tmp/security-audit-20260915.mjs` (Git dışında, yalnız özel test DB'sini kabul eder).
