- XSS ve CSRF'yi açıklamak ve onları
textContent, kaçış, CSP,SameSiteçerezleri ve CSRF token'larıyla önlemek - Girdiyi sunucuda izin listeleriyle doğrulamak ve sırları istemci kodundan ve Git'ten uzak tutmak
- ESLint ve Prettier'ı kurmak ve bir projeyi kademeli olarak TypeScript'e taşımak
Sitende bir yorum alanı var. Biri yorum olarak <img src=x onerror="stealCookies()"> yazıyor. Kodun yorumları innerHTML ile ekliyorsa bu betik her ziyaretçinin tarayıcısında, onun oturumuyla çalışır. En kritik web risklerinin listesi olan OWASP Top 10'da enjeksiyon (injection) saldırıları yer alır ve XSS de bunlardandır. Güvenlik sona eklenen bir özellik değil, günlük alışkanlıklar bütünüdür.
XSS: siteler arası betik çalıştırma
XSS, güvenilmeyen veri sayfaya HTML olarak eklendiğinde ve saldırganın betiği senin sitenin adına çalıştığında olur: çerezleri çalar, kullanıcı adına işlem yapar. Savunma: kullanıcı metnini textContent ile yaz; HTML gerçekten gerekiyorsa DOMPurify gibi bir kütüphaneyle temizle; React {value} değerini kendisi kaçışlar (dangerouslySetInnerHTML hariç); Content-Security-Policy başlığı ise betiklerin nereden yüklenebileceğini sınırlar.
const comment = '<img src="x" onerror="stealCookies()">';
// dangerous: the text is parsed as HTML and onerror runs
list.innerHTML += `<li>${comment}</li>`;
// safe: the text stays text
const item = document.createElement('li');
item.textContent = comment;
list.append(item);<img ...> metnini görür ve hiçbir şey çalışmaz.Bir HTML string'i kurmak zorundaysan (örneğin sunucuda bir şablon), özel karakterleri kaçışla: < işareti <, & ise & olur ve tarayıcı onları etiket değil metin olarak gösterir.
function escapeHtml(text) {
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
return String(text).replace(/[&<>"']/g, (char) => map[char]);
}
console.log(escapeHtml('<img src=x onerror="stealCookies()">'));
console.log(escapeHtml('Tom & Jerry say "hi"'));▸ Beklenen çıktı
<img src=x onerror="stealCookies()"> Tom & Jerry say "hi"
CSRF: siteler arası istek sahteciliği
Kullanıcı bankasının sitesine giriş yapmış ve oturum çerezi tarayıcıda. Sonra başka bir site açıyor ve o sayfa bankanın /transfer adresine gizli bir form gönderiyor. Tarayıcı çerezi isteğe otomatik olarak ekler, banka da işlemi kullanıcının kendisinin yaptığını sanır. Savunma: SameSite nitelikli çerezler (Chrome bu niteliği olmayan çerezleri Lax sayar), formda gizli, rastgele bir CSRF token'ı (saldırganın sitesi onu okuyamaz), Origin başlığını kontrol etmek ve veriyi asla GET isteğiyle değiştirmemek.
Set-Cookie: session=8f3a2c91; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600| Nitelik | Neye karşı korur |
|---|---|
HttpOnly | JavaScript çerezi okuyamaz; XSS oturumu çalamaz |
Secure | çerez yalnızca HTTPS üzerinden gönderilir |
SameSite=Lax | başka sitelerden gelen POST isteklerine çerez eklenmez; CSRF çok zorlaşır |
Max-Age | oturumun ömrünü sınırlar |
Girdiyi doğrula, sırları koru
İstemciye asla güvenme: tarayıcıdan gelen her şey (formlar, URL'ler, başlıklar, JSON) sahtelenebilir; saldırgan arayüzünü atlayıp istekleri doğrudan API'ye gönderir. Bu yüzden girdiyi sunucuda doğrula: tip, uzunluk, biçim ve izin verilen değerler (yasak listesi değil, izin listesi). Tarayıcıdaki doğrulama yalnızca kullanıcının rahatlığı içindir. SQL sorgularını string birleştirerek kurma; parametreli sorgular kullan. Büyük projelerde şema kütüphaneleri (örneğin zod) yardımcı olur.
function validateOrder(input) {
const errors = [];
const allowedSizes = ['S', 'M', 'L'];
if (typeof input.email !== 'string' || !/^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i.test(input.email)) {
errors.push('email: invalid format');
}
if (!Number.isInteger(input.quantity) || input.quantity < 1 || input.quantity > 10) {
errors.push('quantity: must be an integer 1-10');
}
if (!allowedSizes.includes(input.size)) {
errors.push(`size: must be one of ${allowedSizes.join(', ')}`);
}
return errors.length ? { ok: false, errors } : { ok: true };
}
console.log(validateOrder({ email: 'leyla@mail.az', quantity: 2, size: 'M' }));
console.log(validateOrder({ email: 'not-an-email', quantity: '100', size: 'XXL' }));▸ Beklenen çıktı
{ ok: true }
{ ok: false, errors: [ 'email: invalid format', 'quantity: must be an integer 1-10', 'size: must be one of S, M, L' ] }'100' bir string'dir; bu yüzden Number.isInteger onu reddeder, tip de kontrol edilmelidir. Tüm hataları bir kerede döndürmek kullanıcının formu hızla düzeltmesine yardım eder.Sırlar (API anahtarları, parolalar, token'lar) tarayıcı koduna girdikleri anda herkese açık olur; her kullanıcı onları DevTools'ta görebilir. Ücretli API'yi senin sunucun çağırmalı, tarayıcı ise yalnızca senin sunucunla konuşmalı. Sırları bir .env dosyasında tut ve dosyayı .gitignore'a ekle. Vite'ta VITE_ önekli değişkenler istemci koduna gömülür; yani herkese açıktır.
Kod kalitesi: ESLint, Prettier, TypeScript
ESLint, kodu çalıştırmadan hataları ve kötü kalıpları bulur: kullanılmayan değişkenler, ==, bildirilmemiş adlar. Prettier kodu otomatik biçimlendirir; böylece ekip üslup hakkında tartışmaz. İkisini de editörde kaydederken ve CI'da çalıştır. Bir projeye ESLint eklemek için npm init @eslint/config@latest, Prettier için npm install -D prettier yeterlidir.
// eslint.config.js
import js from '@eslint/js';
import globals from 'globals';
export default [
js.configs.recommended,
{
languageOptions: { globals: globals.browser },
rules: {
eqeqeq: 'error',
'no-unused-vars': 'warn',
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
},
];npx eslint . tüm projeyi kontrol eder.const user={name:"Aysel",age:15,city:'Baku'}
function greet(u){return "Hi, "+u.name}
if(user.age>=14){console.log(greet(user))}npx prettier --write --single-quote . çalıştırıldıktan sonra aşağıdaki gibi olur.const user = { name: 'Aysel', age: 15, city: 'Baku' };
function greet(u) {
return 'Hi, ' + u.name;
}
if (user.age >= 14) {
console.log(greet(user));
}TypeScript, JavaScript'e statik tipler ekler: hatalar kod çalışmadan önce editörde görünür. Büyük bir projeyi bir günde dönüştürmen gerekmez; geçiş kademeli yapılır:
- 1tsconfig.json ekle
allowJsvecheckJsseçenekleriyle; TypeScript mevcut JS dosyalarını da kontrol etmeye başlar. - 2JSDoc tipleri yaz
/** @param {number} price */gibi yorumlar dosyanın adını değiştirmeden tip ekler. - 3Dosyaları tek tek
.ts'ye çevirAz bağımlılığı olan en basit modüllerle başla.
- 4
strict'i açSon olarak
"strict": trueen çok hatayı yakalar; örneğinnullolabilecek değerleri.
// shop.ts
interface Product {
id: number;
title: string;
price: number;
}
function total(products: Product[]): number {
return products.reduce((sum, p) => sum + p.price, 0);
}
total([{ id: 1, title: 'Pen', price: '2' }]);
total([{ id: 2, title: 'Notebook' }]);shop.ts(12,31): error TS2322: Type 'string' is not assignable to type 'number'.
shop.ts(13,8): error TS2741: Property 'price' is missing in type '{ id: number; title: string; }' but required in type 'Product'.npx tsc --noEmit çalıştırıldığında: iki hata kod hiç çalıştırılmadan bulundu. JavaScript'te '2' sessizce string birleştirmesine, eksik price ise NaN'a yol açardı.escapeHtml(text) fonksiyonunu yaz: &, <, >, " ve ' karakterlerini sırasıyla &, <, >, " ve ' ile değiştirsin. Tek bir replace ve bir değer haritası yeterli.
function escapeHtml(text) {
// replace & < > " ' with HTML entities
}
const comments = ['<b>Hello</b>', 'Tom & Jerry', '<img src=x onerror="steal()">'];
for (const comment of comments) {
console.log(escapeHtml(comment));
}▸ Beklenen çıktı
<b>Hello</b> Tom & Jerry <img src=x onerror="steal()">
Açık yönlendirmeyi önle: isSafeRedirect(target) yalnızca iki durumda true döndürsün: ardından ikinci bir / ya da \ gelmeyen tek bir / ile başlayan göreli bir yol ya da ana makine adı tam olarak app.example.com olan bir https:// adresi.
function isSafeRedirect(target) {
// 1) relative path: /^\/(?![\/\\])/
// 2) otherwise parse with new URL() inside try...catch
return true;
}
const targets = [
'/lessons/42',
'//evil.example/login',
'/\\evil.example',
'https://app.example.com/profile',
'https://app.example.com.evil.example/',
'javascript:stealCookies()',
];
for (const target of targets) {
console.log(`${target} -> ${isSafeRedirect(target)}`);
}▸ Beklenen çıktı
/lessons/42 -> true //evil.example/login -> false /\evil.example -> false https://app.example.com/profile -> true https://app.example.com.evil.example/ -> false javascript:stealCookies() -> false
Önemli noktalar
- XSS: kullanıcı verisini asla HTML olarak ekleme;
textContent, kaçış ya da temizleme kullan ve birContent-Security-Policyekle. - CSRF: çerezler otomatik gönderilir;
SameSite, CSRF token'ları,Originkontrolü ve GET ile değişiklik yapmamak korur. - İstemciye güvenme: sunucuda izin listeleriyle doğrula ve parametreli SQL sorguları kullan.
- Sırlar yalnızca sunucuda ve
.gitignore'daki bir.envdosyasında yaşar; sızan bir anahtarı hemen iptal et. - ESLint hataları bulur, Prettier biçimlendirir, TypeScript tip hatalarını çalıştırmadan yakalar; ona
checkJsile kademeli geç.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.