8-bo‘lim

Formalar va HTTP so'rov

GET va POST farqi, superglobal massivlar, forma ma'lumotini qabul qilish va PRG naqshi.

🕑 11 daqiqa o‘qish 📄 885 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. GET va POST
  2. Superglobal massivlar
  3. Qiymatlarni xavfsiz olish
  4. To'liq forma misoli
  5. So'rov haqida ma'lumot
  6. Xulosa

Shu paytgacha ma'lumotni kodning o'zida yozib kelgan edik. Endi u tashqaridan - foydalanuvchidan keladi.

Va shu daqiqadan boshlab har bir qiymatga ishonchsizlik bilan qarash kerak.

GET va POST #

GETPOST
Ma'lumot qayerdaURL da ko'rinadiSo'rov tanasida
Uzunlik chegarasi~2000 belgiAmalda cheklanmagan
Xatcho'p qilishMumkinMumkin emas
Brauzer keshlaydiHaYo'q
Qayta yuborishXavfsizTakroriy amal xavfi
Fayl yuborishMumkin emasMumkin

Qoida oddiy:

AmalUsul
Qidirish, filtrlash, saralashGET
Sahifaga havola berishGET
Yozish, o'chirish, to'lovPOST
Parol yuborishPOST
GET hech qachon holatni o'zgartirmasin

Bu shunchaki uslub masalasi emas. Quyidagi havolani tasavvur qiling:

HTML
<a href="/ochir.php?id=5">O'chirish</a>

Nima noto'g'ri:

KimNima qiladi
BrauzerHavolalarni oldindan yuklab qo'yishi mumkin
Qidiruv robotiBarcha havolani bosib chiqadi
AntivirusXatdagi havolalarni tekshiradi
FoydalanuvchiF5 bosib amalni takrorlaydi

Ya'ni hech kim tugmani bosmasa ham, ma'lumotlaringiz o'chib ketishi mumkin. Bu haqiqiy voqea: 2006-yilda Google Accelerator veb-ilovalardagi barcha "o'chirish" havolalarini bosib chiqib, ko'p saytda ma'lumot yo'qotishga sabab bo'lgan.

To'g'ri usul - forma va POST:

HTML
<form method="post" action="/ochir.php">
    <input type="hidden" name="id" value="5">
    <button type="submit">O'chirish</button>
</form>

Ustiga 17-bo'limda ko'radigan CSRF tokeni ham qo'shiladi.

Superglobal massivlar #

PHP
// Har bir misol qat'iy turlar rejimida ishlaydi.
PHP
// Buyruq satrida $_GET va $_POST bo'sh bo'ladi.
// Shuning uchun ularni QO'LDA to'ldirib, mantiqni sinaymiz.
$_GET = [
    "qidiruv" => "klaviatura",
    "sahifa"  => "2",
    "tartib"  => "narx_osish",
];

$_POST = [
    "ism"   => "  Husanboy  ",
    "email" => "[email protected]",
    "yosh"  => "25",
];

echo "GET parametrlari:\n";
foreach ($_GET as $kalit => $qiymat) {
    printf("  %-10s %s\n", $kalit, var_export($qiymat, true));
}

echo "\nPOST maydonlari:\n";
foreach ($_POST as $kalit => $qiymat) {
    printf("  %-10s %s\n", $kalit, var_export($qiymat, true));
}

echo "\n";
echo "DIQQAT: hamma qiymat SATR - '25' emas, 25.\n";
echo "yosh turi: " . get_debug_type($_POST["yosh"]) . "\n";
Natija
GET parametrlari:
  qidiruv    'klaviatura'
  sahifa     '2'
  tartib     'narx_osish'

POST maydonlari:
  ism        '  Husanboy  '
  email      '[email protected]'
  yosh       '25'

DIQQAT: hamma qiymat SATR - '25' emas, 25.
yosh turi: string
$_GET va $_POST da hamma narsa satr

Forma orqali kelgan 25 - bu int emas, "25". HTTP protokolida turlar yo'q: hamma narsa matn.

Bu nimani anglatadi:

PHP
$yosh = $_POST["yosh"];          // "25" - satr
$yosh + 5;                       // 30 - PHP aylantiradi
$yosh === 25;                    // FALSE
soliqHisobla($yosh);             // qat'iy rejimda TypeError

Istisno yo'q: input type="number" ham satr yuboradi. Brauzerdagi tur cheklovi faqat interfeys uchun - u foydalanuvchiga qulaylik beradi, serverga esa hech qanday kafolat bermaydi.

Shuning uchun har bir qiymat uchun ikki qadam kerak:

