جلوگیری از SQL Injection و XSS؛ دفاع از Query تا DOM

در یک فروشگاه ایرانی، جست‌وجو با Query پارامتری امن شده بود؛ اما گزینه مرتب‌سازی هنوز نام ستون را از Request می‌گرفت. هم‌زمان متن Ticket پشتیبانی هنگام ذخیره پاک‌سازی می‌شد، ولی همان متن در پنل جدید داخل یک Sink ناامن DOM نمایش داده می‌شد. هر دو تیم می‌گفتند «ورودی را Sanitize کرده‌ایم»؛ هر دو یک مرز مهم را ندیده بودند: داده در کدام Interpreter و Context مصرف می‌شود.

SQL Injection و XSS با Blacklist چند کاراکتر، WAF یا نام یک Framework حل نمی‌شوند. دفاع قابل اتکا باید Source→Transform→Sink را ثبت کند، ساختار دستور را از داده جدا نگه دارد، خروجی را متناسب با Context پردازش کند، اختیار و اثر را محدود سازد و با تست و Retest شواهد بسازد. این راهنما دفاعی است و روش سوءاستفاده ارائه نمی‌کند.

SQLi و XSS را در یک مدل مشترک ببینید

بُعدSQL InjectionCross-Site Scripting
Interpreter اصلیSQL engine یا لایه QueryHTML/DOM/JavaScript/CSS/URL parser مرورگر
شکست مرزداده به بخشی از دستور Query تبدیل می‌شودداده به Markup یا Code فعال تبدیل می‌شود
کنترل پیشگیرانه اصلیParameterized query/safe APIAuto-escape + contextual output encoding یا Sanitization لازم
بخش پویاIdentifier/Sort با Mapping بستهSafe sink و پرهیز از Dangerous context
کاهش اثرLeast privilege، تفکیک Credential و NetworkCSP، Trusted Types، Cookie/Session controls
کشفReview/SAST/DAST/Test/DB telemetryTemplate/DOM review، SAST/DAST، CSP report
راه‌حل ناقصEscape دستی، WAF یا ORM labelInput filter عمومی، WAF، HttpOnly یا CSP تنها

Validation برای هر دو ارزش دارد، اما OWASP آن را دفاع اصلی SQLi یا XSS نمی‌داند. یک نام فارسی، نقل‌قول یا نشانی URL ممکن است کاملاً معتبر باشد؛ مسئله این است که در Query، HTML Attribute یا JavaScript چگونه مصرف می‌شود.

چهار نوع کنترل را با هم قاطی نکنید

  • Prevent: اجازه ندهید داده ساختار SQL/HTML/JS شود؛
  • Constrain: با Permission، DB role، CSP و Session دامنه اثر را کم کنید؛
  • Detect: با Test، Review، Telemetry و Alert ضعف یا رفتار غیرعادی را پیدا کنید؛
  • Respond: Component را متوقف، Evidence را حفظ، Patch/Rotate/Retest و Recovery را اجرا کنید.

WAF عمدتاً در Constrain/Detect و گاهی Virtual patch موقت است؛ Query ناامن را پارامتری و Sink ناامن را امن نمی‌کند. تنظیم این لایه در راهنمای WAF Tuning و کنترل False Positive جداگانه آمده است.

Data-flow Inventory؛ Source تا Sink را قابل ممیزی کنید

«ورودی کاربر» فقط Form نیست. Source می‌تواند Request، Header، Cookie، URL fragment، فایل CSV، Webhook، API شریک، CMS، داده ذخیره‌شده، Local storage، postMessage، تنظیم Admin یا خروجی یک مدل/سرویس ثالث باشد. اعتماد به هویت Source با ایمنی داده در Sink یکی نیست.

برای هر جریان این Contract را ثبت کنید:

data-id | business-owner | source | trust/classification | normalization
validation | storage | transforms | SQL/API consumers | render contexts
encoder/sanitizer | authorization | logs | retention | tests | code-owner

داده‌ای که امروز فقط در Email متنی است، فردا ممکن است در Dashboard HTML یا Query گزارش مصرف شود. Inventory باید Consumerها را با تغییر محصول به‌روز کند.

