İçeriğe geç
academia.sh

Ders 10 / 15

Girdi Doğrulama ve Arındırma

filter_var() ile bir istemci girdisini geçerli kümeye daraltmak, doğrulama ile arındırmanın ayrı adımlar olduğunu ve doğrulamanın değeri değiştirme gücünü istemciden almadığını ölçmek.

Yayın Güncelleme

Bu derste

  • Bir girdiyi filter_var() ve aralık seçenekleriyle doğrular, geçersiz biçimin false döndürdüğünü gösterir
  • Doğrulama ile arındırmayı ayırır ve arındırmanın bozuk girdiyi sessizce başka bir değere çevirdiğini ölçer
  • Doğrulamanın sonucu istemciden almadığını, yalnızca geçerli kümeye daralttığını taraf ölçümüyle açıklar
İçindekiler

HTTP İstemcisi dersinde dış bir servisin yanıtı gecikince sonucu bir zaman aşımı istisnası sınırladı, ama fiyat yine üçüncü tarafın elinde kaldı. Güvenlik konusu aynı soruyu uygulamaya doğrudan gelen veriye çevirir: sorgu parametresi, form alanı ya da çerez olarak gelen bir değer, istemcinin gönderdiği şeydir. Bu girdi geçerli mi ve geçerliyse sonucu kim değiştirebiliyor?

GÜ1. Bu konunun bütün derslerinde istemci girdisi bir dizge sabitidir; gerçek bir istek kurulmaz. Geçersiz girdi nötr biçim hatalarıyla gösterilir ('4x', '', 'ayse@'), özel karakter dizgeleriyle değil. Koşan kod savunma tarafındadır. Rust müfredatının ileri kursunda bir güvencenin hangi denetimde sona erdiği ölçülmüştü; burada denetimin kendisi değil, sonucun kimin elinde kaldığı sorulur.

Doğrulama: Geçerli Küme

Girdi doğrulama (input validation), gelen değerin beklenen biçimde ve beklenen aralıkta olup olmadığına karar verir ve değilse değeri reddeder. PHP’de bu işin yerleşik aracı filter_var()’dır: ikinci bağımsız değişkeni bir süzgeç sabiti, üçüncüsü seçenekler dizisidir. FILTER_VALIDATE_INT tamsayı biçimini, min_range ve max_range seçenekleri aralığı denetler; uyan girdide tamsayıyı, uymayanda false döndürür. FILTER_VALIDATE_EMAIL aynı sözleşmeyle e-posta biçimini sınar.

Ders boyunca iki küçük doğrulayıcı kullanılır. Alt çizgili sabit adlar PHP geleneğindedir; dosya yalnız diske yazılır ve ölçüm dosyaları onu çeker.

<?php
// girdi.php — adet ve e-posta doğrulayıcıları
declare(strict_types=1);

const ADET_SECENEKLERI = ['options' => ['min_range' => 1, 'max_range' => 10]];

function adetDogrula(string $girdi): int|false {
    return filter_var($girdi, FILTER_VALIDATE_INT, ADET_SECENEKLERI);
}

function epostaDogrula(string $girdi): string|false {
    return filter_var($girdi, FILTER_VALIDATE_EMAIL);
}

Sipariş adedi 1 ile 10 arasında olmalıdır. Aşağıdaki ölçüm dört adet girdisini ve iki e-posta girdisini doğrulayıcılardan geçirir, ardından 0’dan 12’ye kadar her tamsayıyı dener ve kaçının kabul edildiğini sayar.

<?php
// dogrula.php — geçerli ve geçersiz biçimler
declare(strict_types=1);
require 'girdi.php';

foreach (['4', '4x', '', '11'] as $girdi) {
    echo 'adet ', var_export($girdi, true), ' → ', var_export(adetDogrula($girdi), true), "\n";
}
foreach (['[email protected]', 'ayse@'] as $girdi) {
    echo 'e-posta ', var_export($girdi, true), ' → ', var_export(epostaDogrula($girdi), true), "\n";
}
$kabul = array_filter(range(0, 12), fn(int $n) => adetDogrula((string) $n) !== false);
echo count($kabul), ' değer kabul edildi: ', min($kabul), ' ile ', max($kabul), " arası\n";
adet '4' → 4
adet '4x' → false
adet '' → false
adet '11' → false
e-posta '[email protected]' → '[email protected]'
e-posta 'ayse@' → false
10 değer kabul edildi: 1 ile 10 arası