QadamVosita
1. Tekshirishfilter_var, isset
2. Aylantirish(int), (float)

Va tartib muhim: avval tekshiring, keyin aylantiring. Teskarisi qilinsa, (int) "abc" jimgina 0 beradi va xato yo'qoladi.

Keyingi bo'lim aynan shu haqda.

Qiymatlarni xavfsiz olish #

PHP
$_GET = ["sahifa" => "3", "tartib" => "narx", "limit" => "500"];

// Yordamchi funksiya - har bir loyihada shunday bittasi bo'ladi
function getSon(string $kalit, int $standart, int $eng_kam, int $eng_kop): int
{
    $xom = $_GET[$kalit] ?? null;
    if ($xom === null) {
        return $standart;
    }

    $son = filter_var($xom, FILTER_VALIDATE_INT);
    if ($son === false) {
        return $standart;
    }

    return max($eng_kam, min($eng_kop, $son));
}

function getRoyxatdan(string $kalit, array $ruxsat, string $standart): string
{
    $xom = $_GET[$kalit] ?? "";
    return in_array($xom, $ruxsat, true) ? $xom : $standart;
}

$sahifa = getSon("sahifa", 1, 1, 1000);
$limit  = getSon("limit", 20, 1, 100);
$tartib = getRoyxatdan("tartib", ["narx", "sana", "nom"], "sana");

printf("sahifa: %d\n", $sahifa);
printf("limit:  %d  (500 so'ralgan edi, 100 ga cheklandi)\n", $limit);
printf("tartib: %s\n", $tartib);

echo "\n";
echo "Endi yaroqsiz qiymatlar bilan sinaymiz:\n";
$_GET = ["sahifa" => "abc", "tartib" => "; DROP TABLE users", "limit" => "-5"];

printf("sahifa: %d  (abc rad etildi)\n", getSon("sahifa", 1, 1, 1000));
printf("limit:  %d  (-5 chegaraga keltirildi)\n", getSon("limit", 20, 1, 100));
printf("tartib: %s  (ro'yxatda yo'q - standart)\n",
       getRoyxatdan("tartib", ["narx", "sana", "nom"], "sana"));
Natija
sahifa: 3
limit:  100  (500 so'ralgan edi, 100 ga cheklandi)
tartib: narx

Endi yaroqsiz qiymatlar bilan sinaymiz:
sahifa: 1  (abc rad etildi)
limit:  1  (-5 chegaraga keltirildi)
tartib: sana  (ro'yxatda yo'q - standart)
Oq ro'yxat - eng ishonchli himoya

getRoyxatdan funksiyasiga e'tibor bering. U kelgan qiymatni tozalashga urinmaydi - shunchaki ruxsat etilganlar ro'yxatida bor-yo'qligini tekshiradi.

Bu oq ro'yxat (allowlist) yondashuvi va u qora ro'yxat (blocklist) dan ancha xavfsiz:

YondashuvMantiqMuammo
Qora ro'yxat"Yomon narsalarni o'chir"Hammasini oldindan bilib bo'lmaydi
Oq ro'yxat"Faqat shularga ruxsat"Muammo yo'q

Yuqoridagi sinovda "; DROP TABLE users" kelgan edi. Qora ro'yxat bilan biz DROP, ;, -- va boshqa o'nlab narsani filtrlashimiz kerak bo'lardi - va baribir birortasini o'tkazib yuborardik.

Oq ro'yxat bilan esa javob oddiy: bu qiymat uch ruxsatdagi biri emas, demak standart.

Bu tamoyil saralash ustunlari, fayl kengaytmalari, til kodlari - har qanday cheklangan to'plam uchun amal qiladi.

Forma ma'lumoti qanday yo'l bosib o'tadi BRAUZER forma to'ldiriladi JS tekshiruvi - o'chirsa bo'ladi $_POST hammasi SATR ISHONCHSIZ TEKSHIRISH filter_var, oq ro'yxat xato bo'lsa - qaytarish ISHLATISH bazaga yozish, hisoblash endi tur ham to'g'ri Brauzerdagi tekshiruvni HIMOYA deb hisoblamang Hujumchi formani umuman ochmaydi - to'g'ridan-to'g'ri so'rov yuboradi (curl, Postman). POST-Redirect-GET naqshi 1. POST ma'lumot saqlanadi 2. 303 Redirect header("Location: ...") 3. GET natija ko'rsatiladi Nima uchun bu kerak Redirect qilinmasa, foydalanuvchi F5 bosganda brauzer POST ni QAYTA yuboradi. Natija: ikkita bir xil buyurtma yoki ikki marta to'lov.
Har bir qiymat tekshiruvdan o'tmaguncha ishonchsiz

To'liq forma misoli #

Quyidagi fayl ham formani ko'rsatadi, ham uni qabul qiladi:

PHP
<?php
declare(strict_types=1);

session_start();

$xatolar = [];
$qiymatlar = ["ism" => "", "email" => "", "xabar" => ""];

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // 1. Qiymatlarni olamiz va tozalaymiz
    $qiymatlar["ism"]   = trim($_POST["ism"] ?? "");
    $qiymatlar["email"] = trim($_POST["email"] ?? "");
    $qiymatlar["xabar"] = trim($_POST["xabar"] ?? "");

    // 2. Tekshiramiz
    if (mb_strlen($qiymatlar["ism"]) < 2) {
        $xatolar["ism"] = "Ism kamida 2 belgidan iborat bo'lsin.";
    }
    if (!filter_var($qiymatlar["email"], FILTER_VALIDATE_EMAIL)) {
        $xatolar["email"] = "Elektron pochta manzili noto'g'ri.";
    }
    if (mb_strlen($qiymatlar["xabar"]) < 10) {
        $xatolar["xabar"] = "Xabar kamida 10 belgidan iborat bo'lsin.";
    }

    // 3. Xato bo'lmasa - saqlaymiz va YO'NALTIRAMIZ (PRG)
    if ($xatolar === []) {
        // ... bazaga yozish ...
        $_SESSION["natija"] = "Xabaringiz yuborildi.";
        header("Location: " . $_SERVER["PHP_SELF"], true, 303);
        exit;
    }
}

