PHP / XSS хамгаалалт

XSS хамгаалалт

XSS (Cross-Site Scripting) бол вэбийн хамгийн түгээмэл халдлагуудын нэг юм. Халдагч хэрэглэгч хуудасны оролтад хортой JavaScript код оруулж, тухайн кодыг бусад хэрэглэгчдийн хөтөч дээр ажиллуулдаг. Cookie хулгайлах, дансны мэдээлэл авах, хуурамч маягт харуулах зэрэг аюул учруулдаг. Аз болоход PHP-д хамгаалалт нь энгийн.

XSS халдлага хэрхэн ажилладаг вэ?

Хамгаалалтгүй код иймэрхүү харагдана:

php
<?php
// ❌ АЮУЛТАЙ — хэзээ ч ингэж бичиж болохгүй!
$нэр = $_GET['нэр'];
echo "Сайн уу, $нэр!";

// Халдагч дараах URL явуулбал:
// example.mn/мэнд?нэр=<script>document.cookie</script>
// Хуудас дараах HTML-г гаргана:
// Сайн уу, <script>document.cookie</script>!
// Хөтөч тухайн script-ийг гүйцэтгэнэ — Cookie алдагдана!
?>

Халдлага зөвхөн URL-ээр биш — коммент хэсэг, хайлтын талбар, профайлын нэр гэх мэт хэрэглэгч оролт бүхий газраас болдог.

htmlspecialchars() — үндсэн хамгаалалт

HTML тусгай тэмдэгтүүдийг аюулгүй байдлаар хөрвүүлдэг:

php
<?php
// ✓ Аюулгүй хэлбэр
$нэр = $_GET['нэр'] ?? '';
echo "Сайн уу, " . htmlspecialchars($нэр, ENT_QUOTES, 'UTF-8') . "!";

// Юу хийдэг вэ?
// <script>  →  &lt;script&gt;
// "         →  &quot;
// '         →  &#039;
// &         →  &amp;

// Жишээ
$оролт   = '<script>alert("Халдлага!")</script>';
$аюулгүй = htmlspecialchars($оролт, ENT_QUOTES, 'UTF-8');
echo $аюулгүй;
// &lt;script&gt;alert(&quot;Халдлага!&quot;)&lt;/script&gt;
// Хөтөч text болгон л харуулна — ажиллуулдаггүй
?>

ENT_QUOTES нь нэг ба давхар хашилт хоёуланг хамгаалдаг тул заавал тавих хэрэгтэй. UTF-8 нь Монгол кирилл тэмдэгтүүдийг зөв боловсруулна.

Оролт бүрийг цэвэрлэх дүрэм

Хэрэглэгчээс ирсэн аливаа өгөгдлийг найдаж болохгүй. $_GET, $_POST, $_COOKIE, $_SERVER бүгд шүүлт хийх ёстой:

php
<?php
// Хамгаалагдсан output функц үүсгэх нь зөв заншил
function e(string $утга): string {
    return htmlspecialchars($утга, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

// Хэлбэрийн оролт боловсруулах
$дансны_нэр = e($_POST['дансны_нэр'] ?? '');
$имэйл      = e($_POST['имэйл']     ?? '');
$коммент    = e($_POST['коммент']   ?? '');

// HTML дотор ашиглах
?>
<h2>Тавтай морил, <?= e($дансны_нэр) ?></h2>
<p>Имэйл: <?= e($имэйл) ?></p>
<div class="коммент"><?= e($коммент) ?></div>
php
<?php
// Атрибут дотор ашиглахад мөн адил
$аватар_зам = e($_GET['img'] ?? '');
?>
<img src="<?= $аватар_зам ?>" alt="Профайл зураг">

<!-- URL-д ашиглахад urlencode() хэрэглэнэ -->
<?php $хайлт = urlencode($_GET['q'] ?? ''); ?>
<a href="/хайлт?q=<?= $хайлт ?>">Хайх</a>

URL ба JavaScript контекст

XSS зөвхөн HTML тэгт биш — контекст бүрд өөр хамгаалалт хэрэгтэй:

php
<?php
// JavaScript дотор хэзээ ч шууд PHP утга бичиж болохгүй
// ❌ Аюултай
$утга = $_GET['нэр'];
echo "<script>var нэр = '$утга';</script>";

// ✓ JSON болгон encode хийж дамжуулна
$утга = json_encode($_GET['нэр'] ?? '', JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
echo "<script>var нэр = $утга;</script>";

// CSS дотор хэрэглэгчийн оролт бичих нь ч аюултай
// ❌ Аюултай
// <div style="color: <?= $_GET['өнгө'] ?>">
// ✓ Зөвшөөрөгдсөн утгуудын жагсаалтаас шалгана
$зөвшөөрөгдсөн = ['улаан', 'ногоон', 'цэнхэр'];
$өнгө = in_array($_GET['өнгө'] ?? '', $зөвшөөрөгдсөн, true)
    ? $_GET['өнгө']
    : 'хар';
?>
<div style="color: <?= e($өнгө) ?>">Текст</div>

Content Security Policy (CSP) header

Хамгаалалтын нэмэлт давхарга — хөтөчд зөвшөөрөгдсөн script эх сурвалжийг зааж өгнө:

php
<?php
// Хариу хуудсын эхэнд CSP header тохируулна
header("Content-Security-Policy: " . implode('; ', [
    "default-src 'self'",                    // Зөвхөн өөрийн домэйнаас
    "script-src 'self'",                     // Inline script хориглоно
    "style-src 'self' 'unsafe-inline'",      // CSS-д inline зөвшөөрнө
    "img-src 'self' data: https:",           // Зураг — self + https
    "font-src 'self'",                       // Фонт — зөвхөн self
    "connect-src 'self'",                    // Fetch/XHR — зөвхөн self
    "frame-ancestors 'none'",               // iframe-д оруулахыг хориглоно
]));

// X-Content-Type-Options — MIME sniffing хориглоно
header("X-Content-Type-Options: nosniff");

// X-Frame-Options — хуучин хөтөчдөд clickjacking хориглоно
header("X-Frame-Options: DENY");
?>

Дараагийн хичээлд:

SQL Injection халдлагаас хамгаалах аргыг судална. PDO prepared statement яагаад аюулгүй байдгийг, хэрэглэгчийн оролтыг SQL query-д шууд бичвэл яагаад аймшигтай байдгийг жишээнүүдээр үзнэ.