SQL Injection از کجا وارد می‌شود؟

ریسک زمانی شکل می‌گیرد که Value یا قطعه‌ای مشتق از بیرون با String building وارد دستور شود. مسیرهای رایج عبارت‌اند از Search، Login، Filter، Sort، Report، Export، Admin grid، Bulk operation، Analytics query، Background job، Import و Stored procedure دارای Dynamic SQL.

ORM نیز Safe boundary مطلق نیست. Query builder معمولاً Value را bind می‌کند، اما Raw query، literal expression، dynamic relation/field، unsafe interpolation یا Escape hatch می‌تواند همان مرز را بشکند. Code review باید Surfaceهای خام هر Stack را بشناسد.

دفاع اصلی SQLi؛ ساختار و داده را جدا کنید

در Prepared statement یا Parameterized query، Template دستور پیش از Value مشخص است و Driver مقدار را به‌عنوان داده منتقل می‌کند. منبع اصلی اجرا، OWASP SQL Injection Prevention Cheat Sheet است.

// الگوی دفاعی؛ API دقیق به Driver/Framework وابسته است
statement = db.prepare("SELECT id, title FROM products WHERE category_id = ? AND active = ?")
rows = statement.execute([categoryId, true])

Parameterization باید در همه Queryهای Read/Write/Delete، مسیرهای Admin، Job و Report باشد؛ امن‌کردن Login و رهاکردن Export کافی نیست. مستندات Driver را برای نوع، Encoding، placeholder، Server/client preparation و Error behavior بررسی کنید و با Test اثبات کنید Value وارد ساختار نمی‌شود.

بخش‌هایی که Bind variable نمی‌پذیرند

نام Table/Column، Keyword یا جهت Sort در بسیاری از APIها Value پارامتری نیست. ورودی خام را Quote یا Escape نکنید؛ یک Intent محدود را به Fragment داخلی ثابت نگاشت کنید:

sortMap = {
  "newest": "created_at DESC",
  "price_low": "price ASC",
  "price_high": "price DESC"
}
orderClause = sortMap[userChoice] ?? sortMap["newest"]

OWASP برای Identifier/Sort، Allowlist یا بازطراحی Query را توصیه می‌کند. یک تابع عمومی «نام جدول را Validate کن» می‌تواند در Context اشتباه خطرناک باشد؛ Mapping باید مخصوص همان Use case و محدود باشد.

فیلتر اختیاری، IN و LIKE را درست بسازید

Query پویا را از Fragmentهای کد ثابت و آرایه Parameter بسازید؛ شرطی که فعال می‌شود باید Placeholder متناظر بسازد. برای IN به تعداد Valueها Placeholder تولید کنید و تعداد/نوع ورودی را محدود سازید. آرایه را به یک String و سپس به SQL نچسبانید.

در Search با LIKE، Wildcardهایی که معنای جست‌وجو را عوض می‌کنند طبق API همان Driver مدیریت شوند و سپس Value bind شود. Escape wildcard با Escape SQL یک مسئله نیست؛ هر دو را با یک تابع مبهم حل نکنید.

Stored procedure و ORM ضمانت خودکار نیستند

Stored procedure وقتی از Parameter واقعی و SQL ثابت استفاده می‌کند می‌تواند سطح دسترسی را محدود کند؛ اگر داخل آن Dynamic SQL با Concatenation ساخته شود، ریسک به Database منتقل شده است. ORM نیز تا زمانی کمک می‌کند که مسیر Safe API حفظ شود.

فهرست Raw escape hatchها را در Repository ثبت، Owner و دلیل را اجباری و SAST/Review را روی آن‌ها سخت‌گیر کنید. «توسط ORM ساخته شده» Evidence نیست؛ Query path و binding Evidence است.

Second-order Injection؛ ذخیره‌شدن داده آن را Trusted نمی‌کند

ممکن است داده هنگام ورود فقط ذخیره شود و هیچ اثر فوری نداشته باشد، اما Job گزارش‌گیری بعداً آن را داخل Query پویا ترکیب کند. این Second-order path نشان می‌دهد Sanitize-on-input به‌تنهایی کافی نیست.

در هر Sink، کنترل همان Interpreter را اعمال کنید. Data provenance برای تحلیل و Policy مفید است، اما مقدار خوانده‌شده از Database، Cache یا Internal API را ذاتاً امن نکنید.

Validation و Normalization نقش دقیق دارند

Validation باید Business shape را محدود کند: Type، Range، Length، Enum، State transition، File shape و رابطه شناسه با Tenant. برای شماره تلفن ایران، فاصله/پیش‌شماره را با Policy یکنواخت Normalize کنید؛ برای ی/ک و ارقام نیز ترتیب Normalize→Validate را مشخص کنید.

اما Validated data را با String concatenation وارد Query نکنید و به‌صورت خام در HTML قرار ندهید. Validation سطح حمله و خطا را کم می‌کند؛ مرز داده/دستور را Parameterization یا Contextual rendering حفظ می‌کند.

Least Privilege اثر SQLi را محدود می‌کند

Runtime نباید DBA/Root باشد. Migration، Read-only report، Background job و Application write می‌توانند Credentialهای جدا با Scope حداقلی داشته باشند. View یا Stored procedure محدود نیز گاهی سطح دسترسی Table/Column را کاهش می‌دهد.

  • Database از اینترنت عمومی مستقیم در دسترس نباشد؛
  • Secret در Repository، Browser، URL یا Log نرود و Rotation آزموده شود؛
  • Tenant scope و Object authorization مستقل از Query safety اجرا شوند؛
  • Error دیتابیس و Stack trace عمومی نشوند؛ Correlation ID کافی است؛
  • Backup رمزنگاری و Restore دوره‌ای باشد.

Query پارامتری مانع IDOR/BOLA یا دسترسی Cross-tenant نیست. Least privilege نیز Query ناامن را مجاز نمی‌کند. برای مرزبندی احراز، مجوز و Endpoint از راهنمای امنیت API استفاده کنید.

XSS را با محل ذخیره طبقه‌بندی نکنید؛ Context اجرا مهم است

Stored، Reflected و DOM-based مسیرهای تحویل را توضیح می‌دهند. کنترل واقعی به Context مصرف بستگی دارد: HTML text، Attribute، URL، JavaScript، CSS، SVG/MathML یا DOM sink. یک مقدار ممکن است در HTML text امن Encode شود و در URL یا Inline script ناامن باشد.

Sourceهای DOM شامل location، Storage، Message، API response و DOM attributes هستند. حتی اگر Response اولیه Server سالم باشد، Client code می‌تواند داده را به Sink اجرایی ببرد.

Auto-escaping Framework را حفظ کنید

Template engineهای مدرن معمولاً خروجی متنی را Escape می‌کنند. ریسک در خاموش‌کردن این Default یا استفاده از Escape hatchهایی مانند Raw HTML، unsafe template، direct DOM manipulation و bypass trust ظاهر می‌شود. هر Framework نام و رفتار متفاوت دارد؛ فهرست Escape hatchهای Stack را وارد Secure coding standard کنید.

SSR، Hydration، Email template، PDF renderer و Admin preview را جدا ببینید. امن‌بودن Component مرورگر به معنای امن‌بودن Template ایمیل یا گزارش HTML نیست.

Encoding باید در آخرین Context انجام شود

Contextراه پیش‌فرضمرز مهم
HTML textAuto-escape/HTML entity encodingبه Markup خام تبدیل نشود
Quoted attributeAttribute encoding + نام Attribute ثابتEvent handler و Attribute اجرایی ممنوع
URL parameterURL component encode سپس attribute encodeScheme/Origin/Destination را Validate کنید
JavaScriptداده را از Inline script خارج و با API امن منتقل کنیدDynamic code/eval و Event handler Context خطرناک‌اند
CSSترجیحاً مقدار ساختاری محدود با API StyleSelector/Rule/URL پویا نکنید
Rich HTMLSanitizer Policy نگهداری‌شدهپس از پاک‌سازی دوباره mutate نشود

OWASP XSS Prevention Cheat Sheet Contextها، Dangerous locationها و Anti-patternهایی مانند Encoding یکسان در Interceptor را شرح می‌دهد. Encoding را هنگام ذخیره به داده نچسبانید؛ Context آینده هنوز معلوم نیست و Double encoding/Context mismatch رخ می‌دهد.

URL نیازمند Parse، Policy و Encoding است

URL encoding به‌تنهایی Scheme را امن نمی‌کند. URL را با Parser معتبر بسازید؛ Scheme، Origin/Host، مسیر یا مقصد Redirect را طبق Use case Allowlist کنید؛ Query value را Encode و هنگام قرارگرفتن در HTML، Attribute encoding را نیز اعمال کنید.

برای Redirect مقصد بیرونی، Image source، Webhook URL و Link کاربر Policyهای جدا لازم است. «با https شروع می‌شود» جای Parse و Host comparison را نمی‌گیرد.

HTML مجاز؛ Sanitization با Policy کوچک

اگر محصول فقط متن می‌خواهد، Encoding انتخاب ساده‌تر است. اگر Rich text واقعاً لازم است، Sanitizer نگهداری‌شده با Allowlist کوچک Element/Attribute/Protocol استفاده کنید. Policy برای Ticket پشتیبانی لزوماً با محتوای Editorial یا Description فروشنده یکسان نیست.

  • Sanitizer و Browser compatibility را Patch کنید؛
  • SVG، MathML، embedded content و URL را جداگانه تصمیم بگیرید؛
  • Preview و Published output از Policy واحد یا سازگار استفاده کنند؛
  • Sanitized HTML را بعداً با Library یا String operation ناامن تغییر ندهید؛
  • Removed content، Error و false positive را بدون ذخیره Payload حساس مانیتور کنید؛
  • Fixture regression برای Policy داشته باشید.

Library محبوب مصون از Bypass آینده نیست؛ Version، Owner و Patch SLA بخشی از کنترل‌اند.

Safe Sink در DOM را Default کنید

برای متن از textContent، برای Form از value و برای ساختار از createElement/append استفاده کنید. setAttribute فقط وقتی نام Attribute ثابت و ذاتاً غیراجرایی است مناسب است. innerHTML، outerHTML، insertAdjacentHTML، document.write و Dynamic code باید ممنوع یا پشت Policy مشخص باشند.

// متن؛ مرورگر آن را Markup تفسیر نمی‌کند
messageNode.textContent = apiResponse.message

// HTML لازم؛ فقط خروجی Policy نگهداری‌شده
trusted = richTextPolicy.createHTML(untrustedRichText)
container.innerHTML = trusted

این نمونه نام Library خاص را اجبار نمی‌کند. Policy باید واقعاً Sanitize کند؛ برچسب trusted روی String یا Policy باز که ورودی را بدون تغییر عبور می‌دهد دفاع نیست.

postMessage، JSON و داده Third-party

در postMessage، Origin مورد انتظار و Schema پیام را بررسی کنید و پاسخ را به Origin مشخص بفرستید؛ Wildcard عمومی Default نباشد. داده JSON با Content-Type درست منتقل و با DOM API امن Render شود؛ JSON بودن، محتوای String را برای HTML امن نمی‌کند.

Widget، Chat، Review، Tag manager و A/B tool نیز Trust boundary هستند. Owner، داده قابل دسترسی، Script origin، Update path، Consent و Kill switch را ثبت کنید.

CSP؛ لایه مکمل با Rollout قابل مشاهده

CSP می‌تواند منابع Script/Style/Frame/Connect را محدود و اجرای Inline یا Dynamic code را سخت کند. Policy مبتنی بر Nonce/Hash و strict-dynamic ممکن است از Host allowlist گسترده قوی‌تر باشد، اما طراحی آن به معماری و Browserهای هدف وابسته است.

  1. Script/Style/Frame/Worker/Connect و Owner هر Dependency را Inventory کنید؛
  2. Inline و eval-like dependency را کم کنید؛
  3. Policy پیشنهادی را در Report-Only منتشر و Reportها را Redact/Group کنید؛
  4. Journeyهای Login/Checkout/Admin را با Browser matrix تست کنید؛
  5. Policy اجرایی را Canary و Rollback path را آماده کنید؛
  6. Violation و تغییر Dependency را دائماً مانیتور کنید.

اجرای دقیق Nonce، Report و Rollout در راهنمای Content Security Policy پوشش داده شده است. CSP جای Auto-escape، Encoding، Sanitizer یا Safe sink نیست.

Trusted Types؛ Enforcement همراه با Policy governance

Trusted Types اجازه می‌دهد Injection sinkهای مشخص به‌جای String، فقط TrustedHTML، TrustedScript یا TrustedScriptURL بپذیرند. Directive require-trusted-types-for 'script' Enforcement را فعال و trusted-types نام Policyهای مجاز را محدود می‌کند.

طبق مستندات فعلی MDN، پشتیبانی در مرورگرهای جدید گسترش یافته اما Matrix مخاطبان خودتان باید سنجیده شود. Rollout پیشنهادی: Sink inventory→Report-only→Refactor→Policy کوچک و بازبینی‌شده→Enforce→monitor. Policy پیش‌فرضی که هر ورودی را بدون Sanitization تأیید کند، فقط خطا را پنهان می‌کند. منبع فنی: MDN Trusted Types API.

Cookie و Header چه کاری نمی‌کنند؟

HttpOnly خواندن مستقیم Cookie توسط JavaScript را محدود می‌کند، اما XSS می‌تواند Action را در Session کاربر اجرا یا داده قابل مشاهده در DOM را بخواند. Secure و SameSite نیز اهداف Transport/CSRF-related دارند و ریشه XSS را رفع نمی‌کنند.

X-Content-Type-Options، Frame protection، Referrer policy و Permissions policy Baseline مفیدند؛ هیچ‌کدام جای کنترل Interpreter را نمی‌گیرند. HTTPS نیز حمل‌ونقل را محافظت می‌کند، نه Query یا DOM ناامن را.

WordPress؛ Validate/Sanitize زود، Escape دیر

در WordPress از APIهای Core استفاده و SQL/HTML خام را کم کنید. اصل «Escape late» یعنی خروجی را درست در Context نهایی با تابع مناسب پردازش کنید:

  • esc_html() برای متن HTML، esc_attr() برای Attribute و esc_url() برای URL؛
  • wp_kses()/wp_kses_post() برای HTML مجاز با Policy مناسب؛
  • $wpdb->prepare() و Placeholder درست برای Valueهای Query سفارشی؛
  • APIهای Post/Meta/Term/User به‌جای SQL مستقیم هرجا ممکن است؛
  • Capability check مستقل از Nonce؛ Nonce کنترل CSRF است، نه Authorization یا XSS؛
  • REST permission_callback و Object-level permission برای هر عملیات؛
  • محدودکردن unfiltered_html، File editor و Adminهای غیرضروری.

WordPress Security APIs و مستند wpdb::prepare() منبع پیاده‌سازی‌اند. Patch Core/Theme/Plugin و Rollback امن نیز در راهنمای به‌روزرسانی امن وردپرس آمده است.

Secure requirement را نسخه‌دار کنید

«نباید SQLi/XSS داشته باشیم» Testable requirement نیست. از ASVS برای تبدیل هدف به Requirementهای قابل Verification استفاده کنید و Version را در ID نگه دارید. آخرین نسخه پایدار در Snapshot ۱۲ اوت ۲۰۲۶، OWASP ASVS 5.0.0 است؛ Bleeding-edge را به‌جای Baseline قرارداد Production به کار نبرید.

برای هر Requirement، Scope/Level، Component owner، روش Verification، Evidence، Tool version، Result، Exception، Fix owner و Retest date ثبت شود. Scanner pass با Verification کامل برابر نیست.

Test pyramid برای SQLi و XSS

