در یک فروشگاه ایرانی، جستوجو با Query پارامتری امن شده بود؛ اما گزینه مرتبسازی هنوز نام ستون را از Request میگرفت. همزمان متن Ticket پشتیبانی هنگام ذخیره پاکسازی میشد، ولی همان متن در پنل جدید داخل یک Sink ناامن DOM نمایش داده میشد. هر دو تیم میگفتند «ورودی را Sanitize کردهایم»؛ هر دو یک مرز مهم را ندیده بودند: داده در کدام Interpreter و Context مصرف میشود.
SQL Injection و XSS با Blacklist چند کاراکتر، WAF یا نام یک Framework حل نمیشوند. دفاع قابل اتکا باید Source→Transform→Sink را ثبت کند، ساختار دستور را از داده جدا نگه دارد، خروجی را متناسب با Context پردازش کند، اختیار و اثر را محدود سازد و با تست و Retest شواهد بسازد. این راهنما دفاعی است و روش سوءاستفاده ارائه نمیکند.
SQLi و XSS را در یک مدل مشترک ببینید
| بُعد | SQL Injection | Cross-Site Scripting |
|---|---|---|
| Interpreter اصلی | SQL engine یا لایه Query | HTML/DOM/JavaScript/CSS/URL parser مرورگر |
| شکست مرز | داده به بخشی از دستور Query تبدیل میشود | داده به Markup یا Code فعال تبدیل میشود |
| کنترل پیشگیرانه اصلی | Parameterized query/safe API | Auto-escape + contextual output encoding یا Sanitization لازم |
| بخش پویا | Identifier/Sort با Mapping بسته | Safe sink و پرهیز از Dangerous context |
| کاهش اثر | Least privilege، تفکیک Credential و Network | CSP، Trusted Types، Cookie/Session controls |
| کشف | Review/SAST/DAST/Test/DB telemetry | Template/DOM review، SAST/DAST، CSP report |
| راهحل ناقص | Escape دستی، WAF یا ORM label | Input 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 text | Auto-escape/HTML entity encoding | به Markup خام تبدیل نشود |
| Quoted attribute | Attribute encoding + نام Attribute ثابت | Event handler و Attribute اجرایی ممنوع |
| URL parameter | URL component encode سپس attribute encode | Scheme/Origin/Destination را Validate کنید |
| JavaScript | داده را از Inline script خارج و با API امن منتقل کنید | Dynamic code/eval و Event handler Context خطرناکاند |
| CSS | ترجیحاً مقدار ساختاری محدود با API Style | Selector/Rule/URL پویا نکنید |
| Rich HTML | Sanitizer 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های هدف وابسته است.
- Script/Style/Frame/Worker/Connect و Owner هر Dependency را Inventory کنید؛
- Inline و
eval-like dependency را کم کنید؛ - Policy پیشنهادی را در Report-Only منتشر و Reportها را Redact/Group کنید؛
- Journeyهای Login/Checkout/Admin را با Browser matrix تست کنید؛
- Policy اجرایی را Canary و Rollback path را آماده کنید؛
- 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
| لایه | SQLi | XSS | خروجی |
|---|---|---|---|
| Unit | Binding، dynamic mapping، type/range | Encoder/Sanitizer/URL policy/Safe component | Test deterministic سریع |
| Component/Integration | ORM/raw paths، DB role، error | Template/DOM/SSR/Email/Preview | Source→Sink proof |
| Static | Concatenation/raw query rules | Unsafe sink/escape hatch rules | PR finding با Owner |
| Dynamic | Endpoint/role/state coverage در محیط مجاز | Render/DOM behavior و header | Reproducible evidence |
| Manual | Business query/second-order path | Context chaining/framework gadget | Risk-aware review |
| Production-safe | Error/anomaly/DB telemetry | CSP report/component anomaly | Detection و 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،
postMessageorigin و 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
- Scope سرویس/Route/Query/DB role و زمان احتمالی را مشخص و Evidence را Preserve کنید.
- مسیر آسیبپذیر را با Feature flag، محدودیت Route یا Rollback کمریسک مهار کنید؛ WAF فقط پل موقت باشد.
- Credential/Token را بر اساس شواهد Exposure Rotate و دسترسی اضافی را بردارید.
- Query را با Safe API اصلاح و همه Call site/second-order consumerهای همان Pattern را جستوجو کنید.
- DB audit، تغییر داده، Confidentiality و Backup را با تیم Incident/Legal بررسی کنید.
- Unit/Integration/DAST مجاز و Retest مستقل انجام و Virtual patch را با Expiry حذف کنید.
پاککردن Log یا Restart فوری پیش از حفظ Evidence میتواند تحلیل را از بین ببرد. پاسخ باید با سیاست حقوقی/حریم خصوصی سازمان هماهنگ باشد.
Runbook رخداد XSS
- Source، Context، Sink، Page/role، Stored content و Browserهای متأثر را ثبت کنید.
- Render/Component یا محتوای آلوده را مهار، Cache/CDN را مدیریت و Evidence را حفظ کنید.
- Auto-escape/Safe sink/Sanitizer Policy را در ریشه اصلاح و Sinkهای مشابه را جستوجو کنید.
- اگر شواهد Session/Secret exposure وجود دارد، Revoke/Rotate متناسب اجرا کنید؛ Rotation کور پیامد عملیاتی دارد.
- CSP/Trusted Types/WAF را برای کاهش اثر یا Detection تنظیم کنید، نه جای Patch.
- 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 دارد، از شعار «ورودی را پاک کردیم» عبور میکند و یک برنامه دفاعی قابل اندازهگیری میسازد.