Geçerli biçim değeri tamsayıya çevirerek geçirir: '4' metni 4 olur. '4x' ve boş dizge biçim hatasıdır, '11' biçimce geçerli ama aralığın dışındadır; üçü de false döndürür. Dönüş tipi bu yüzden int|false’tır ve çağıran false’u açıkça denetlemek zorundadır; false bir sayı olarak kullanılırsa sıfır gibi davranır. Sayım, doğrulayıcının tanımladığı kümeyi gösterir: 13 değerden yalnız 1 ile 10 arasındaki on tanesi geçer.

Dönüş Değerini Denetlemek

int|false dönüşünün bir tuzağı vardır: geçerli sonuç 0 olabilir ve 0, PHP’de false gibi yanlış sayılır. Aşağıdaki ölçüm, alt sınırı sıfır olan bir doğrulayıcıyı iki biçimde denetler: gevşek karşılaştırmayla ($sonuc == false) ve katı karşılaştırmayla ($sonuc === false).

<?php
// sifir.php — sıfır geçerli bir değer olduğunda
declare(strict_types=1);

function stokDogrula(string $girdi): int|false {
    return filter_var($girdi, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 10]]);
}

foreach (['0', '5', '4x'] as $girdi) {
    $sonuc = stokDogrula($girdi);
    echo var_export($girdi, true), ' → ', var_export($sonuc, true),
        ' · gevşek red ', var_export($sonuc == false, true),
        ' · katı red ', var_export($sonuc === false, true), "\n";
}
'0' → 0 · gevşek red true · katı red false
'5' → 5 · gevşek red false · katı red false
'4x' → false · gevşek red true · katı red true

Gevşek karşılaştırma geçerli '0' girdisini de reddeder: 0 == false doğrudur. Katı karşılaştırma yalnız gerçek false’u, yani '4x' biçim hatasını yakalar. Doğrulayıcının sözleşmesi bu yüzden === false ile okunur; geçerli kümede sıfır varsa gevşek denetim sessizce bir geçerli değeri kaybettirir. Hata bu kez güvenlik tarafında değil fonksiyon tarafındadır, ama aynı kökten gelir: doğrulamanın sonucu yanlış okunursa, doğrulamanın kendisi bir şey kanıtlamaz.

Taraf: Doğrulama Sahibi Değiştirmez

Şimdi kursun sorusunu bu sonuca soralım. Çekirdek dosya, her derste aynı biçimde verilen taraf.php’dir; DIS_TARAFLAR kodun dışındaki üç tarafı karar sırasıyla tutar, sina() sonucu önce sapmasız sonra her sapmayla ayrı ayrı hesaplar, taraf() ilk değiştireni, hiçbiri değiştiremediyse sunucuyu seçer. Sıra saldırı yüzeyinin genişliğidir: istemci her isteği gönderen herkestir, üçüncü taraf yalnız uygulamanın bağlandığı taraflardır, ortam yalnız dağıtımı yapanın elindedir.

<?php
// taraf.php — bu sonucu kim değiştirebiliyor?
declare(strict_types=1);

// Kodun dışındaki taraflar, karar sırasıyla: her istek, her dış çağrı, her dağıtım.
const DIS_TARAFLAR = ['istemci', 'üçüncü taraf', 'ortam'];

/** Sonucu bir kez sapmasız, sonra her dış tarafın sapmasıyla ayrı ayrı hesaplar. */
function sina(callable $sonuc, array $sapmalar): array {
    $taban = $sonuc([]);
    $degisti = [];
    foreach (DIS_TARAFLAR as $taraf) {
        if (array_key_exists($taraf, $sapmalar)) {
            $degisti[$taraf] = $sonuc([$taraf => $sapmalar[$taraf]]) !== $taban;
        }
    }
    return $degisti;
}

/** Sonucu değiştirebilen ilk dış taraf; hiçbiri değiştiremiyorsa sunucu. */
function taraf(array $degisti): string {
    foreach (DIS_TARAFLAR as $t) {
        if ($degisti[$t] ?? false) return $t;
    }
    return 'sunucu';
}

function tarafRaporu(string $ad, callable $sonuc, array $sapmalar): string {
    $degisti = sina($sonuc, $sapmalar);
    $satirlar = [];
    foreach ($degisti as $t => $d) $satirlar[] = $t . ($d ? ' değiştirdi' : ' değiştiremedi');
    $sonTaraf = taraf($degisti);
    echo "$ad → $sonTaraf\n    ", implode(' · ', $satirlar), "\n";
    return $sonTaraf;
}

Ölçülen sonuç, doğrulanmış adettir. Sapmasız istekte istemci '3' gönderir; istemcinin sapması başka bir geçerli sayıdır, '7'.

<?php
// adet.php — doğrulanmış adedin tarafı
declare(strict_types=1);
require 'taraf.php';
require 'girdi.php';

tarafRaporu('adet (doğrulanmış)', fn(array $s) => adetDogrula($s['istemci'] ?? '3'), ['istemci' => '7']);
adet (doğrulanmış) → istemci
    istemci değiştirdi

Sonuç istemcidir. Doğrulama '7'’yi reddetmedi, çünkü '7' geçerli kümenin içindedir; sonuç 3’ten 7’ye geçti. Doğrulama istemcinin değeri değiştirmesini önlemez. Yaptığı, istemcinin değiştirebileceği değerleri sayımdaki on tamsayıya daraltmaktır: '4x', '' ve '11' sonucu false’a çevirir ve uygulamanın geri kalanına hiç ulaşmaz. Daralma bir kazançtır, ama sahip değişmemiştir; adedi hâlâ isteği gönderen belirler.

Sınırlayıcı Ölçüm

Doğrulamanın yanında sık karıştırılan ikinci bir adım vardır. Arındırma (sanitization) girdiyi reddetmez, değiştirir: FILTER_SANITIZE_NUMBER_INT rakam, artı ve eksi dışındaki her karakteri siler ve kalanı kabul eder. İki adım ayrıdır: doğrulama “bu değer geçerli mi” diye sorar ve hayır diyebilir; arındırma hayır diyemez. Aynı üç girdiyi her ikisinden de geçirelim, ayrıca arındırılmış değeri doğrulayıcıya verelim.

<?php
// arindir.php — arındırma ile doğrulama yan yana
declare(strict_types=1);
require 'girdi.php';

foreach (['4', '4x', '1x0'] as $girdi) {
    $arindirilmis = filter_var($girdi, FILTER_SANITIZE_NUMBER_INT);
    echo var_export($girdi, true), ' → arındırma ', var_export($arindirilmis, true),
        ' · doğrulama ', var_export(adetDogrula($girdi), true),
        ' · arındırıp doğrulama ', var_export(adetDogrula($arindirilmis), true), "\n";
}
'4' → arındırma '4' · doğrulama 4 · arındırıp doğrulama 4
'4x' → arındırma '4' · doğrulama false · arındırıp doğrulama 4
'1x0' → arındırma '10' · doğrulama false · arındırıp doğrulama 10

Sonuç beklenen yöndedir ve rahatsız edicidir. Doğrulama '4x' ve '1x0' girdilerini reddeder; arındırma ikisini de kabul eder, üstelik değiştirerek: '4x' sessizce 4, '1x0' sessizce 10 olur. Arındırılmış değer doğrulayıcıdan geçer, çünkü arındırma bozukluğu silmiştir. Girdinin bozuk olduğu bilgisi bu noktada kaybolur: uygulama dört adetlik bir sipariş alır, kullanıcı ise 4x yazmıştır. Sınırlayıcı sonuç şudur: arındırma doğrulamanın yerini tutmaz. Bozuk girdi önce doğrulamayla reddedilir; arındırma, değerin reddedilemeyeceği, yalnız güvenli biçime getirilmesi gereken yerlerde kullanılır.

Özet

  • Bu dersin ölçü sınıfı istemci: doğrulanmış adedi istemci '3'’ten '7'’ye değiştirdi; doğrulama bunu önlemedi.
  • filter_var() ile FILTER_VALIDATE_INT ve aralık seçenekleri girdiyi geçerli kümeye daraltır: 13 değerden yalnız on tanesi geçti; geçersiz biçim false döndürür.
  • Doğrulama reddeder, arındırma değiştirip kabul eder; ikisi ayrı adımdır ve yerine geçmez.
  • Arındırma, bozuk bir girdiyi sessizce başka bir değere çevirir; reddedilmesi gereken girdi sonuca değer olarak girer.
  • Savunma her zaman sahibi değiştirmez: doğrulama sonucu sunucuya taşımaz, yalnız istemcinin değiştirebileceklerini daraltır.

Sonraki Adım

Doğrulanmış bir değer, geçerli kümede kaldığı sürece hâlâ istemcinin seçtiği bir değerdir. Peki bu değer bir sorgu metninin içine girerse, metnin kendisini kim belirler? SQL Enjeksiyonu Önleme dersi, hazır deyimin sorgu metnini istemciden alıp sunucuya taşıdığını ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat