Softalica
Blog

Next.js uygulamalarında güvenlik en iyi uygulamaları.

Kimlik doğrulama, veri doğrulama ve ortam değişkenleri yönetimi için kontrol listesi.

Next.js, React tabanlı modern uygulamalar geliştirmek için güçlü bir framework olsa da güvenli bir uygulama geliştirmek yalnızca framework'ün sunduğu varsayılan özelliklere güvenmekle mümkün değildir. Server Components, Client Components, Server Actions, API Route'ları, Proxy ve üçüncü taraf servisler farklı güvenlik riskleri oluşturabilir.

Üretim ortamına alınacak bir Next.js uygulamasında kimlik doğrulama, yetkilendirme, ortam değişkenleri, güvenlik başlıkları, XSS, CSRF, SSRF ve bağımlılık güvenliği gibi konular birlikte ele alınmalıdır.

1. Next.js ve Bağımlılıklarını Güncel Tutun

Güvenliğin en temel adımlarından biri Next.js, React ve diğer npm bağımlılıklarını güncel tutmaktır. Framework seviyesinde ortaya çıkan güvenlik açıkları uygulamanın tamamını etkileyebilir.

Next.js'in Mayıs 2026 güvenlik güncellemesinde middleware ve Proxy yetki atlatma, cache poisoning, XSS, SSRF ve çeşitli DoS açıkları dahil olmak üzere çok sayıda güvenlik açığı giderildi. Bu nedenle güvenlik güncellemeleri yalnızca performans veya yeni özellikler için değil, doğrudan güvenlik için de takip edilmelidir.

  • Next.js'i güncel sürümde tutun.
  • React ve React Server Components paketlerini güncel tutun.
  • npm audit gibi araçlarla bağımlılıkları düzenli olarak kontrol edin.
  • Güvenlik duyurularını ve resmi Next.js advisories sayfasını takip edin.
bashbash
npm outdated
npm audit
npm update

2. Ortam Değişkenlerini Güvenli Kullanın

API anahtarları, veritabanı bağlantı bilgileri, JWT secret değerleri ve diğer hassas bilgiler kaynak koduna doğrudan yazılmamalıdır.

Next.js'te özellikle NEXTPUBLIC ile başlayan değişkenlere dikkat edilmelidir. Bu prefix ile tanımlanan değerler istemci tarafındaki JavaScript bundle'ına dahil edilebilir ve kullanıcı tarafından görülebilir.

envenv
DATABASE_URL="postgresql://user:password@localhost/database"
AUTH_SECRET="very-long-random-secret"

NEXT_PUBLIC_ANALYTICS_ID="public-value"

Burada DATABASEURL ve AUTHSECRET sunucu tarafında tutulması gereken gizli değerlerdir. NEXTPUBLICANALYTICS_ID ise bilinçli olarak istemciye açılan bir değerdir.

  • Gizli anahtarları kaynak koduna yazmayın.
  • .env dosyalarını Git repository'sine göndermeyin.
  • .env.* dosyalarını .gitignore içerisine ekleyin.
  • Sadece gerçekten public olması gereken değişkenlerde NEXTPUBLIC kullanın.
  • Production ortamındaki secret değerleri deployment platformunun environment variable sistemi üzerinden yönetin.

3. Authentication ve Authorization'ı Birbirinden Ayırın

Authentication kullanıcının kim olduğunu doğrularken authorization kullanıcının hangi işlemleri yapabileceğini belirler.

Örneğin bir kullanıcının sisteme giriş yapmış olması onun admin paneline erişebileceği anlamına gelmez.

typescripttypescript
async function deleteUser(userId: string) {
const session = await getSession()

if (!session?.user) {
throw new Error("Unauthorized")
}

if (session.user.role !== "admin") {
throw new Error("Forbidden")
}

await db.user.delete({
where: {
id: userId
}
})
}