لایهSQLiXSSخروجی
UnitBinding، dynamic mapping، type/rangeEncoder/Sanitizer/URL policy/Safe componentTest deterministic سریع
Component/IntegrationORM/raw paths، DB role، errorTemplate/DOM/SSR/Email/PreviewSource→Sink proof
StaticConcatenation/raw query rulesUnsafe sink/escape hatch rulesPR finding با Owner
DynamicEndpoint/role/state coverage در محیط مجازRender/DOM behavior و headerReproducible evidence
ManualBusiness query/second-order pathContext chaining/framework gadgetRisk-aware review
Production-safeError/anomaly/DB telemetryCSP report/component anomalyDetection و trend

تست نفوذ باید Scope، مجوز، Rate، داده، Safety stop و Retest داشته باشد. DAST تهاجمی روی Production یا سامانه ثالث بدون مجوز، کنترل امنیتی نیست. برنامه کامل در راهنمای تست نفوذ وب‌اپلیکیشن شرح داده شده است.

Fixtureهای امن برای CI

برای Unit/Regression لازم نیست Payload اجرایی خطرناک در Log و Report پخش شود. از Sentinelهای بی‌اثر شامل کاراکترهای مرزی، متن Unicode/RTL، Quote، Ampersand، زاویه، URLهای Test-domain و HTML بی‌خطر استفاده و Assertion کنید که خروجی به‌عنوان Text/Value باقی می‌ماند، Query structure ثابت است و Sanitizer فقط Policy مورد انتظار را نگه می‌دارد.

Corpus امنیتی حساس را در Repository با دسترسی و Logging مناسب نگه دارید. Screenshot یا CI artifact نباید Token، Cookie، Query واقعی مشتری یا داده شخصی را منتشر کند.

Code review پرسش‌محور

SQL/Database

  • Source هر Value چیست و Binding کجا انجام می‌شود؟
  • آیا Identifier/Sort/Operator از Mapping ثابت می‌آید؟
  • IN/LIKE/optional filters با API صحیح ساخته شده‌اند؟
  • Raw query/Stored procedure/Report/Job و second-order consumer بررسی شده‌اند؟
  • DB role فقط Table/Operation لازم را دارد؟
  • Tenant authorization مستقل و خطا/Log بدون نشت است؟

Template/Browser

  • Render context دقیق چیست و Auto-escape فعال است؟
  • URL scheme/origin و Attribute name محدودند؟
  • HTML خام واقعاً لازم و Sanitizer version/Policy/Owner مشخص است؟
  • DOM source و Sink، postMessage origin و JSON render امن‌اند؟
  • CSP/Trusted Types نقش مکمل و Rollout آزموده دارند؟
  • SSR، Hydration، Preview، Email، PDF و Admin همان مسیر را پوشش می‌دهند؟

Logging بدون ساختن مخزن داده خطرناک

Raw request، Cookie، Authorization header، SQL string، Stack trace، Rich HTML و CSP sample می‌توانند Secret یا داده شخصی داشته باشند. Structured log با Event type، Route template، Correlation ID، Rule/Policy ID، Result، Tenant pseudonym و زمان کافی است؛ Payload فقط در مسیر محدود و با Retention/Access مصوب نگهداری شود.

افزایش Validation failure یا DB syntax error سیگنال بررسی است، نه اثبات حمله یا ضعف. Alert را با Rate، Route، Release، User/tenant impact و Telemetry دیگر ترکیب کنید تا Noise و Block اشتباه کم شود.

Runbook رخداد SQL Injection

  1. Scope سرویس/Route/Query/DB role و زمان احتمالی را مشخص و Evidence را Preserve کنید.
  2. مسیر آسیب‌پذیر را با Feature flag، محدودیت Route یا Rollback کم‌ریسک مهار کنید؛ WAF فقط پل موقت باشد.
  3. Credential/Token را بر اساس شواهد Exposure Rotate و دسترسی اضافی را بردارید.
  4. Query را با Safe API اصلاح و همه Call site/second-order consumerهای همان Pattern را جست‌وجو کنید.
  5. DB audit، تغییر داده، Confidentiality و Backup را با تیم Incident/Legal بررسی کنید.
  6. Unit/Integration/DAST مجاز و Retest مستقل انجام و Virtual patch را با Expiry حذف کنید.