$natija = $_SESSION["natija"] ?? null;
unset($_SESSION["natija"]);

function e(string $matn): string
{
    return htmlspecialchars($matn, ENT_QUOTES, "UTF-8");
}
?>
<!DOCTYPE html>
<html lang="uz">
<head><meta charset="UTF-8"><title>Bog'lanish</title></head>
<body>

<?php if ($natija !== null): ?>
    <p class="muvaffaqiyat"><?= e($natija) ?></p>
<?php endif; ?>

<form method="post">
    <label>
        Ism
        <input type="text" name="ism" value="<?= e($qiymatlar['ism']) ?>">
    </label>
    <?php if (isset($xatolar["ism"])): ?>
        <span class="xato"><?= e($xatolar['ism']) ?></span>
    <?php endif; ?>

    <label>
        Email
        <input type="email" name="email" value="<?= e($qiymatlar['email']) ?>">
    </label>
    <?php if (isset($xatolar["email"])): ?>
        <span class="xato"><?= e($xatolar['email']) ?></span>
    <?php endif; ?>

    <label>
        Xabar
        <textarea name="xabar"><?= e($qiymatlar['xabar']) ?></textarea>
    </label>
    <?php if (isset($xatolar["xabar"])): ?>
        <span class="xato"><?= e($xatolar['xabar']) ?></span>
    <?php endif; ?>

    <button type="submit">Yuborish</button>
</form>

</body>
</html>
Kiritilgan qiymatlarni qaytarib ko'rsatish

Formada value="<?= e($qiymatlar['ism']) ?>" bor. Bu kichik detal, lekin foydalanuvchi tajribasi uchun juda muhim.

Usiz nima bo'ladi: foydalanuvchi uzun xabar yozadi, emailda bitta harfni xato qiladi, "Yuborish" ni bosadi - va hamma narsa yo'qoladi. Qaytadan yozishga majbur bo'ladi.

e() funksiyasi esa htmlspecialchars ning qisqa nomi. Uni har bir loyihada yozib qo'yish keng tarqalgan amaliyot - chunki u juda ko'p ishlatiladi.

Diqqat: value atributi ichida ENT_QUOTES majburiy. Usiz foydalanuvchi " onmouseover="alert(1) deb yozib, atributdan chiqib ketishi mumkin.

17-bo'limda bu hujumni batafsil ko'ramiz.

So'rov haqida ma'lumot #

PHP
// Buyruq satrida bu qiymatlar bo'lmaydi - shuning uchun taqlid qilamiz
$_SERVER["REQUEST_METHOD"] = "POST";
$_SERVER["REQUEST_URI"]    = "/mahsulotlar?sahifa=2&tartib=narx";
$_SERVER["HTTP_USER_AGENT"] = "Mozilla/5.0 (Windows NT 10.0)";
$_SERVER["REMOTE_ADDR"]    = "203.0.113.45";
$_SERVER["HTTPS"]          = "on";

