CodeBlanks Logo Icon CodeBlanks

Meydan okuma - Güvenli mimari

PHP kursu · Uzman · Ders 53/54

Rate limiting, CSP ve session_regenerate_id'yle sertleştirilmiş bir API yazarsın.

Sertleştirilmiş API mimarisi

Bu derste API'yi dört yönden sertleştirirsin. Rate limiting bir istemcinin aşırı istek göndermesini engeller; RateLimiter her anahtar için sayaç tutar ve limit aşılınca reddeder. CSP Content-Security-Policy başlığıyla içerik kaynaklarını kısıtlar.

session_regenerate_id(true) oturum kimliğini yeniler; oturum sabitleme saldırılarına karşı giriş sonrası kullanılır. password_needs_rehash eski bcrypt hash'lerini saptar ve gerektiğinde yeniden hash'ler. filter_var(..., FILTER_VALIDATE_EMAIL) ise girdi doğrulamasının temel taşıdır.

Uygulama Güvenlik katmanını yaz

Başlangıç kodunda ETL, ReflectionSerializer, Cache, benchmark ve ReportService hazır. Şimdi beş parça ekle. RateLimiter sınıfı: özel array $counts = [], yapılandırma ile private int $limit = 3; allow(string $key): bool sayacı artırır ve $limit'i aşmadıkça true döner.

applyCsp(): void header("Content-Security-Policy: default-src 'self'") gönderir. SessionGuard::regenerate(): void session_regenerate_id(true) çağırır. needsRehash(string $hash): bool password_needs_rehash($hash, PASSWORD_DEFAULT) döndürür. validateEmail(string $email): bool filter_var(..., FILTER_VALIDATE_EMAIL) !== false döndürür. Demo RateLimiter(2) ile üç istek yapar, iki e-posta ve iki hash durumu yazdırır.

Örnek çözümü göster
bool her çağrıda sayacı artırır ve limiti aşıp aşmadığını döner. applyCsp(): void header() ile Content-Security-Policy gönderir. SessionGuard::regenerate() session_regenerate_id(true) çağırır. needsRehash(string $hash): bool password_needs_rehash ile eski hash'i saptar. validateEmail(string $email): bool filter_var(..., FILTER_VALIDATE_EMAIL) kullanır.
Örnek çözüm:
<?php
// ETL + Reflection + Performans + Güvenlik: RateLimiter + CSP + SessionGuard yaz

function readRows(array $rows): Generator {
    foreach ($rows as $row) {
        yield $row;
    }
}

function transform(iterable $rows, callable $fn): Generator {
    foreach ($rows as $row) {
        yield $fn($row);
    }
}

function pipeline(array $rows, callable $fn): Generator {
    yield from transform(readRows($rows), $fn);
}

function loadAll(iterable $rows): array {
    $out = [];
    foreach ($rows as $row) {
        $out[] = $row;
    }
    return $out;
}

#[Attribute]
class Field {
    public function __construct(public string $name = "") {}
}

class Product {
    public function __construct(
        #[Field("id")] public int $id,
        #[Field("name")] public string $name,
    ) {}
}

class ReflectionSerializer {
    public function serialize(object $obj): array {
        $rc = new ReflectionClass($obj);
        $out = [];
        foreach ($rc->getProperties() as $prop) {
            $attrs = $prop->getAttributes(Field::class);
            if (empty($attrs)) continue;
            $field = $attrs[0]->newInstance();
            $key = $field->name !== "" ? $field->name : $prop->getName();
            $out[$key] = $prop->getValue($obj);
        }
        return $out;
    }
}

class Cache {
    private array $store = [];
    public function get(string $key, callable $loader) {
        if (!isset($this->store[$key])) {
            $this->store[$key] = $loader();
        }
        return $this->store[$key];
    }
}

function benchmark(callable $fn): array {
    $start = hrtime(true);
    $result = $fn();
    $elapsed = (int)((hrtime(true) - $start) / 1000000);
    $mem = memory_get_usage(true);
    return ["result" => $result, "ms" => $elapsed, "bytes" => $mem];
}

class ReportService {
    private PDO $pdo;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->pdo->exec("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, category_id INTEGER)");
        $this->pdo->exec("CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT)");
        $this->pdo->prepare("INSERT INTO categories (name) VALUES (?)")->execute(["kirtasiye"]);
        $this->pdo->prepare("INSERT INTO products (name, category_id) VALUES (?, ?)")->execute(["kale", 1]);
        $this->pdo->prepare("INSERT INTO products (name, category_id) VALUES (?, ?)")->execute(["defter", 1]);
    }
    public function eagerReport(): array {
        $sql = "SELECT p.name AS product, c.name AS category FROM products p JOIN categories c ON p.category_id = c.id ORDER BY p.id";
        return $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    }
}

class RateLimiter {
    private array $counts = [];
    public function __construct(private int $limit = 3) {}
    public function allow(string $key): bool {
        $this->counts[$key] = ($this->counts[$key] ?? 0) + 1;
        return $this->counts[$key] <= $this->limit;
    }
}

function applyCsp(): void {
    header("Content-Security-Policy: default-src 'self'");
}

class SessionGuard {
    public function regenerate(): void {
        session_regenerate_id(true);
    }
}

function needsRehash(string $hash): bool {
    return password_needs_rehash($hash, PASSWORD_DEFAULT);
}

function validateEmail(string $email): bool {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

// Demo:
$rl = new RateLimiter(2);
echo "istek1: " . ($rl->allow("ip1") ? "tamam" : "red") . "\n";
echo "istek2: " . ($rl->allow("ip1") ? "tamam" : "red") . "\n";
echo "istek3: " . ($rl->allow("ip1") ? "tamam" : "red") . "\n";
echo "eposta_geçerli: " . (validateEmail("ayse@site.com") ? "evet" : "hayır") . "\n";
echo "eposta_geçersiz: " . (validateEmail("not-email") ? "evet" : "hayır") . "\n";
echo "rehash_md5: " . (needsRehash(md5("x")) ? "1" : "0") . "\n";
echo "rehash_fresh: " . (needsRehash(password_hash("x", PASSWORD_DEFAULT)) ? "1" : "0") . "\n";
applyCsp();
echo "csp: uygulandı\n";

Sertleştirme özeti

Bu derste dört savunma hattı kurdun. RateLimiter istemci başına sayaç tutar; limit aşıldığında false döner. applyCsp tek satırlık header() çağrısıyla default-src 'self' kısıtlamasını ekler. SessionGuard::regenerate oturum sabitlemeye karşı yeni kimlik üretir.

needsRehash bcrypt parametreleri güncellendiğinde eski hash'leri saptar; validateEmail ise filter_var ile girdi doğrulamanın temelini atar. Bir sonraki derste tüm bu katmanlar modern PHP özellikleriyle birleşecek: enum, readonly, match ve Fiber.

Kendini test et

  1. Rate limiting neyi engeller?

    • SQL enjeksiyon saldırılarını
    • XSS saldırılarını
    • CSRF saldırılarını
    • Bir istemcinin aşırı istek göndermesini
    Cevabı göster

    Bir istemcinin aşırı istek göndermesini — Doğru. Rate limiting bir istemcinin aşırı istek göndermesini engeller; brute-force ve flood koruması sağlar.

  2. filter_var çağrısını tamamla

    $ok = ____($email, FILTER_VALIDATE_EMAIL) !== false;
    Cevabı göster
    $ok = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
  3. Güvenlik kavramlarını eşleştir

    RateLimiter
    Aşırı istekleri engeller
    CSP
    İçerik kaynaklarını kısıtlar
    session_regenerate_id
    Oturum kimliğini yeniler
    filter_var
    Girdiyi doğrular

Uzman bölümündeki tüm dersler