پاک‌کردن Log یا Restart فوری پیش از حفظ Evidence می‌تواند تحلیل را از بین ببرد. پاسخ باید با سیاست حقوقی/حریم خصوصی سازمان هماهنگ باشد.

Runbook رخداد XSS

  1. Source، Context، Sink، Page/role، Stored content و Browserهای متأثر را ثبت کنید.
  2. Render/Component یا محتوای آلوده را مهار، Cache/CDN را مدیریت و Evidence را حفظ کنید.
  3. Auto-escape/Safe sink/Sanitizer Policy را در ریشه اصلاح و Sinkهای مشابه را جست‌وجو کنید.
  4. اگر شواهد Session/Secret exposure وجود دارد، Revoke/Rotate متناسب اجرا کنید؛ Rotation کور پیامد عملیاتی دارد.
  5. CSP/Trusted Types/WAF را برای کاهش اثر یا Detection تنظیم کنید، نه جای Patch.
  6. Contextهای SSR/DOM/Email/Admin را Retest و داده ذخیره‌شده را با فرایند امن Re-sanitize یا Quarantine کنید.

مثال ایرانی؛ Search و Ticket را یکجا نسنجید

فروشگاه «الف» Search دارد: عبارت جست‌وجو Value پارامتری است؛ Sort فقط از Map سه‌گزینه‌ای می‌آید؛ Tenant و وضعیت محصول در Scope سرور اعمال می‌شوند؛ DB role فقط Read لازم را دارد. همین تیم Ticket پشتیبانی دارد: متن ساده با Output encoding نمایش داده می‌شود و Rich text محدود فقط برای Agent مجاز، با Sanitizer Policy جدا و Safe sink Render می‌شود.

نام فارسی بلند، نیم‌فاصله، اعداد، URL، Quote و متن ترکیبی در Corpus هستند. تیم به‌جای یک تابع sanitize() عمومی، دو Contract متفاوت دارد—زیرا Query و DOM دو Interpreter متفاوت‌اند.

معیار و Dashboard

  • درصد Query pathهای Inventoryشده با Binding proof؛
  • تعداد Raw query/unsafe sink/escape hatch باز به تفکیک Owner و Age؛
  • پوشش Template/DOM/Email/Preview و Rich-text Policy؛
  • Patch age کتابخانه Sanitizer، Framework، Theme/Plugin؛
  • ASVS requirement pass/exception/retest با Version؛
  • Mean time to contain/fix/retest و Virtual patchهای منقضی؛
  • DB role over-privilege و CSP/Trusted Types rollout coverage؛
  • False positive/negative findings و Incidentهای واقعی.

تعداد Finding به‌تنهایی KPI کیفیت نیست؛ ابزار جدید ممکن است Finding را زیاد و امنیت را بهتر کند. Coverage، Age، Severity، Exploitability، Exposure و Closure evidence را کنار هم ببینید.

بودجه را بر اساس Risk و Change خرج کنید

Critical query/render path، Rich text، Admin/Support، Payment، Multi-tenant و Third-party integration به Review و Test عمیق‌تر نیاز دارند. هزینه پیشگیری شامل Training، Safe library، SAST rule، Unit test، Dependency patch، CSP rollout، Pen-test و Incident drill است.

بودجه‌ای که فقط License WAF می‌خرد ولی Owner اصلاح، Retest و Patch window ندارد، دفاع ناقص می‌سازد. مدل اولویت در راهنمای بودجه امنیت وب‌سایت کمک می‌کند Controlها به دارایی و تهدید متصل شوند.

Zero Trust جای Secure coding نیست

احراز پیوسته، Segmentation و Least privilege می‌توانند اثر رخداد را محدود کنند، اما داده‌ای که پس از احراز وارد Query ناامن یا DOM ناامن می‌شود همچنان خطر دارد. Zero Trust را به‌عنوان معماری دسترسی در کنار Secure coding ببینید؛ راهنمای Zero Trust وب‌اپلیکیشن این مرز را عمیق‌تر توضیح می‌دهد.