$maydonlar = ["REQUEST_METHOD", "REQUEST_URI", "REMOTE_ADDR", "HTTPS"];
foreach ($maydonlar as $m) {
    printf("%-18s %s\n", $m, $_SERVER[$m] ?? "(yo'q)");
}

echo "\n";
$xavfsizmi = ($_SERVER["HTTPS"] ?? "off") === "on";
echo "HTTPS orqalimi: " . ($xavfsizmi ? "ha" : "yo'q") . "\n";

echo "\n";
// URL ni qismlarga ajratish
$qismlar = parse_url($_SERVER["REQUEST_URI"]);
echo "Yo'l:        " . ($qismlar["path"] ?? "") . "\n";
echo "So'rov qismi: " . ($qismlar["query"] ?? "") . "\n";

parse_str($qismlar["query"] ?? "", $parametrlar);
echo "Parametrlar:\n";
foreach ($parametrlar as $k => $v) {
    printf("  %-8s %s\n", $k, $v);
}
Natija
REQUEST_METHOD     POST
REQUEST_URI        /mahsulotlar?sahifa=2&tartib=narx
REMOTE_ADDR        203.0.113.45
HTTPS              on

HTTPS orqalimi: ha

Yo'l:        /mahsulotlar
So'rov qismi: sahifa=2&tartib=narx
Parametrlar:
  sahifa   2
  tartib   narx
$_SERVER ning ba'zi maydonlari ham ishonchsiz

$_SERVER massivi ikki xil ma'lumotdan iborat:

Ishonchli (server yozadi)Ishonchsiz (mijoz yuboradi)
REQUEST_METHODHTTP_USER_AGENT
SCRIPT_NAMEHTTP_REFERER
SERVER_NAMEHTTP_X_FORWARDED_FOR
REMOTE_ADDRHTTP_HOST (sozlamaga bog'liq)

HTTP_ bilan boshlanadigan hamma narsa - bu foydalanuvchi yuborgan sarlavha va uni xohlagancha o'zgartirish mumkin.

Eng xavflisi - kirish nazoratini HTTP_REFERER ga asoslash:

PHP
if (str_contains($_SERVER["HTTP_REFERER"], "mening-saytim")) {
    // himoya EMAS - istalgan odam buni soxtalashtiradi
}

REMOTE_ADDR ham nozik: agar sayt proksi yoki CDN orqasida bo'lsa, u proksi manzilini ko'rsatadi. Haqiqiy IP X-Forwarded-For da bo'ladi - lekin uni faqat ishonchli proksidan kelganda qabul qilish kerak.

Amaliy topshiriq
  1. GET va POST uchun ikkita forma yozing, farqni URL da kuzating.
  2. $_GET dagi qiymat turini get_debug_type bilan tekshiring.
  3. filter_var bilan butun sonni tekshiruvchi yordamchi yozing.
  4. Unga "abc" bering - standart qiymat qaytdimi?
  5. Saralash ustuni uchun oq ro'yxat yarating.
  6. Ro'yxatda yo'q qiymat yuboring - nima bo'ladi?
  7. Formani value bilan to'ldiring va xato holatini sinang.
  8. htmlspecialchars ni ENT_QUOTES siz sinab ko'ring.
  9. PRG naqshini qo'llang va F5 bosib ko'ring.
  10. parse_url va parse_str bilan URL ni qismlarga ajrating.

Xulosa #

  • GET o'qish uchun, POST o'zgartirish uchun - bu qat'iy qoida.
  • GET havolasi holatni o'zgartirsa, uni brauzer yoki robot o'zi bosib yuborishi mumkin.
  • $_GET va $_POST dagi hamma qiymat satr - input type="number" ham.
  • Tartib muhim: avval tekshiring, keyin aylantiring.
  • (int) "abc" jimgina 0 beradi - shuning uchun filter_var ishlating.
  • Oq ro'yxat qora ro'yxatdan har doim xavfsizroq.
  • Xato bo'lganda formani kiritilgan qiymatlar bilan qayta ko'rsating.
  • HTML ichida chiqarishda htmlspecialchars($x, ENT_QUOTES, "UTF-8") ishlating.
  • PRG naqshi: POST -> 303 redirect -> GET; aks holda F5 amalni takrorlaydi.
  • $_SERVER da HTTP_ bilan boshlanuvchi maydonlar foydalanuvchi yuborgan - ishonmang.
  • Brauzerdagi JavaScript tekshiruvi - qulaylik, himoya emas.

Keyingi bo'limda validatsiyani chuqurroq - qayta ishlatiladigan tekshiruv qatlami sifatida yozamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.