Özellikle Server Actions ve API endpoint'lerinde yetkilendirme kontrolü doğrudan işlem yapılmadan önce gerçekleştirilmelidir.

  • Sadece frontend'de yetki kontrolü yapmayın.
  • Server Action içerisinde tekrar authorization kontrolü yapın.
  • API endpoint'lerinde kullanıcı rolünü ve izinlerini doğrulayın.
  • Admin işlemlerini yalnızca UI'da gizlemekle yetinmeyin.

4. Server ve Client Kodunu Doğru Ayırın

Next.js'in en önemli özelliklerinden biri Server Components mimarisidir. Ancak sunucu tarafında kalması gereken kod yanlışlıkla Client Component içerisine taşınırsa hassas bilgiler açığa çıkabilir.

Veritabanı işlemleri, secret anahtarlar ve özel backend servisleri mümkün olduğunca server tarafında tutulmalıdır.

typescripttypescript
import "server-only"

export async function getUsers() {
return await db.user.findMany()
}

server-only kullanmak, sunucuya özel bir modülün yanlışlıkla Client Component içerisinden import edilmesini engellemeye yardımcı olur.

5. Kullanıcı Verilerini Doğrudan Güvenmeyin

Tarayıcıdan gelen her veri güvenilmeyen veri olarak değerlendirilmelidir.

Bir formda kullanıcıya admin rolü seçtirmemek tek başına güvenlik sağlamaz. Saldırgan HTTP isteğini doğrudan oluşturabilir ve istediği alanları gönderebilir.

Bu nedenle validation sunucu tarafında da yapılmalıdır.

typescripttypescript
const schema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
})

const result = schema.safeParse(formData)

if (!result.success) {
throw new Error("Invalid input")
}
  • Form verilerini server tarafında doğrulayın.
  • ID, rol ve izin gibi değerleri kullanıcıdan gelen haliyle güvenmeyin.
  • Beklenen veri tiplerini ve uzunluklarını kontrol edin.
  • Veritabanına göndermeden önce input validation uygulayın.

6. XSS Saldırılarına Karşı Dikkatli Olun

Cross-Site Scripting veya XSS, saldırganın uygulamaya zararlı JavaScript kodu enjekte etmesiyle ortaya çıkar.

React varsayılan olarak birçok değeri escape ettiği için XSS riskini azaltır. Ancak dangerouslySetInnerHTML gibi özellikler kullanıldığında geliştiricinin ekstra dikkatli olması gerekir.

tsxtsx
<div dangerouslySetInnerHTML={{ __html: userContent }} />

Kullanıcıdan gelen HTML doğrudan bu şekilde render edilmemelidir.

HTML içeriğinin gerçekten render edilmesi gerekiyorsa içerik güvenilir bir sanitizer üzerinden geçirilmelidir.

7. Content Security Policy Kullanmayı Düşünün

Content Security Policy yani CSP, tarayıcıya hangi kaynaklardan script, image, font veya diğer içeriklerin yüklenmesine izin verildiğini bildirir.

Doğru yapılandırılmış bir CSP, XSS ve çeşitli code injection saldırılarına karşı önemli bir savunma katmanı oluşturabilir.

typescripttypescript
const securityHeaders = [
{
key: "Content-Security-Policy",
value: [
"default-src 'self'",
"script-src 'self'",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"form-action 'self'"
].join("; ")
}
]

CSP hazırlanırken uygulamanın kullandığı üçüncü taraf servisler dikkatlice belirlenmeli ve yalnızca gerekli domainlere izin verilmelidir.

8. HTTP Security Header'larını Yapılandırın

Güvenlik başlıkları tarayıcı tarafında ek güvenlik kontrollerinin uygulanmasına yardımcı olur.

Next.js uygulamalarında en azından aşağıdaki başlıklar değerlendirilmelidir.

  • Content-Security-Policy
  • X-Content-Type-Options
  • X-Frame-Options
  • Referrer-Policy
  • Strict-Transport-Security
typescripttypescript
const nextConfig = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "X-Content-Type-Options",
value: "nosniff"
},
{
key: "X-Frame-Options",
value: "DENY"
},
{
key: "Referrer-Policy",
value: "strict-origin-when-cross-origin"
}
]
}
]
}
}

export default nextConfig

Güvenlik başlıkları rastgele eklenmemeli, uygulamanın ihtiyaçlarına göre yapılandırılmalıdır.

9. Server Actions İçerisinde Yetkilendirme Yapın

Server Actions sunucu tarafında çalışsa da otomatik olarak güvenli kabul edilmemelidir.

Bir Server Action doğrudan HTTP üzerinden tetiklenebildiği için kullanıcı yetkisi işlem gerçekleştirilmeden önce kontrol edilmelidir.

typescripttypescript
"use server"

export async function updateProfile(data: FormData) {
const session = await getSession()

if (!session?.user) {
throw new Error("Unauthorized")
}

const name = String(data.get("name") ?? "")

if (name.length < 2) {
throw new Error("Invalid name")
}

await db.user.update({
where: {
id: session.user.id
},
data: {
name
}
})
}

Bir sayfanın veya layout'un korumalı olması, Server Action'ın da otomatik olarak güvenli olduğu anlamına gelmez.

10. API Route'larında Rate Limiting Kullanın

Login, kayıt, şifre sıfırlama, OTP doğrulama ve arama gibi endpoint'ler brute-force ve abuse saldırılarına açık olabilir.

Bu nedenle hassas endpoint'lerde rate limiting uygulanmalıdır.

  • Login denemelerini sınırlandırın.
  • Şifre sıfırlama isteklerini sınırlandırın.
  • OTP doğrulama denemelerini sınırlandırın.
  • Ağır veritabanı sorgularını yapan endpoint'leri sınırlandırın.
  • Public API endpoint'lerinde abuse koruması uygulayın.

Rate limiting uygulaması Redis, CDN, WAF veya kullanılan deployment altyapısının sunduğu çözümler üzerinden gerçekleştirilebilir.

11. CSRF Korumasını İhmal Etmeyin

Özellikle cookie tabanlı authentication kullanılan uygulamalarda CSRF saldırıları dikkate alınmalıdır.

Cookie'lerin HttpOnly, Secure ve uygun SameSite ayarlarıyla yapılandırılması saldırı yüzeyini azaltır.

typescripttypescript
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax" as const
}

Bunun yanında state-changing işlemlerde framework ve kullanılan authentication kütüphanesinin CSRF koruma mekanizmaları doğru şekilde yapılandırılmalıdır.

12. SSRF Risklerine Karşı URL'leri Kontrol Edin

Sunucu tarafından kullanıcı tarafından verilen bir URL'ye istek gönderiliyorsa SSRF riski oluşabilir.

Örneğin aşağıdaki yapı tehlikeli olabilir:

typescripttypescript
const url = request.nextUrl.searchParams.get("url")

const response = await fetch(url!)

Saldırgan sunucunun erişebildiği internal servisleri hedeflemeye çalışabilir.

Bu nedenle kullanıcıdan alınan URL'ler doğrudan fetch() içerisine verilmemelidir.

  • İzin verilen domainleri allowlist ile belirleyin.
  • Internal IP adreslerine erişimi engelleyin.
  • URL protokolünü kontrol edin.
  • Redirect davranışlarını kontrol edin.

13. CORS Ayarlarını Gereksiz Yere Genişletmeyin

API'nizin farklı domainlerden çağrılması gerekmiyorsa CORS'u herkese açık hale getirmeyin.

Özellikle Access-Control-Allow-Origin: * gibi geniş izinler authentication kullanılan API'lerde dikkatle değerlendirilmelidir.

Sadece gerçekten ihtiyaç duyulan origin'lere izin vermek daha güvenli bir yaklaşımdır.

14. Hata Mesajlarında Hassas Bilgi Vermeyin

Production ortamında kullanıcılara veritabanı hataları, stack trace veya internal servis bilgileri gösterilmemelidir.

Kötü bir hata mesajı:

texttext
PrismaClientKnownRequestError:
Database connection failed at
postgres://admin:password@10.0.0.15:5432/app

Daha güvenli bir yaklaşım:

typescripttypescript
return Response.json(
{ error: "Bir hata oluştu." },
{ status: 500 }
)

Detaylı hata bilgileri ise güvenli bir logging veya monitoring sistemine gönderilmelidir.

15. Production Source Map'lerini Dikkatli Kullanın

Production ortamında source map'ler istemeden uygulamanın kaynak kodu hakkında fazla bilgi sağlayabilir.

Production build yapılandırmanızda source map ayarlarını güvenlik açısından değerlendirin ve gerçekten ihtiyaç yoksa public source map yayınlamayın.

16. dangerouslyAllowSVG Kullanırken Dikkatli Olun

Next.js Image Optimization içerisinde SVG desteği açılırken güvenlik sonuçları dikkate alınmalıdır.

SVG dosyaları HTML benzeri aktif içerik barındırabildiği için güvenilmeyen SVG kaynaklarını doğrudan uygulamaya almak XSS riskini artırabilir.

Güvenilmeyen SVG dosyaları kullanılıyorsa CSP ve içerik sanitizasyonu gibi ek güvenlik önlemleri uygulanmalıdır.

17. Veritabanı Katmanını Server-Only Tutun

Veritabanı erişimi Client Component'lerden yapılmamalıdır.

Bunun yerine veritabanı sorgularını merkezi bir Data Access Layer içerisinde toplamak daha güvenli ve sürdürülebilir bir mimari oluşturur.

typescripttypescript
import "server-only"

import { db } from "@/lib/db"

export async function findUserById(id: string) {
return db.user.findUnique({
where: { id }
})
}

Bu yaklaşım sayesinde veritabanı bağlantısı ve backend'e özel kod istemci bundle'ına yanlışlıkla dahil edilmez.

18. Güvenlik Kontrol Listesi Oluşturun

Bir Next.js uygulamasını production'a almadan önce aşağıdaki maddeler kontrol edilebilir.

  • Next.js ve React güncel mi?
  • Güvenlik açıkları için dependency kontrolü yapıldı mı?
  • Secret değerler source code içerisinde bulunuyor mu?
  • .env dosyaları Git repository'sine ekleniyor mu?
  • NEXTPUBLIC değişkenleri kontrol edildi mi?
  • Authentication doğru şekilde uygulanıyor mu?
  • Authorization server tarafında kontrol ediliyor mu?
  • Server Actions içerisinde yetki kontrolü var mı?
  • API endpoint'lerinde input validation yapılıyor mu?
  • Rate limiting uygulanıyor mu?
  • CSP yapılandırıldı mı?
  • HTTP security header'ları mevcut mu?
  • CORS gereksiz şekilde açık mı?
  • Kullanıcıdan gelen URL'ler SSRF açısından kontrol ediliyor mu?
  • Hassas hata bilgileri kullanıcıya gösteriliyor mu?
  • Veritabanı erişimi server-only tutuluyor mu?
  • Production source map ayarları kontrol edildi mi?

Sonuç

Next.js uygulamalarında güvenlik, tek bir paket veya middleware eklemekten çok daha kapsamlı bir süreçtir. Framework'ün sunduğu Server Components, Server Actions ve server-side execution gibi özellikler doğru kullanıldığında güvenli bir mimari oluşturmayı kolaylaştırabilir; ancak yanlış yapılandırıldıklarında yeni güvenlik riskleri de ortaya çıkabilir.

En önemli yaklaşım, istemciden gelen hiçbir veriye varsayılan olarak güvenmemek ve güvenlik kontrollerini sunucu tarafında gerçekleştirmektir. Authentication, authorization, input validation, environment variable yönetimi, CSP, security headers, rate limiting ve güncel bağımlılıklar birlikte ele alınmalıdır.

Özellikle Next.js gibi hızlı gelişen framework'lerde güvenlik güncellemelerini takip etmek kritik öneme sahiptir. Güncel sürümleri kullanmak ve resmi güvenlik duyurularını düzenli olarak kontrol etmek, production uygulamalarının güvenlik seviyesini korumanın en temel adımlarından biridir.

Etiketler

Bu yazı için etiket eklenmemiş.

1