- Süper global dizilerden form verisini okumak, sunucuda doğrulamak ve POST'tan sonra yönlendirmek
htmlspecialcharsile XSS'i önlemek, oturumları ve çerezleri güvenle kullanmak- Parolaları
password_hashile saklamak, CSRF belirteci uygulamak ve yüklenen dosyaları denetlemek
İletişim formu, giriş sayfası, alışveriş sepeti... Bir sitenin canlı kısmı kullanıcılardan gelen verilerle çalışır. Bu verilere ise asla körü körüne güvenilmemelidir: biri yanlışlıkla hatalı yazabilir, bir başkası da bilerek zararlı hâle getirebilir. Bu derste PHP'nin web özelliklerini ve her web geliştiricisinin bilmesi gereken temel korunma yöntemlerini öğreneceksin.
Süper global diziler
PHP, istekle ilgili bilgileri özel dizilere yerleştirir. Bunlar kodun her yerinde, hatta fonksiyonların içinde bile görünür; bu yüzden süper global diye adlandırılır. Kural şudur: yalnızca veri okuyan istekler (arama, filtreler) GET ile, veriyi değiştiren istekler (giriş, sipariş, yorum) ise POST ile gönderilir.
| Dizi | Ne içerir |
|---|---|
$_GET | adresteki parametreler: search.php?q=php → $_GET['q'] |
$_POST | method="post" ile gönderilen formun alanları |
$_SERVER | istekle ilgili bilgiler, ör. $_SERVER['REQUEST_METHOD'] |
$_COOKIE, $_SESSION | tarayıcının gönderdiği çerezler ve oturum verileri |
$_FILES | yüklenen dosyalar |
XSS ve htmlspecialchars
Bir ziyaretçi defterinde birinin adının yerine bir <script> etiketi yazdığını düşün. Onu sayfaya olduğu gibi yazdırırsan bu betik her ziyaretçinin tarayıcısında çalışır: çerezleri çalabilir ya da sayfayı değiştirebilir. Bu saldırıya XSS (cross-site scripting) denir. Korunma yolu basittir: kullanıcıdan gelen her metni HTML'e koymadan önce htmlspecialchars ile kaçışla (escape).
<p>Hello, <?= $_GET['name'] ?>!</p><p>Hello, <?= htmlspecialchars($_GET['name'] ?? '', ENT_QUOTES, 'UTF-8') ?>!</p>?name=<script>...</script> adresiyle soldaki sayfa betiği çalıştırır, sağdaki ise onu düz metin olarak gösterir.<?php
$input = '<script>alert("hacked")</script>';
echo htmlspecialchars($input), "\n";
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
echo e("Tom & Jerry's \"show\""), "\n";<script>alert("hacked")</script> Tom & Jerry's "show"
Formu işleme ve doğrulama
Tarayıcıdaki required gibi denetimler yalnızca kolaylık içindir; kolayca atlatılabilir. Asıl doğrulama sunucuda yapılır. Başarılı bir POST'tan sonra header('Location: ...') ile başka bir sayfaya yönlendir ve exit yaz. Bu, PRG (Post/Redirect/Get) desenidir: kullanıcı sayfayı yenilediğinde form ikinci kez gönderilmez.
<?php
session_start();
$errors = [];
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (mb_strlen($name) < 2) {
$errors[] = 'Name is too short';
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors[] = 'Email is not valid';
}
if ($errors === []) {
// save the message to the database here
$_SESSION['flash'] = "Thanks, $name!";
header('Location: /contact.php');
exit;
}
}
// below: the HTML form shows $errors and refills the fields with e($name)Doğrulama için filter_var fonksiyonu kullanışlıdır: geçerli bir değer için değerin kendisini (gerekirse doğru türe dönüştürülmüş olarak), geçersiz bir değer için ise false döndürür. Onu terminalde de deneyebilirsin:
<?php
var_dump(filter_var("aysel@example.com", FILTER_VALIDATE_EMAIL));
var_dump(filter_var("aysel@", FILTER_VALIDATE_EMAIL));
var_dump(filter_var("42", FILTER_VALIDATE_INT));
var_dump(filter_var("200", FILTER_VALIDATE_INT, [
"options" => ["min_range" => 1, "max_range" => 120],
]));string(17) "aysel@example.com" bool(false) int(42) bool(false)
Oturumlar ve çerezler
HTTP “durumsuzdur”: her istek bir öncekinden habersizdir. Kullanıcıyı tanımanın iki yolu vardır. Çerez (cookie), tarayıcıda saklanan ve her istekle sunucuya geri gönderilen küçük bir veridir. Oturum (session) ise veriyi sunucuda tutar, tarayıcıya yalnızca oturum kimliği (PHPSESSID çerezi) verilir. session_start() ve setcookie() HTTP üst bilgileri gönderdiği için sayfaya herhangi bir metin yazdırılmadan önce çağrılmalıdır.
<?php
session_start();
$_SESSION['views'] = ($_SESSION['views'] ?? 0) + 1;
setcookie('theme', 'dark', [
'expires' => time() + 60 * 60 * 24 * 30,
'path' => '/',
'httponly' => true,
'samesite' => 'Lax',
]);
$theme = $_COOKIE['theme'] ?? 'light';
echo "Views this session: {$_SESSION['views']}, theme: $theme";Views this session: 1, theme: light. Yeniledikten sonra: 2, theme: dark — çerez ancak bir sonraki istekle gelir.httponly, çerezi JavaScript'ten gizler; XSS olsa bile çalınması zorlaşır. HTTPS kullanan bir sitede'secure' => trueda ekle.- Kullanıcı giriş yaptığında
session_regenerate_id(true)çağır, sonra$_SESSION['user_id']değerini yaz; eski kimlik işe yaramaz hâle gelir. - Çıkışta
$_SESSION = [];vesession_destroy();yaz. - Çerezde gizli bilgi tutma: kullanıcı onu görebilir ve değiştirebilir. Gizli veriler oturumda kalır.
Dosya yükleme
Dosya gönderen bir formda method="post" ve enctype="multipart/form-data" bulunmalıdır. PHP dosyayı geçici bir klasöre koyar, bilgilerini ise $_FILES içine yazar. Dosyanın boyutunu ve gerçek türünü denetle, ona rastgele yeni bir ad ver ve ancak ondan sonra move_uploaded_file ile taşı.
<?php
$file = $_FILES['photo'] ?? null;
if ($file && $file['error'] === UPLOAD_ERR_OK) {
if ($file['size'] > 2 * 1024 * 1024) {
exit('File is larger than 2 MB');
}
$type = mime_content_type($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png'];
if (!isset($allowed[$type])) {
exit('Only JPG and PNG images are allowed');
}
$newName = bin2hex(random_bytes(8)) . '.' . $allowed[$type];
move_uploaded_file($file['tmp_name'], __DIR__ . '/uploads/' . $newName);
echo 'Uploaded!';
}<input type="file" name="photo"> alanı gönderirParolalar ve CSRF
Parolaları asla açık metin olarak ya da md5, sha1 ile saklama; bu özetler çok hızlı kırılır. password_hash, parolayı bilerek yavaş tasarlanmış bir algoritmayla (şu anda bcrypt) ve rastgele bir “tuzla” (salt) özetler, password_verify ise doğrular. Özet her çağrıda farklı çıkar; bu yüzden özetler === ile karşılaştırılamaz. Veritabanında özet için VARCHAR(255) bir sütun ayır.
<?php
$hash = password_hash("secret123", PASSWORD_DEFAULT);
var_dump(password_verify("secret123", $hash));
var_dump(password_verify("Secret123", $hash));
var_dump($hash === password_hash("secret123", PASSWORD_DEFAULT));bool(true) bool(false) bool(false)
Bir CSRF (cross-site request forgery) saldırısında başka bir site, kullanıcının tarayıcısı adına senin sitene bir form gönderir; örneğin parolasını değiştirmek için. Tarayıcı oturum çerezini otomatik olarak eklediğinden istek “meşru” görünür. Korunmak için oturumda rastgele bir belirteç (token) oluşturulur, her POST formuna gizli bir alan olarak konur ve gelen istekte denetlenir.
<?php
session_start();
// 1. create a token once per session
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
// 2. every POST form gets a hidden field named csrf with this value
// 3. check the token when a form is submitted
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$sent = $_POST['csrf'] ?? '';
if (!hash_equals($_SESSION['csrf'], $sent)) {
http_response_code(403);
exit('Invalid CSRF token');
}
// the form is safe to process
}<input type="hidden" name="csrf" value="<?= e($_SESSION['csrf']) ?>">Önemli noktalar
$_GETadres parametrelerini,$_POSTform alanlarını tutar; veriyi değiştiren işlemlerPOSTile gönderilir.- Kullanıcıdan gelen her metni HTML'e koymadan önce
htmlspecialcharsile kaçışla; bu, XSS'i önler. - Doğrulama sunucuda yapılır (
filter_var, türler); başarılı birPOST'tan sonraheader('Location: ...')veexitile yönlendir. - Oturum verisi sunucuda, çerez ise tarayıcıda saklanır;
session_start()her çıktıdan önce çağrılmalıdır. - Parolalar
password_hash/password_verifyile, formlar CSRF belirteciyle, yüklemeler ise boyut ve gerçek tür denetimiyle korunur.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.