نصب mod_xsendfile در دایرکت ادمین
mod_xsendfile یک ماژول کوچک برای وبسرور آپاچی است که اجازه میدهد اسکریپت شما فقط درباره «مجاز بودن» دانلود تصمیم بگیرد، اما خودِ فایل را آپاچی تحویل کاربر بدهد. روی سرور دایرکتادمین این ماژول بهصورت پیشفرض وجود ندارد و باید سورس آن را با ابزار apxs کامپایل کنید، سپس با دو دستور XSendFile و XSendFilePath آن را پیکربندی کنید.
مشکلی که این ماژول حل میکند
روش معمول برای محافظت از فایلهای دانلودی این است که فایل را بیرون از مسیر عمومی سایت نگه دارید و یک اسکریپت PHP بعد از بررسی لاگین یا اشتراک کاربر، محتوای فایل را با readfile() یا fpassthru() به خروجی بفرستد. این روش درست کار میکند، اما گران تمام میشود: تا لحظه آخرِ دانلود یک پروسه PHP اشغال میماند، حافظه و بافر مصرف میشود، و مدیریت درخواستهای Range (ادامه دانلود نیمهکاره) هم بر عهده خودِ کد است. بالا بردن memory_limit با ini_set هم لزوماً جواب نمیدهد، چون بسیاری از میزبانها این تابع را بستهاند؛ دلیلش را در رفع خطای ini_set() has been disabled توضیح دادهایم.
با mod_xsendfile اسکریپت شما بهجای بدنه فایل، فقط یک هدر برمیگرداند:
X-Sendfile: /home/user/private/file.zip
آپاچی این هدر را میبیند، بدنه پاسخِ اسکریپت را دور میریزد، هدر را از پاسخ نهایی حذف میکند و فایل را خودش سرو میکند. نتیجه این است که پروسه PHP بلافاصله آزاد میشود و ارسال فایل با همان مسیر بهینهای انجام میگیرد که آپاچی برای فایلهای استاتیک استفاده میکند؛ یعنی پشتیبانی از Range و ادامه دانلود، هدرهای کش و If-Modified-Since بدون یک خط کد اضافه.
چه زمانی واقعاً به آن نیاز دارید
- سایتها و پنلهایی که فایلهای حجیم عرضه میکنند و همزمانی دانلود بالاست.
- فایلهای پولی، اشتراکی یا محدود به کاربران لاگینکرده که نباید با لینک مستقیم قابل دسترسی باشند.
- فایلهایی که عمداً بیرون از
public_htmlنگهداری میشوند. - مواردی که کاربران از دانلود منیجر استفاده میکنند و قطعشدن و ادامه دانلود اهمیت دارد.
اگر سرویس شما اساساً حول دانلود میچرخد، معماری میزبانی هم به اندازه این ماژول مهم است؛ هاست دانلود با کنترلپنل دایرکتادمین دقیقاً برای همین الگوی مصرف طراحی شده است. در مقابل، اگر ترافیک دانلود شما پایین است، احتمالاً افزودن یک ماژول کامپایلشده دستی به سرور، پیچیدگی بیدلیل اضافه میکند.
پیشنیازها روی سرور دایرکتادمین
کامپایل ماژول نیاز به دسترسی root دارد، بنابراین این کار فقط روی سرور ابری یا سرور اختصاصی ممکن است و روی هاست اشتراکی قابل انجام نیست. علاوه بر آن باید ابزارهای بیلد (gcc و make) و ابزار apxs روی سرور موجود باشند. چون دایرکتادمین آپاچی را از سورس میسازد، apxs معمولاً همراه خود آپاچی نصب شده است. مسیر آن را بررسی کنید:
which apxs
در بسیاری از نصبهای دایرکتادمین این مسیر /usr/sbin/apxs است، اما بسته به نسخه CustomBuild ممکن است متفاوت باشد، پس به خروجی دستور بالا اعتماد کنید نه به حدس.
دریافت سورس و کامپایل
سورس رسمی ماژول را از صفحه نویسنده آن یا مخزن گیتهاب بگیرید: tn123.org/mod_xsendfile و github.com/nmaier/mod_xsendfile. آخرین نسخه پایدار 0.12 است و نویسنده ماژول سازگاری آن با آپاچی 2.4 را تأیید کرده است. نسخه 1.0 beta1 هم در مخزن موجود است، اما هنوز بتا به حساب میآید و برای سرور فعال گزینه مناسبی نیست. آرشیو را روی سرور باز کنید، وارد پوشه آن شوید و ماژول را بسازید:
/usr/sbin/apxs -cia mod_xsendfile.c
سه سوییچ این دستور یعنی: -c کامپایل، -i نصب فایل .so در پوشه ماژولهای آپاچی و -a افزودن خط LoadModule به فایل پیکربندی اصلی.
یک هشدار مهم: در دایرکتادمین فایلهای پیکربندی آپاچی توسط CustomBuild بازنویسی میشوند. بنابراین بهتر است بهجای -a از -ci استفاده کنید و خط زیر را دستی در یکی از فایلهای سفارشی آپاچی (در نصبهای رایج /etc/httpd/conf/extra/httpd-includes.conf) قرار دهید تا بعد از بیلد مجدد باقی بماند:
LoadModule xsendfile_module modules/mod_xsendfile.so
همچنین توجه داشته باشید هر بار که آپاچی را با CustomBuild دوباره بیلد کنید، فایل .so از بین میرود و باید کامپایل را تکرار کنید. اگر مدیریت لایسنس و بهروزرسانی پنل را برونسپاری کردهاید، این نکته را در فرایند بهروزرسانی خود لحاظ کنید؛ سرویس لایسنس دایرکتادمین شامل نصب و کانفیگ اولیه پنل است، اما ماژولهای سفارشی آپاچی جزو بیلد استاندارد نیستند.
پیکربندی
ماژول دو دستور اصلی دارد. XSendFile On پردازش هدر را فعال میکند و XSendFilePath مشخص میکند فایلها فقط از کدام مسیرها اجازه سرو شدن دارند. این دو را داخل بلوک <VirtualHost> همان سایت (یا در فایل کانفیگ سفارشی همان دامنه) قرار دهید:
XSendFile OnXSendFilePath /home/user/private_files
سپس صحت پیکربندی را بررسی و آپاچی را ریلود کنید:
apachectl configtest
apachectl graceful
هشدار: دستور apachectl restart یا systemctl restart httpd اتصالهای در حال انجام (از جمله دانلودهای نیمهکاره کاربران) را قطع میکند. graceful این مشکل را ندارد و برای سرور فعال گزینه امنتری است.
برای اطمینان از بارگذاری ماژول:
httpd -M | grep xsendfile
فراخوانی از سمت اپلیکیشن
در PHP، بعد از انجام بررسیهای دسترسی، بهجای خواندن فایل فقط هدرها را ست کنید و اسکریپت را تمام کنید:
header('X-Sendfile: /home/user/private_files/report.zip');header('Content-Type: application/octet-stream');header('Content-Disposition: attachment; filename="report.zip"');exit;
هیچ خروجی دیگری تولید نکنید. Content-Type و Content-Disposition را خودتان تعیین میکنید، اما Content-Length و هدرهای مربوط به کش را آپاچی بر اساس فایل واقعی میسازد.
هر دو این کارها روی دایرکتادمین با همان ابزار CustomBuild انجام میشود؛ اگر با دستورهای da build آشنا نیستید، همین روند گامبهگام در نصب SpamAssassin در دایرکت ادمین نشان داده شده است.
نکات امنیتی و عیبیابی
مهمترین نکته امنیتی، محدود کردن مسیرهاست. اگر نام فایل از ورودی کاربر ساخته شود و XSendFilePath را تنظیم نکرده باشید، یک باگ ساده در کد میتواند به خواندن فایلهای حساس سیستم منجر شود. مسیر مجاز را تا حد ممکن باریک بگیرید و ورودی کاربر را هم مستقل از آن اعتبارسنجی کنید.
اگر بعد از تنظیمات هنوز فایل دانلود نمیشود، این موارد را بررسی کنید: خودِ هدر X-Sendfile نباید در پاسخ نهایی به مرورگر دیده شود؛ با curl -sI روی آدرس اسکریپت تست کنید. دیده شدن این هدر یعنی ماژول فعال نیست یا XSendFile در آن VirtualHost اعمال نشده است. همچنین کاربری که آپاچی با آن اجرا میشود باید دسترسی خواندن فایل و پیمایش تمام پوشههای مسیر را داشته باشد.
یک محدودیت را هم در نظر بگیرید: این ماژول مخصوص آپاچی است. اگر جلوی آپاچی از Nginx استفاده میکنید یا وبسرور شما Nginx است، معادل آن هدر X-Accel-Redirect است و در LiteSpeed هدر X-LiteSpeed-Send-File کاربرد دارد. پیش از پیادهسازی، بررسی کنید پاسخ نهایی از کدام وبسرور به کاربر میرسد. اگر ترکیب وبسرور شما پیچیده است یا نمیخواهید ریسک تغییر پیکربندی روی سرور فعال را بپذیرید، انجام این کار را به پشتیبانی و مدیریت سرور بسپارید و ابتدا روی یک سرور تست آن را بسنجید.