برنامه ۳۰/۶۰/۹۰روزه

۳۰ روز: Inventory و Hotspot

Source→Sink map، Raw query، dynamic identifiers، unsafe DOM sinks، Raw HTML، Rich text، DB roles و WordPress custom code را Inventory کنید. Critical pathها را با Owner و Test پایه پوشش دهید.

۶۰ روز: Safe default و Verification

Repository wrapperهای Safe query/render، allowlist mapping، Sanitizer Policy، SAST rules، unit/component tests و ASVS ۵.۰.۰ baseline را برقرار کنید. CSP Report-only و Trusted Types discovery را روی Browserهای هدف آغاز کنید.

۹۰ روز: Enforce و Operate

Roleها را کم، CSP/Trusted Types را Canary، DAST/Pen-test مجاز و Retest را اجرا کنید. Dashboard، Incident runbook، Secret rotation، Virtual-patch expiry و Quarterly sink/query review را وارد عملیات کنید.

موضوعات مکملی که به مقاله مستقل نیاز دارند

  • Source→Transform→Sink inventory generator برای SQL/Template/DOM/Email؛
  • Safe query wrapper و lint rules برای PHP/Node/Java/.NET و ORMهای رایج؛
  • Persian/RTL contextual encoding و Rich-text sanitizer regression corpus؛
  • Trusted Types migration kit با sink inventory، Report-only و Policy governance؛
  • ASVS ۵.۰.۰ evidence/retest registry و Dashboard برای تیم‌های ایرانی.

پرسش‌های متداول SQL Injection و XSS

آیا Input Validation به‌تنهایی جلوی SQL Injection را می‌گیرد؟

خیر. Validation نوع و دامنه Business را محدود می‌کند، اما دفاع اصلی برای Valueهای SQL، Parameterized query است. Identifier و Sort نیز باید از Mapping بسته داخلی بیایند.

آیا ORM یا React به‌طور خودکار مشکل را حل می‌کنند؟

Safe defaultها ریسک را کم می‌کنند، اما Raw query، literal expression، Raw HTML، unsafe sink و trust bypass همچنان آسیب‌پذیری می‌سازند. Escape hatchهای Stack باید Inventory، Review و Test شوند.

تفاوت Output Encoding و Sanitization چیست؟

Encoding داده را در Context مشخص به متن غیرقابل اجرا تبدیل می‌کند. Sanitization برای حالتی است که بخشی از HTML باید حفظ شود و عناصر/Attribute/Protocolهای ناامن طبق Policy حذف شوند.

آیا CSP، Trusted Types یا WAF جای اصلاح کد را می‌گیرند؟

نه. CSP و Trusted Types می‌توانند اجرای DOM XSS را محدود یا Sink را Enforce کنند و WAF لایه کشف/کاهش است؛ Query binding، Auto-escape، Contextual encoding، Sanitization و Safe sink همچنان کنترل ریشه‌اند.

برای WordPress از کجا شروع کنیم؟

Core/Theme/Plugin را Patch کنید؛ Custom query را با $wpdb->prepare() و Custom output را با Escape متناسب با Context بررسی کنید؛ HTML مجاز را با KSES، دسترسی را با Capability و REST را با Permission callback کنترل کنید و سپس Retest بگیرید.

جمع‌بندی

قاعده SQLi ساده اما فراگیر است: داده را هرگز با ساختار Query مخلوط نکنید؛ Value را bind و بخش‌های غیرقابل bind را از Mapping ثابت بسازید. قاعده XSS نیز وابسته به Context است: Auto-escape را حفظ، متن را Encode، HTML لازم را Sanitize و DOM را با Safe sink به‌روزرسانی کنید.

Least privilege، CSP، Trusted Types، WAF، Header، Test و Monitoring لایه‌های ارزشمندند اما هیچ‌کدام جای کنترل ریشه را نمی‌گیرند. تیمی که Source→Sink، Owner، Evidence و Retest دارد، از شعار «ورودی را پاک کردیم» عبور می‌کند و یک برنامه دفاعی قابل اندازه‌گیری می‌سازد.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *