دسته بندی:

بلاک کردن آی‌پی در htaccess؛ از مسدود کردن کاربر مزاحم تا CloudFlare و فایروال سرور

بلاک کردن آی‌پی در htaccess؛ از مسدود کردن کاربر مزاحم تا CloudFlare و فایروال سرور

یک آی‌پی مشخص هر ثانیه ده‌ها درخواست به سایت می‌فرستد و لود سرور بالا رفته است؛ یا ترافیک عجیبی از یک سایت دیگر به صفحه‌های شما سرازیر شده؛ یا تازه متوجه شده‌اید تصویرهای سایتتان مستقیم روی یک وبلاگ دیگر لود می‌شوند و پهنای باند شما را مصرف می‌کنند. هر سه مسئله یک مسیر حل مشترک دارند: اول منبع ترافیک را دقیق مشخص کنید، بعد آن را در لایه‌ای ببندید که واقعاً به آن لایه مربوط است.

در ادامه همین مسیر را قدم‌به‌قدم می‌رویم: از شمارش کانکشن‌ها روی سرور تا بلاک کردن آی‌پی در htaccess، حالت خاصِ سایت‌های پشت CloudFlare، ساخت صفحه ۴۰۳ اختصاصی و نقطه‌ای که باید کار را از htaccess به فایروال سرور منتقل کنید.

قدم اول: مزاحم را دقیق شناسایی کنید

قبل از نوشتن هر قانونی، باید بدانید کدام آی‌پی چقدر سهم دارد. اگر به سرور دسترسی SSH دارید، ساده‌ترین سنجه، شمارش کانکشن‌های باز به تفکیک آی‌پی مبدأ است:

ss -tn | grep ESTAB | awk '{print $5}' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn | head -20

خروجی، بیست آی‌پی با بیشترین کانکشن باز را از پرتعدادترین به کم‌تعدادترین نشان می‌دهد. دستور قدیمی معادل آن با netstat این بود:

netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20

این نسخه هنوز کار می‌کند، اما netstat بخشی از بسته منسوخ net-tools است و روی نصب‌های تمیزِ AlmaLinux، Rocky Linux و اوبونتوی جدید اصلاً وجود ندارد. جایگزین رسمی آن ss است؛ عادت کنید همان را بنویسید. اگر لازم شد net-tools را نصب کنید، روی خانواده RHEL 8 و ۹ دستور dnf install net-tools است، نه yum.

شمارش درخواست‌ها در لاگ وب‌سرور

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

awk '{print $1}' /var/log/httpd/access_log | sort | uniq -c | sort -rn | head -20

مسیر لاگ بسته به سیستم فرق می‌کند: روی AlmaLinux/Rocky/CentOS مسیر /var/log/httpd/access_log، روی دبیان و اوبونتو /var/log/apache2/access.log و روی سرورهای cPanel لاگ هر دامنه جداگانه در /etc/apache2/logs/domlogs/ قرار دارد. با همین الگو می‌توانید پرتکرارترین Referrer یا User-Agent را هم دربیاورید و بفهمید ترافیک از کجا می‌آید.

هشدار مهم: پشت CDN این اعداد دروغ می‌گویند

اگر سایت پشت CloudFlare یا هر CDN دیگری است، تا وقتی تنظیمات لازم را انجام نداده‌اید همه این آی‌پی‌ها متعلق به خودِ CDN هستند، نه بازدیدکننده. در این حالت بلاک کردن آنچه در لاگ می‌بینید یعنی بلاک کردن CDN و از دسترس خارج شدن کل سایت. بخش مربوط به CloudFlare در همین مقاله دقیقاً برای همین است.

بلاک کردن آی‌پی و رنج در htaccess

ساده‌ترین لایه‌ای که معمولاً به آن دسترسی دارید (حتی روی هاست اشتراکی) فایل .htaccess است.

یک تذکر قبل از شروع: آی‌پی‌هایی که در نمونه‌های این مقاله می‌بینید فقط نمونه‌اند. رنج‌های 192.0.2.0/24، 198.51.100.0/24 و 203.0.113.0/24 رنج‌های رسمیِ «مستندسازی» هستند و عمداً به‌کار رفته‌اند تا اگر کسی کد را عیناً کپی کرد، آی‌پی واقعیِ کسی را نبندد. هیچ‌کدام از این خط‌ها را بدون جایگزین کردن آی‌پی واقعی خودتان اجرا نکنید.

نحو آپاچی ۲.۴ — چیزی که امروز باید بنویسید

برای بستن یک یا چند آی‌پی و باز گذاشتن بقیه:

<RequireAll>
    Require all granted
    Require not ip 2.144.32.54
    Require not ip 216.176.12.21
</RequireAll>

نکته‌ای که خیلی‌ها را گیر می‌اندازد: Require not را نمی‌توان تنها نوشت. یک شرط منفی فقط می‌تواند «رد» یا «بی‌طرف» شود و هیچ‌وقت خودش دسترسی نمی‌دهد، بنابراین اگر تنها بنویسیدش نتیجه قابل‌اتکایی نمی‌گیرید. حتماً باید داخل بلوک <RequireAll> و کنار یک Require all granted بیاید.

برای بستن یک رنج، همان دستور را با نشانه‌گذاری CIDR بنویسید:

<RequireAll>
    Require all granted
    Require not ip 198.51.100.0/24
    Require not ip 203.0.113
</RequireAll>

هر دو شکل معتبرند: 198.51.100.0/24 یعنی کل آن ساب‌نت، و 203.0.113 یعنی هر آی‌پی که با این سه اوکتت شروع شود (آپاچی آی‌پی ناقصِ یک تا سه اوکتتی را می‌پذیرد).

حالت برعکس: فقط چند آی‌پی اجازه داشته باشند

مثلاً برای بستن پوشه مدیریت روی همه به‌جز دفتر خودتان:

Require all denied
Require ip 192.0.2.10
Require ip 192.0.2.11

اینجا بلوک <RequireAll> لازم نیست: چند دستور Require پشت‌سرهم به‌صورت پیش‌فرض داخل یک <RequireAny> ضمنی قرار می‌گیرند، یعنی «یکی از این‌ها کافی است». Require all denied هیچ‌وقت اجازه نمی‌دهد، پس فقط دو آی‌پی فهرست‌شده رد می‌شوند و بقیه ۴۰۳ می‌گیرند.

نحو قدیمی order/deny/allow و چرا دیگر ننویسیدش

در بسیاری از آموزش‌های قدیمی (و در نسخه پیشین همین مطلب) این شکل را می‌بینید:

order deny,allow
deny from all
allow from 192.0.2.10
allow from 192.0.2.11

این نحو مربوط به آپاچی ۲.۲ است. در آپاچی ۲.۴ فقط تا وقتی کار می‌کند که ماژول سازگاری mod_access_compat فعال باشد و رسماً منسوخ اعلام شده است. مهم‌تر اینکه هرگز نباید نحو قدیم و جدید را در یک مسیر قاطی کنید؛ نتیجه ترکیب Order/Allow/Deny با Require غیرقابل پیش‌بینی است و معمولاً یا همه را می‌بندد یا هیچ‌کس را. اگر فایل قدیمی دارید، کل بلوک را یکجا به نحو ۲.۴ تبدیل کنید.

یک نکته اجرایی: تغییرات .htaccess بلافاصله اعمال می‌شود و نیازی به ری‌استارت آپاچی ندارد. در عوض روی سرور اختصاصی باید مطمئن شوید AllowOverride برای آن مسیر اجازه می‌دهد: برای دستورهای Require مقدار AuthConfig، برای Order/Allow/Deny قدیمی مقدار Limit و برای RewriteRule، SetEnvIf و ErrorDocument مقدار FileInfo لازم است. اگر یکی از این‌ها مجاز نباشد، آپاچی به‌جای اعمال قانون، خطای 500 Internal Server Error می‌دهد و در error_log می‌نویسد کدام دستور «not allowed here» بوده است — قبل از هر چیز همان لاگ را ببینید. روی cPanel هم اگر ترجیح می‌دهید با رابط گرافیکی کار کنید، ابزار IP Blocker همین قانون‌ها را برایتان می‌سازد.

بستن ورودی از یک سایت خاص (Referrer)

گاهی مشکل یک آی‌پی نیست؛ یک سایت دیگر به شما لینک داده و سیل کاربر یا ربات از آنجا می‌آید. برای رد کردن درخواست‌هایی که Referrer آن‌ها دامنه مشخصی است (این روش به فعال بودن mod_rewrite نیاز دارد):

RewriteEngine on
# Options +FollowSymLinks
RewriteCond %{HTTP_REFERER} otherdomain.com [NC]
RewriteRule .* - [F]

به‌جای otherdomain.com دامنه موردنظر را بگذارید. آن بک‌اسلش پیش از نقطه تزئینی نیست: در عبارت باقاعده، نقطه یعنی «هر کاراکتری»، پس . می‌نویسیم تا واقعاً نقطه معنی شود. خط Options +FollowSymLinks روی بیشتر سرورها از قبل فعال است؛ فقط اگر آپاچی خطای مربوط به آن داد کامنتش را بردارید.

یک نکته که در نمونه‌های قدیمی نادیده گرفته می‌شود: این الگو لنگر ندارد، یعنی هر جای مقدار Referrer که رشته پیدا شود قانون فعال می‌شود — از nototherdomain.com گرفته تا آدرسی مثل example.com/?u=otherdomain.com. اگر می‌خواهید فقط خودِ آن دامنه (و زیردامنه‌هایش) بسته شود، الگو را لنگردار بنویسید:

RewriteCond %{HTTP_REFERER} ^https?://([^.]+.)?otherdomain.com(/|$) [NC]
RewriteRule .* - [F]

چند دامنه هم‌زمان

RewriteEngine on
RewriteCond %{HTTP_REFERER} otherdomain1.com [NC,OR]
RewriteCond %{HTTP_REFERER} otherdomain2.com [NC,OR]
RewriteCond %{HTTP_REFERER} anotherdomain.com [NC]
RewriteRule .* - [F]

قاعده‌اش ساده است: همه شرط‌ها به‌جز آخری باید پرچم OR داشته باشند. اگر OR را از یکی بردارید، شرط‌ها با «و» ترکیب می‌شوند و چون یک درخواست فقط یک Referrer دارد، عملاً هیچ‌وقت هم‌زمان برقرار نمی‌شوند، یعنی قانون بی‌اثر می‌شود. پرچم NC هم یعنی بزرگی و کوچکی حروف مهم نباشد.

نتیجه برای بازدیدکننده مسدودشده، همان پیام استاندارد 403 Forbidden است؛ کمی جلوتر می‌بینیم چطور آن را با صفحه دلخواه خودتان جایگزین کنید.

همین کار بدون mod_rewrite

اگر mod_rewrite در دسترس نیست یا ترجیح می‌دهید همه‌چیز در یک زبان باشد:

SetEnvIf Referer "otherdomain.com" bad_referer

<RequireAll>
    Require all granted
    Require not env bad_referer
</RequireAll>

دقت کنید نام هدر در استاندارد HTTP با املای ناقصِ تاریخی Referer (یک «r») نوشته می‌شود؛ در SetEnvIf باید همین شکل را بنویسید، در حالی که متغیر mod_rewrite نامش %{HTTP_REFERER} است.

و یک هشدار جدی که نباید نادیده بگیرید: هدر Referer را کلاینت می‌فرستد و هر کسی می‌تواند آن را جعل کند یا اصلاً نفرستد. مرورگرهای امروزی هم به‌خاطر سیاست پیش‌فرض حریم خصوصی، در درخواست‌های بین‌دامنه‌ای معمولاً فقط دامنه (origin) را می‌فرستند نه آدرس کامل. این روش برای قطع کردن ترافیک ارجاعی مزاحم و اسپم خوب است، اما یک سازوکار امنیتی نیست.

جلوگیری از هات‌لینک تصویرها و مصرف پهنای باند

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

قانون پایه برای بستن هات‌لینک:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://([^.]+.)?yourdomain.com(/|$) [NC]
RewriteRule .(gif|jpe?g|png|webp|css)$ - [F,NC]

سه اصلاح مهم نسبت به نسخه‌های قدیمی این قانون: اولاً الگو باید https? باشد نه فقط http، وگرنه روی سایتی که امروز کاملاً روی HTTPS است هر درخواست داخلی هم مسدود می‌شود و تصویرهای خودتان از کار می‌افتند. ثانیاً نقطه در yourdomain.com باید escape شود. ثالثاً بهتر است پسوندهای امروزی مثل png و webp را هم اضافه کنید. پایان الگو را هم (/|$) نوشته‌ایم تا Referrerهای فقط-دامنه (بدون اسلش انتهایی) از قلم نیفتند.

خط !^$ یعنی درخواست‌های بدون Referrer اجازه عبور دارند. این خط را نگه دارید: باز کردن مستقیم آدرس تصویر، بعضی افزونه‌های حریم خصوصی و بخشی از کلاینت‌ها اصلاً Referrer نمی‌فرستند و بدون این استثنا کاربران واقعی را هم می‌بندید. طبیعتاً همین یعنی دور زدن این قانون هم ساده است.

قبل از فعال کردن، قانون را روی یک مرورگر و یک curl با هدر Referrerِ دستی تست کنید؛ اشتباه در نام دامنه، همه تصویرهای خودِ سایت را از کار می‌اندازد:

curl -I -e "https://yourdomain.com/" https://yourdomain.com/wp-content/uploads/test.jpg
curl -I -e "https://otherdomain.com/" https://yourdomain.com/wp-content/uploads/test.jpg

نمایش تصویر جایگزین

به‌جای خطای ۴۰۳ می‌توانید تصویر دلخواه خودتان (مثلاً لوگو یا یک هشدار) را نشان دهید:

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/hotlink.jpg$
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://([^.]+.)?yourdomain.com(/|$) [NC]
RewriteRule .(gif|jpe?g|png)$ /hotlink.jpg [R=302,L,NC]

خط اول شرط‌ها را دست‌کم نگیرید. بدون آن، خودِ hotlink.jpg هم یک فایل jpg است و چون مرورگر هنگام دنبال کردن ریدایرکت همان Referrer سایت مزاحم را می‌فرستد، دوباره وارد همان قانون می‌شود و یک حلقه ریدایرکت بی‌پایان می‌سازد؛ این ایراد در نسخه‌های قدیمی این قانون هست. فایل جایگزین را در ریشه سایت بگذارید و حجمش را کم نگه دارید.

یک ملاحظه: اگر تصویرهایتان برای جست‌وجوی تصویری گوگل اهمیت دارند، پیش از فعال کردن هات‌لینک‌بلاک تصمیم بگیرید که Referrer موتورهای جست‌وجو را استثنا کنید یا نه. روی cPanel هم ابزار Hotlink Protection همین قانون‌ها را با رابط گرافیکی می‌سازد.

وقتی سایت پشت CloudFlare یا CDN است

در این حالت آپاچی آی‌پی بازدیدکننده را نمی‌بیند؛ چیزی که به سرور می‌رسد آی‌پی سرورهای CloudFlare است. بنابراین Require ip و Require not ip روی آی‌پی واقعی کاربر جواب نمی‌دهند. آی‌پی اصلی داخل هدرهای درخواست منتقل می‌شود: CloudFlare آن را در CF-Connecting-IP می‌گذارد و اغلب CDNها در X-Forwarded-For.

راه سریع: خواندن هدر با SetEnvIf

SetEnvIf CF-Connecting-IP "^203.0.113.54$" blocked_ip
SetEnvIf CF-Connecting-IP "^203.0.113." blocked_ip

<RequireAll>
    Require all granted
    Require not env blocked_ip
</RequireAll>

اینجا دو اشتباه رایج را اصلاح کرده‌ایم. اول اینکه مقدار SetEnvIf یک عبارت باقاعده است، پس نوشتن 203.0.113.54 بدون escape و بدون لنگر، آی‌پی‌های ناخواسته دیگری را هم می‌گیرد؛ شکل درست "^203.0.113.54$" است. دوم اینکه برای یک رنج، الگوی "^203.0.113.*" که در نمونه‌های قدیمی دیده می‌شود غلط است: .* یعنی «صفر یا چند نقطه»، نه «هر ادامه‌ای». شکل درست "^203.0.113." است.

حالت برعکس هم به همین سادگی است — مثلاً وقتی می‌خواهید پشت CDN فقط چند آی‌پی مشخص به یک مسیر دسترسی داشته باشند:

SetEnvIf CF-Connecting-IP "^192.0.2.10$" allowed_ip
SetEnvIf CF-Connecting-IP "^192.0.2.11$" allowed_ip

Require env allowed_ip

این دقیقاً معادل مدرنِ همان order deny,allow / deny from all / allow from env=allow ی است که در آموزش‌های قدیمی می‌بینید و باید جایگزینش شود.

اگر مجبورید X-Forwarded-For را بخوانید، حواستان باشد این هدر می‌تواند فهرستی از چند آی‌پی جداشده با کاما باشد، پس لنگر $ در انتها ممکن است هیچ‌وقت برقرار نشود.

راه درست: mod_remoteip

راه‌حل تمیزتر این است که به آپاچی یاد بدهید آی‌پی واقعی را از هدر بردارد و از آن به بعد همه‌چیز — قانون‌های دسترسی، لاگ‌ها، محدودسازی نرخ و افزونه‌های امنیتی — خودبه‌خود درست کار کند. این کار با ماژول mod_remoteip انجام می‌شود:

RemoteIPHeader CF-Connecting-IP

# به‌جای خطوط زیر، رنج‌های رسمی CloudFlare را یکی‌یکی وارد کنید
RemoteIPTrustedProxy xxx.xxx.xxx.0/20
RemoteIPTrustedProxy xxx.xxx.xxx.0/22

ماژول را جداگانه فعال کنید و آن را دوباره LoadModule نکنید: روی AlmaLinux/Rocky/CentOS و سرورهای cPanel معمولاً از قبل بارگذاری شده و افزودن دوباره‌اش فقط اخطار «module already loaded» می‌دهد. روی دبیان و اوبونتو هم به‌جای نوشتن مسیر فایل، دستور a2enmod remoteip را بزنید. اگر واقعاً روی یک نصب دستی باید خودتان بارگذاری کنید، مسیر ماژول روی خانواده RHEL معمولاً modules/mod_remoteip.so و روی دبیان/اوبونتو /usr/lib/apache2/modules/mod_remoteip.so است.

دو نکته حیاتی: این دستورها در .htaccess کار نمی‌کنند و باید در کانفیگ اصلی آپاچی یا VirtualHost نوشته شوند، یعنی به دسترسی root و سرور اختصاصی/مجازی نیاز دارید. و در RemoteIPTrustedProxy باید رنج‌های رسمی CloudFlare را بگذارید؛ این فهرست تغییر می‌کند، پس آن را از صفحه رسمی آی‌پی‌های CloudFlare بردارید و در کانفیگ خود به‌روز نگه دارید. اگر این خط‌ها را ننویسید، آپاچی هدر را از هر مبدأیی قبول می‌کند و عملاً هر کسی می‌تواند آی‌پی خودش را جعل کند.

روی سرور زنده، هیچ‌وقت کانفیگ را بدون تست نحوی ری‌استارت نکنید؛ یک خطای تایپی کافی است تا آپاچی بالا نیاید و همه سایت‌های سرور از دسترس خارج شوند:

apachectl configtest        # یا: httpd -t
systemctl restart httpd     # AlmaLinux / Rocky / CentOS

apache2ctl configtest
systemctl restart apache2   # Debian / Ubuntu

فقط وقتی خروجی Syntax OK بود ری‌استارت کنید. روش‌های قدیمی service httpd restart و /etc/init.d/httpd restart روی توزیع‌های امروزی یا کار نمی‌کنند یا فقط به systemd پاس داده می‌شوند؛ همان systemctl را بنویسید. روی سرورهای cPanel دستور اختصاصی /scripts/restartsrv_httpd هم در دسترس است.

نکته امنیتی که نباید از آن بگذرید

هدرهای X-Forwarded-For و CF-Connecting-IP را هر کسی می‌تواند دستی در درخواست خود بگذارد. اگر آی‌پی اصلی سرور شما بیرون درز کرده باشد و کسی بتواند مستقیم — بدون عبور از CloudFlare — به آن وصل شود، می‌تواند هر آی‌پی دلخواهی را در این هدر جا بزند و همه قانون‌های بالا را دور بزند یا حتی خودش را در جایگاه یک آی‌پی مجاز بنشاند. به همین دلیل:

  • حتماً RemoteIPTrustedProxy را تنظیم کنید تا آپاچی فقط هدر آمده از پراکسی‌های مورد اعتماد را قبول کند.
  • در فایروال سرور، پورت‌های ۸۰ و ۴۴۳ را فقط به روی رنج‌های CloudFlare باز بگذارید و بقیه ترافیک مستقیم را ببندید. قبل از این کار مطمئن شوید همه رکوردهای DNS آن دامنه واقعاً از CloudFlare عبور می‌کنند (ابر نارنجی)؛ رکوردهایی که Proxy آن‌ها خاموش است — یا سرویس‌هایی مثل کنترل‌پنل و وب‌میل که روی همان پورت‌ها هستند — با این قانون قطع می‌شوند. این قانون را هم فقط روی پورت ۸۰ و ۴۴۳ ببندید و به پورت SSH دست نزنید تا خودتان بیرون نمانید.

صفحه ۴۰۳ اختصاصی با ErrorDocument

پیام پیش‌فرض 403 Forbidden آپاچی خشک و بی‌توضیح است. یک فایل 403.html کنار .htaccess بسازید و در آن هر چه می‌خواهید — توضیح کوتاه، راه تماس با پشتیبانی — بگذارید، بعد در .htaccess:

ErrorDocument 403 /403.html

<RequireAll>
    Require all granted
    Require not ip 2.144.32.54
    Require not ip 216.176.12.21
</RequireAll>

<Files "403.html">
    Require all granted
</Files>

بلوک <Files> را حذف نکنید. وقتی آپاچی درخواست کاربر مسدودشده را رد می‌کند، برای نمایش صفحه خطا یک درخواست داخلی به /403.html می‌زند و همان قانون‌های دسترسی دوباره اجرا می‌شوند؛ بدون این استثنا، کاربر مسدودشده به فایل ۴۰۳ هم دسترسی ندارد و در نهایت همان صفحه پیش‌فرض آپاچی را می‌بیند. در نمونه‌های قدیمی این کار با بلوک <Limit GET HEAD POST> انجام می‌شد؛ آن روش دو ایراد دارد: فقط همان متدهای فهرست‌شده را پوشش می‌دهد و متدهای دیگر آزاد می‌مانند، و بر پایه نحو منسوخ order/deny/allow نوشته شده است. شکل بالا هم امن‌تر است و هم کوتاه‌تر.

دو نکته تکمیلی: مسیر ErrorDocument باید نسبت به ریشه سایت باشد (با / شروع شود)، نه یک آدرس کامل با دامنه؛ و در محتوای صفحه ۴۰۳ آی‌پی مسدودشده یا دلیل دقیق بلاک را ننویسید، چون همین اطلاعات به کسی که دنبال دور زدن است کمک می‌کند.

کِی htaccess کافی نیست: فایروال سرور و WAF

بلاک در .htaccess در بالاترین لایه اتفاق می‌افتد. یعنی اتصال TCP برقرار شده، هندشیک TLS انجام شده و یک worker آپاچی درگیر پاسخ‌دادن شده است و تازه بعد از همه این‌ها درخواست رد می‌شود. برای چند آی‌پی مزاحم کاملاً کافی است؛ برای سیل درخواست، خودش بخشی از مشکل می‌شود. اگر به سرور مجازی یا اختصاصی با دسترسی root دسترسی دارید، در این حالت‌ها بلاک را پایین‌تر ببرید.

CSF

روی سرورهایی که ConfigServer Security & Firewall نصب است:

csf -d 203.0.113.54 "flooding /wp-login.php"   # بلاک دائم
csf -td 203.0.113.54 3600 "temporary"          # بلاک موقت، 1 ساعت (3600 ثانیه)
csf -dr 203.0.113.54                           # حذف بلاک
csf -g 203.0.113.54                            # جست‌وجوی قانون‌های مربوط به یک آی‌پی
csf -a 192.0.2.10                              # قرار دادن در لیست سفید
csf -r                                         # بارگذاری مجدد قانون‌ها

فهرست بلاک‌ها در /etc/csf/csf.deny و لیست سفید در /etc/csf/csf.allow نگهداری می‌شود و همین‌ها را می‌توانید مستقیم ویرایش کنید و بعد csf -r بزنید. قبل از هر کاری آی‌پی خودتان را با csf -a در لیست سفید بگذارید؛ در غیر این صورت یک قانون اشتباه می‌تواند دسترسی SSH خودتان را هم قطع کند. برای ری‌استارت سرویس هم به‌جای service csf restart قدیمی، systemctl restart csf lfd را بنویسید.

iptables، nftables و firewalld

بدون CSF، ساده‌ترین بلاک با iptables:

iptables -I INPUT -s 203.0.113.54 -j DROP
iptables -I INPUT -s 198.51.100.0/24 -j DROP

حواستان باشد این قانون‌ها با ری‌استارت سرور از بین می‌روند. برای ماندگار کردنشان روی دبیان/اوبونتو بسته iptables-persistent و دستور netfilter-persistent save و روی خانواده RHEL بسته iptables-services و دستور service iptables save لازم است. روی AlmaLinux و Rocky Linux نسخه ۸ و ۹ اما پشت‌صحنه nftables است و ابزار پیش‌فرض مدیریت، firewalld؛ آنجا بهتر است اصلاً سراغ iptables خام نروید:

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.54" reject'
firewall-cmd --reload

سوئیچ --permanent همان چیزی است که قانون را ماندگار می‌کند؛ بدون آن قانون تا اولین firewall-cmd --reload یا ری‌استارت سرویس بیشتر عمر نمی‌کند. یک احتیاط همیشگی: هیچ قانون فایروالی را روی سروری که فقط از راه SSH به آن دسترسی دارید بدون داشتن کنسول اضطراری (KVM یا کنسول پنل ارائه‌دهنده) اجرا نکنید. روی هاست اشتراکی هم به هیچ‌کدام از این‌ها دسترسی ندارید؛ آنجا آی‌پی‌ها و نمونه لاگ را به پشتیبانی بدهید تا در سطح سرور اعمال شود.

WAF کلودفلر

اگر سایت پشت CloudFlare است، منطقی‌ترین جا برای بلاک، خود CloudFlare است، نه سرور شما. آنجا درخواست اصلاً به سرور نمی‌رسد، پس نه پهنای باند مصرف می‌کند و نه پردازنده. قانون‌های IP Access Rules و WAF امکان بلاک بر اساس آی‌پی، رنج، کشور و ASN را می‌دهند و در همان لایه هم می‌توانید به‌جای بلاک کامل، Challenge بگذارید که برای ربات‌ها گران و برای کاربر واقعی بی‌دردسر است.

چیزهایی که این روش‌ها انجام نمی‌دهند

قبل از اینکه احساس امنیت کنید، این محدودیت‌ها را جدی بگیرید:

  • هدر Referer و X-Forwarded-For قابل جعل‌اند. هر قانونی که بر پایه محتوای این دو هدر نوشته شود، در برابر مهاجم آگاه بی‌اثر است. برای فیلتر کردن ترافیک مزاحم خوب‌اند، برای کنترل دسترسی حساس نه.
  • بلاک دستی آی‌پی، ضدِ دیداس نیست. در یک حمله واقعی، منبع ترافیک هزاران آی‌پی چرخشی است و شما نمی‌توانید با .htaccess از آن جلو بزنید. لایه محافظت در برابر دیداس باید بالادست سرور باشد.
  • آی‌پی هویت نیست. آی‌پی خانگی اغلب داینامیک است و ممکن است فردا به کاربر دیگری برسد؛ همچنین چند کاربر پشت یک NAT یک آی‌پی مشترک دارند. بستن یک رنج بزرگ می‌تواند کاربران بی‌گناه را هم بیرون بگذارد.
  • لیست سفید بر اساس آی‌پی شکننده است. اگر با Require all denied فقط آی‌پی خودتان را باز گذاشته‌اید و آی‌پی‌تان عوض شود، خودتان هم بیرون می‌مانید. حتماً یک راه دسترسی جایگزین (مثل SSH یا فایل‌منیجر کنترل‌پنل) داشته باشید تا بتوانید .htaccess را برگردانید.
  • قانون‌های IPv4 روی IPv6 اثر ندارند. اگر سرور شما آدرس IPv6 هم دارد و سایت روی آن سرو می‌شود، بازدیدکننده می‌تواند از مسیر IPv6 وارد شود و همه قانون‌های بالا را رد کند. برای همان آی‌پی، نسخه IPv6 را هم جداگانه ببندید.

چک‌لیست عملی

  • اول با ss و لاگ آپاچی مطمئن شوید کدام آی‌پی واقعاً منبع است.
  • اگر پشت CDN هستید، پیش از هر بلاکی mod_remoteip را تنظیم کنید تا آی‌پی واقعی را ببینید.
  • در .htaccess فقط نحو آپاچی ۲.۴ (Require) را بنویسید و آن را با order/deny/allow قاطی نکنید.
  • قبل از هر تغییر، یک نسخه پشتیبان از .htaccess بگیرید و بعد از اعمال، سایت را از یک شبکه دیگر (مثلاً دیتای موبایل) تست کنید.
  • برای هات‌لینک، الگو را با https? و نقطه escape‌شده بنویسید و حلقه ریدایرکت تصویر جایگزین را ببندید.
  • صفحه ۴۰۳ اختصاصی را با یک بلوک <Files> از دایره بلاک بیرون بگذارید.
  • روی سرور اختصاصی، هر تغییر کانفیگ آپاچی را با apachectl configtest بسنجید و تازه بعد ری‌استارت کنید.
  • برای حجم بالا یا حمله، بلاک را به CSF/فایروال یا مستقیماً به WAF کلودفلر منتقل کنید.
  • هر قانونی که می‌گذارید در یک فایل یادداشت با تاریخ و دلیل ثبت کنید؛ بلاک‌های فراموش‌شده یکی از رایج‌ترین دلایل «سایت برای بعضی کاربران باز نمی‌شود» هستند.