واقعاً چه چیزی به یک سرور جدید حمله میکند — و واقعاً چه چیزی آن را از دستتان درمیآورد
یک آدرس تازهٔ IPv4 را رو به اینترنت بگیرید و ترافیک ناخواسته تقریباً بلافاصله از راه میرسد. نه به این خاطر که کسی متوجه شما شده باشد: کل فضای آدرسدهی بهطور پیوسته پویش میشود، و هر میزبانی که روی پورت 22 پاسخ بدهد فقط به صفی میپیوندد که از قبل در جریان بوده است. در روز اول، یک سرور تازهٔ معمولی چیزی بین چند صد تا چند دههزار تلاش احراز هویت SSH را ثبت میکند، که تقریباً همهشان روی root، admin، ubuntu، test و دهها نام قابلپیشبینی دیگر انجام میشوند، با رمزهای عبوری از فهرستی که در یک دهه گذشته تقریباً تغییری نکرده است. این خواندنش شبیه یک حمله است. اما بیشتر شبیه آبوهواست.
این تفاوت مهم است، چون تعیین میکند کدام کنترلها ارزش آن پانزده دقیقه را دارند. حدس زدن رمز عبور در آن حجم، با خاموشکردن احراز هویت با رمز عبور کم نمیشود — بلکه کاملاً حذف میشود. دیگر ریسک باقیماندهای برای مدیریتکردن، تنظیمی برای انجامدادن، و نرخی برای پایشکردن وجود ندارد. در همین حال، دو چیزی که واقعاً سرورهای کوچک را از دست صاحبانشان درمیآورند چندان جذاب نیستند: یک برنامهٔ رو-به-اینترنت که نسخهای با یک آسیبپذیریِ منتشرشده و چندماهه را اجرا میکند، و اعتبارنامهای که جای دیگری لو رفته و اینجا هم هنوز کار میکند. هیچکدام عجیب نیستند، و هیچکدام ربطی به این ندارند که SSH روی کدام پورت گوش میدهد.
یک حالت شکستِ سوم هم هست، و روی میزبانی که هرگز نپرسیده شما که هستید، این رایجترینِ همه است. خودتان را از سرور بیرون قفل میکنید. روی یک ارائهدهندهٔ معمولی، این فقط یک تیکت پشتیبانیِ آزاردهنده است: به چند سؤال جواب میدهید، ثابت میکنید همان شخصِ روی سابقهٔ صورتحساب هستید، و یک نفر دسترسی را برایتان بازنشانی میکند. اینجا آن مسیر وجود ندارد — نه از سرِ یک انتخابِ سیاستیِ اکراهآمیز، بلکه چون هویتی که باید بررسی میشد اصلاً هرگز جمعآوری نشده است. آنچه بهجایش وجود دارد، دسترسیای است که به حساب گره خورده، نه به یک شخص: یک کنسول اضطراریِ KVM-over-VNC در پنل، اسنپشاتهای ساعتی با نگهداریِ هفتروزه، و امکان بوتکردنِ چیزی کاملاً متفاوت. همینها کافیاند. اما فقط زمانی کافیاند که پیش از نیازتان یکبار امتحانشان کرده باشید.
پس چکلیست زیر بر اساس پیامد مرتب شده، نه بر اساس مد روز. کنترلهایی که یک کل دسته از حمله را حذف میکنند اول میآیند، آنهایی که فقط نویز را کم میکنند آخر میآیند، و قدمی که هیچ هزینهای ندارد و شب را نجات میدهد — یعنی اثبات اینکه هنوز میتوانید وارد شوید — پیش از همهٔ آنها میآید.
| نگرانی اصلی مردم | راه واقعیِ رسیدنش | چه چیزی جلویش را میگیرد | چه چیزی جلویش را نمیگیرد |
|---|---|---|---|
| حدس زدن رمز عبور SSH | پویشهای خودکار، هزاران بار در روز، فهرستهای نامکاربریِ عمومی | PasswordAuthentication no — بهطور کامل | یک پورت غیراستاندارد؛ یک رمز عبور طولانیتر |
| سرویسِ دارای آسیبپذیریِ شناختهشده | یک اسکنر هفتهها بعد از انتشار پچ، بنرِ نسخهٔ شما را پیدا میکند | بهروزرسانی خودکار امنیتی بههمراه یک سیاست ریبوت | فایروال، اگر پورت به هر حال باید باز باشد |
| اعتبارنامهٔ تکراری یا لو رفته | ورود موفق در همان تلاش اول، از آدرسی که تا بهحال ندیدهاید | احراز هویت فقط با کلید؛ اعتبارنامهٔ یکتا برای هر سرویس | محدودسازی نرخ — یک تلاش که یک هجوم نیست |
| سرویس داخلیِ در معرض | پایگاهداده، کش یا نقطهٔ پایانِ متریک که به آدرس wildcard بایند شده | بایند به loopback؛ رد پیشفرض ورودی روی هر دو خانواده | فرضکردنِ خصوصیبودنش، فقط چون هیچوقت به آن لینک ندادید |
| از دست دادن دسترسیِ خودتان | سختسازی میکنید، خارج میشوید، و تغییری که دادید اشتباه بوده | یک نشست باز دوم، یک اسنپشات، یک کنسولِ تستشده | اعتماد به نفس |
پیش از آنکه در را ببندید، راه خروج را بسازید
این همان توالیای است که مردم را گیر میاندازد، و هر قدمش بهتنهایی درست است: پیکربندی SSH را ویرایش کنید، پورت را جابهجا کنید، ورود با رمز عبور را غیرفعال کنید، یک فایروال را فعال کنید، دیمن را ریلود کنید، ترمینال را ببندید. اشتباه در هیچکدام از تغییرهای تکتک نیست. اشتباه این است که همان نشستی که در آن تایپ میکردید، تنها مدرکِ کارکردنِ هرکدام از آنها بود، و پیش از بررسی، همان را دور انداختید.
قانونی که کل این چکلیست را ایمن میکند، فقط یک جمله است: نشستِ کاری را باز نگه دارید و تغییر را از یک نشستِ دوم اثبات کنید. یک ترمینال جدید باز کنید، از نو وصل شوید، id را اجرا کنید، با sudo دسترسی را ارتقا دهید. اگر موفق شد، تغییر واقعی است و دیگر نیازی به نشست اول نیست. اگر شکست خورد، هنوز یک شل در اختیار دارید که با آن میتوانید تغییر را برگردانید. این هم شامل فایروال میشود، هم sshd، هم کلید یک کاربر جدید، و هم هر چیز دیگری که میتواند دم درْ جلویتان را بگیرد.
پیش از هر کدام از اینها، مسیر خارج از باند را برقرار کنید. در پنل BitVPS، هر ردیفِ سرور یک دکمهٔ Console دارد که یک نشستِ KVM-over-VNC روی نمایشگرِ ماشین مجازی باز میکند. این یک شل روی شبکه نیست — این صفحهنمایش و صفحهکلیدِ خودِ ماشین است — پس حتی وقتی sshd بالا نمیآید، وقتی فایروال هر بستهٔ ورودی را رد میکند، و وقتی پیکربندی شبکه اشتباه است، همچنان کار میکند. همین حالا، یکبار، روی سروری که هنوز خرابش نکردهاید بازش کنید. مطمئن شوید یک پرامپت ورود میبینید. این کار سی ثانیه طول میکشد و تفاوتِ بین یک اشتباه و یک قطعی کامل است.
یک اسنپشات هم بگیرید. اینها روی هر پلن ساعتیاند، با نگهداریِ هفتروزه، و در حدود سی ثانیه بازیابی میشوند؛ به همین خاطر «رولِ nft اشتباه را پیست کردم» را از یک عصرِ کامل بازیابی، به یک بازگشتِ ساده تبدیل میکنند. و برای تغییراتی که واقعاً مطمئن نیستید، یک کلید مرگ فقط یک خط هزینه دارد: systemd-run --on-active=10min /usr/sbin/ufw --force disable تایمری را فعال میکند که ظرف ده دقیقه فایروال را برمیگرداند، مگر اینکه خودتان لغوش کنید. اگر قوانین جدید درست کار کردند، لغوش میکنید. اگر شما را بیرون قفل کردند، ماشین بدون نیاز به حضورتان دوباره راهتان میدهد.
SSH: چهار دستورالعملی که کارِ اصلی را انجام میدهند — و فایلی که بر آنها ارجحیت دارد
کلید را روی ماشین خودتان تولید کنید، هرگز روی سرور: ssh-keygen -t ed25519 -C "laptop-2026". Ed25519 در سال 2026 گزینهٔ پیشفرضِ درست است — کلیدهای کوتاه، تأیید سریع، بدون هیچ انتخابِ پارامتری که بشود اشتباه کرد، و پشتیبانیشده توسط هر OpenSSH ساختهشده در دههٔ گذشته. RSA با 4096 بیت ناامن نیست، فقط بدون هیچ فایدهای بزرگتر و کندتر است. روی کلید خصوصی یک گذرواژه بگذارید؛ ssh-agent یعنی فقط یکبار در هر نشست آن را تایپ میکنید، و این تنها چیزی است که بین یک لپتاپِ دزدیدهشده و تکتکِ سرورهایی که دارید ایستاده است.
نیمهٔ عمومی را با ssh-copy-id بالا کپی کنید و پیش از غیرفعالکردنِ هرچیزی، مطمئن شوید میتوانید با آن وارد شوید. مجوزها اهمیت دارند و OpenSSH از قصد دربارهٔ آنها سختگیر است: ~/.ssh روی 700، authorized_keys روی 600، هر دو متعلق به همان کاربر. کلیدی که بیسروصدا کار نمیکند، تقریباً همیشه یک مشکلِ مجوز است، و sshd -e -d در پیشزمینه، همین را در یک خط به شما میگوید.
بعد، چهار دستورالعمل در /etc/ssh/sshd_config اساساً کل کار را انجام میدهند. هر چیز دیگری در آن فایل فقط سلیقه است.
| دستورالعمل | مقدارش را بگذارید | چه کاری انجام میدهد | اگر اشتباه تنظیمش کنید چه چیزی خراب میشود |
|---|---|---|---|
PasswordAuthentication | no | رمز عبور را بهطور کامل بهعنوان روش ورود حذف میکند — کل سیلِ brute-force بهجای صرفاً کند شدن، کاملاً غیرممکن میشود | اگر کلیدتان واقعاً هیچوقت نصب نشده بود، بیرون قفل میشوید. اول در یک نشستِ دوم تستش کنید |
KbdInteractiveAuthentication | no | دومین در به همان اتاق را میبندد — مسیر keyboard-interactive در PAM حتی وقتی دستورالعمل اول تنظیم شده هم میتواند رمز عبور را بپذیرد | راهاندازیهای دوعاملیِ مشروع که به PAM متکیاند را خراب میکند. فقط اگر از یکی از آنها استفاده میکنید روشن نگهش دارید |
PermitRootLogin | prohibit-password | root همچنان میتواند با کلید وارد شود (برای اتوماسیون مفید است) اما هرگز با رمز عبور | no سختگیرانهتر و مناسب است — به شرطی که کاربر sudoتان کار کند. اول همین را تأیید کنید |
PubkeyAuthentication | yes | همان روشی که نگهش میدارید. اینجا صریحبودن بهتر از پیشفرضبودن است | هیچچیز، اما ارزش نوشتن دارد تا یک ویرایشِ آینده بیسروصدا حذفش نکند |
AllowUsers / AllowGroups | کاربر یا گروه خودتان | اختیاری است، و باارزشترین خطِ اختیاری — دیمن پیش از احراز هویت هر حساب دیگری را رد میکند | یک غلط تایپی همه را رد میکند. آن را آخر از همه تنظیم کنید، و از یک نشستِ دوم |
حالا بخشی که حتی افراد باتجربه را هم گیر میاندازد. در Debian 12، Ubuntu 22.04 و هر چیز جدیدتر، /etc/ssh/sshd_config با Include /etc/ssh/sshd_config.d/*.conf شروع میشود، و OpenSSH از اولین مقداری که برای هر کلیدواژه به دست میآورد استفاده میکند. ایمیجهای کلود در آن دایرکتوری فایلهای drop-in ارسال میکنند — معمولاً یکی که PasswordAuthentication yes را تنظیم میکند — و چون Include بالای فایل نشسته، همان drop-in بر خطی که با دقت دویست خط پایینتر ویرایش کرده بودید غلبه میکند. فایل یک چیز میگوید و دیمن کار دیگری میکند.
راهحل این است که هرگز به فایل اعتماد نکنید. از خودِ دیمن بپرسید چه چیزی را resolve کرده: sshd -T | grep -Ei 'passwordauth|permitrootlogin|kbdinteractive|pubkeyauth' پیکربندیِ واقعاً اعمالشده را بعد از اجرای هر include، هر match block و هر پیشفرض چاپ میکند. همین یک دستور، بیشتر از بقیهٔ این بخش میارزد. آن را با sshd -t جفت کنید، که نحو را اعتبارسنجی میکند و در صورت اشتباه با کدِ غیرصفر خارج میشود، و فقط بعد از آن systemctl reload ssh را بزنید — یک ریلود نشستهای موجود را مختل نمیکند، پس حتی یک ریلودِ اشتباه هم شلِ فعلیِ شما را زنده نگه میدارد.
اما دربارهٔ جابهجا کردن SSH از پورت 22: این کار شاید 95% از حجمِ لاگهایتان را حذف کند، ولی صفر درصد از ریسکتان را. هر اسکنری که ارزشِ نگرانی داشته باشد، هر 65,535 پورت را پویش میکند، و آنهایی هم که این کار را نمیکنند، اصلاً قرار نبود از سدِ احراز هویتِ فقط-با-کلید عبور کنند. اگر نویز اذیتتان میکند یا باعث سکوتِ یک هشدارِ مانیتورینگ میشود، جابهجایش کنید، اما آن را بهعنوان یک کنترل امنیتی ثبت نکنید — و اگر واقعاً جابهجایش کردید، آن را زیر 1024 نگه دارید، چون فقط پردازههای ممتاز اجازهٔ بایند شدن به آنها را دارند، پس اگر دیمن یک روز از کار بیفتد، هیچ پردازهٔ غیرممتازی نمیتواند آن پورت را تصاحب کند.
فایروال: رد پیشفرض ورودیها، و نیمهای که همه فراموشش میکنند
فایروالِ روی یک سرورِ تکمنظوره فقط یک وظیفه دارد: مجموعهٔ پورتهای قابلدسترسی را برابر مجموعهٔ پورتهایی کند که میتوانید برایشان دلیل بیاورید. رد پیشفرض ورودی، اجازه به خروجی، اجازه به بازگشتِ اتصالهای established و related، و بعد فقط باز کردنِ همان دو سه چیزی که واقعاً اجرا میکنید. اگر روی Debian یا Ubuntu هستید، ufw چهار دستور است و درست کار میکند؛ اگر ترجیح میدهید یک فایل قانونِ خوانا و از آنِ خودتان داشته باشید، nftables پاسخِ مدرنِ درون-کرنل است و همان چیزی که ufw در زیرش روی آن مینویسد.
ترتیب دقیقاً در یک جا اهمیت دارد: پورتِ SSH را پیش از فعالکردنِ سیاست، اجازه بدهید. اول ufw allow 22/tcp، بعد ufw default deny incoming، بعد ufw enable. اگر برعکس انجامش بدهید، مرحلهٔ enable همان نشستی را که در آن تایپ میکنید قطع میکند — که اگر بخش قبلی را انجام داده باشید و هنوز یک کنسول داشته باشید قابلجبران است، وگرنه راهِ کلاسیکِ زودتر تمام کردنِ یک شب است.
نیمهٔ فراموششده، IPv6 است. هر سروری اینجا با یک /64 مسیریابیشده عرضه میشود، و سرویسها بهطور پیشفرض به هر دو خانواده بایند میشوند. مجموعهقانونی که فقط با iptables نوشته شده، فقط IPv4 را اداره میکند و دربارهٔ IPv6 اصلاً چیزی نمیگوید، پس همان پایگاهدادهای که با دقت فایروال کرده بودید، از طریق آن پروتکلِ دیگر همچنان از کلِ اینترنت قابلدسترسی میماند. ufw وقتی IPV6=yes در /etc/default/ufw تنظیم شده باشد (که پیشفرضِ نسخههای فعلی است — بررسی کنید، فرض نکنید) هر دو را پوشش میدهد، و nftables این مشکل را بهشکلی ساختاری دور میزند: یک مجموعهقانونِ table inet هر دو خانواده را با یک مجموعه قانون پوشش میدهد. اگر فقط یک خط پیکربندیِ فایروال را دستی مینویسید، همین یکی را بنویسید.
بعد Docker میرسد، که آنقدرها هم فایروال شما را دور نمیزند که از زیرش وارد میشود. انتشار یک پورت با -p 5432:5432 قوانین DNAT را داخل جدولِ nat وارد میکند، و آنها پیش از زنجیرههای filter که ufw مدیریت میکند ارزیابی میشوند. نتیجه، یک کانتینر قابلدسترسی از اینترنت است درحالیکه ufw status پورت را بسته نشان میدهد، و هر سال همین موضوع کسانی را که یک پایگاهداده در یک کانتینر پشتِ چیزی که فکر میکردند یک سیاستِ deny-all است اجرا میکنند غافلگیر میکند. در عوض به loopback منتشرش کنید — -p 127.0.0.1:5432:5432 — یا کانتینر را روی یک شبکهٔ داخلی بگذارید و از یک کانتینر دیگر با نام به آن برسید.
| چیزی که فکر میکنید | چیزی که واقعاً درست است | چطور با یک دستور بررسی کنید |
|---|---|---|
| «فایروال هر چیزی را که اجازه ندادهام رد میکند» | فقط برای IPv4 درست است، اگر فقط قوانینِ IPv4 نوشته باشید | ip6tables -L -n یا nft list ruleset |
| «پایگاهداده در معرض نیست، چون در یک کانتینر است» | یک پورتِ منتشرشده، صرفنظر از سیاستِ filter، قابلدسترسی است | docker ps --format '{{.Ports}}' |
| «هیچچیز روی آن پورت گوش نمیدهد» | یک چیزی ریاستارت شده و بعد از آخرین نگاهتان دوباره بایند شده | ss -tulpn |
| «پورت بسته است، اسکنم همین را میگوید» | یک اسکن از خودِ ماشین، loopback را تست میکند، نه اینترنت را | از یک میزبانِ دوم، یا از لپتاپتان اسکن کنید |
واقعاً چه چیزی گوش میدهد — دستوری که به حدسزدن پایان میدهد
یک دستور به مفیدترین سؤالِ ممکن دربارهٔ میزانِ در معرضبودنِ یک سرور جواب میدهد: ss -tulpn. این دستور هر ساکتِ TCP و UDP در حال گوشدادن را همراه با پردازهای که مالکش است فهرست میکند، و ستونی که اهمیت دارد آدرسِ محلی است. 127.0.0.1:5432 یعنی فقط همین ماشین میتواند وصل شود. 0.0.0.0:5432 یعنی هر آدرسی که به این ماشین برسد میتواند وصل شود، فقط مشروط به فایروال. [::]:5432 همین معنا را روی IPv6 میدهد، و همان خطی است که بیشتر مردم از رویش رد میشوند.
مقصرهای تکراری همیشه همان مشتِ مشخصاند. Redis، که در بیشترِ تاریخش بهطور گسترده بایند میشد و بدون احراز هویت عرضه میشد. PostgreSQL و MySQL، وقتی یک فایل پیکربندی طوری ویرایش میشود که اتصالها را «از سمتِ اپلیکیشن» بپذیرد، و بعد اپلیکیشن جابهجا میشود. MongoDB و Elasticsearch، که این دو با هم یک دهه تیترِ افشای داده بهخاطر دقیقاً همین دلیل تولید کردهاند. Memcached، که شنوندهٔ UDP آن به یکی از محبوبترین تقویتکنندههای پهنایباند تبدیل شد. Prometheus node_exporter، که با خوشحالی جزئیاتِ داخلیِ میزبانتان را برای هرکس که بپرسد روایت میکند. Docker API روی پورت 2375، که همان اجرای کدِ ریموت است با لباسِ یک رابطِ REST. و یک سرورِ توسعه که یک نفر «فقط برای یک دقیقه» در یک پنجرهٔ tmux یازده ماه پیش راهش انداخته بود.
قانون ساده است و نیازی به قضاوت ندارد: اگر یک سرویس لازم نیست از اینترنت قابلدسترسی باشد، آن را به loopback بایند کنید. هر کاری که خودتان شخصاً لازم دارید با آن انجام دهید همچنان کار میکند — ssh -L 5432:127.0.0.1:5432 user@server یک پورتِ محلی به شما میدهد که روی همان اتصالی که از قبل به آن اعتماد دارید تانل میزند، و اگر چند ماشین به دسترسی نیاز دارند، یک رابطِ WireGuard خصوصی محدودهٔ آدرسی به آنها میدهد که هرگز به اینترنت عمومی نمیرسد. هیچکدام هزینهای ندارد، و هر دو سرویس را بهطور کامل از سطح حمله حذف میکنند، بهجای اینکه فقط از آن دفاع کنند.
از بیرون تأیید کنید، نه از داخل. پورتی که ماشینِ خودتان میگوید filtered است، فقط یک ادعا دربارهٔ پشتهٔ loopback شماست؛ پورتی که یک ماشینِ دوم نمیتواند به آن برسد، مدرک است. از لپتاپتان، یا از یک سرور دیگر وصل شوید، و هم پورتهایی را که انتظار دارید باز باشند و هم یکی را که انتظار دارید بسته باشد بررسی کنید — تستی که فقط میتواند پاس شود، اصلاً تست نیست.
| سرویس | پیشفرضِ رایج | یک نمونهٔ در معرض چه چیزی را لو میدهد | راهحل |
|---|---|---|---|
| Redis | بایندِ گسترده، از نظر تاریخی بدون رمز عبور | خواندن/نوشتنِ کامل، و یک مسیرِ شناختهشده برای نوشتنِ فایل بهعنوان کاربر redis | بایند به loopback؛ requirepass را تنظیم کنید؛ protected-mode را روشن نگه دارید |
| PostgreSQL / MySQL | loopback، تا وقتی یک نفر ویرایشش کند | کل دیتاستتان، اگر یک اعتبارنامه ضعیف یا تکراری باشد | بایند به loopback؛ از طریق SSH یا WireGuard به آن برسید |
| Docker API | خاموش — مگر اینکه یک آموزش روشنش کرده باشد | اجرای کدی همارزِ root روی میزبان | هرگز افشایش نکنید؛ در عوض از SSH context استفاده کنید |
| node_exporter / متریکها | همهٔ اینترفیسها، پورت 9100 | نام میزبانها، مسیرهای mount، نسخهها، پردازههای در حال اجرا | بایند به loopback؛ از طریق اینترفیسِ خصوصی scrape کنید |
| ارسال ایمیل، پنلهای مدیریتِ وب | همهٔ اینترفیسها، از سرِ ضرورت | سطحی برای حدسزدنِ رمز عبور که کلیدها نمیتوانند از آن محافظت کنند | اینجا همانجایی است که محدودسازیِ نرخ واقعاً بهکار میآید |
Fail2ban و CrowdSec: وقتی رمزهای عبور دیگر نیستند، اینها چه ارزشی دارند
این همان موردی است که بیشترِ چکلیستها نزدیک بالای فهرست میگذارندش و بیشترِ اپراتورها بیش از حد ارزشش میدهند، پس ارزش دارد دقیق باشیم. با PasswordAuthentication no، یک حدسِ رمز عبور نمیتواند موفق شود — نه بهندرت، نه بهسختی، بلکه اصلاً. بنکردنِ یک آدرس بعد از پنج شکست، یک احرازِ هویتِ غیرممکن را غیرممکنتر نمیکند. چیزی که fail2ban در لایهٔ SSH برایتان میخرد، یک auth.log ساکتتر، کمی CPU کمتر صرفشده برای برقراریِ اتصال، و نمودارهای مانیتورینگی است که خواندنشان راحتتر است. اینها مزیتهای واقعیاند. اینها بهداشتِ عملیاتیاند، نه یک کنترل امنیتی، و گذاشتنشان زیرِ عنوانِ اشتباه، همان چیزی است که باعث میشود آدمها با یک فایلِ لاگِ سختسازیشده و یک اپلیکیشنِ patchنشده تمام کنند.
جایی که این دسته از ابزارها واقعاً جایگاهشان را ثابت میکنند، هر لایهای است که رمز عبور هنوز در آن وجود دارد: یک فرمِ ورودِ یک اپلیکیشن وب، یک پورتِ ارسال ایمیل، یک پنلِ مدیریتِ Nextcloud یا WordPress، یک سرورِ IMAP. اینها را نمیشود به احراز هویت با کلید عمومی تغییر داد، پس حدسزدن همچنان ممکن میماند و محدودسازیِ نرخ همان دفاعِ واقعی است. CrowdSec یک فیدِ اعتبارِ مشترک هم رویش اضافه میکند، که در لایهٔ HTTP واقعاً مفید است — از آدرسهایی که دیگران قبلاً بدرفتاریشان را دیدهاند سود میبرید — و در لایهٔ SSH، وقتی کلیدها اجباریاند، تقریباً بیربط است.
دو تلهی خودزنی ارزش نامبردن دارند. اولی، بنکردنِ خودتان است، معمولاً درست وقتی ساعت سه صبح دارید چیزی را تست میکنید؛ ignoreip را طوری تنظیم کنید که محدودهٔ خودتان را هم شامل شود، و بهخاطر داشته باشید که یک آدرسِ مشترک یا carrier-grade-NAT یعنی بنکردنِ «یک مهاجم» میتواند چند صد نفرِ بیربط را هم بن کند. دومی این است که ابزار باید بتواند لاگهایی را که دارد فیلتر میکند بخواند: روی توزیعهایی که فقط در ژورنالِ systemd لاگ میگیرند، بکاندِ پیشفرضِ فایل بیسروصدا فایلی را تماشا میکند که دیگر چیزی دریافت نمیکند، و jail همانجا نشسته، سالم بهنظر میرسد و هیچ کاری نمیکند.
توصیهٔ صادقانه: اگر نویز اذیتتان میکند نصبش کنید، برای لایهٔ اپلیکیشنتان — جایی که واقعاً اهمیت دارد — پیکربندیاش کنید، و دوبار حسابش نکنید. اگر همان سکوت را بدون چیزی برای نگهداری میخواهید، MaxStartups و LoginGraceTime در دیمنِ SSH بههمراه احراز هویتِ فقط-با-کلید، بیشترِ راه را میروند و نمیتوانند شما را از ماشینِ خودتان بیرون قفل کنند.
پچکردن: کنترلِ خستهکنندهای که بیشترِ نفوذهای واقعی را جلو میگیرد
هر گزارشِ حادثهای را که دربارهٔ یک سرورِ کوچکِ رو-به-اینترنت باشد بخوانید، همان الگو تکرار میشود: آسیبپذیری یک پچ داشته، آن پچ هفتهها یا ماهها در دسترس بوده، و هیچکس اعمالش نکرده. فهرستِ آسیبپذیریهای شناختهشدهٔ زیرِ بهرهبرداریِ CISA همان خواندنیِ مفید در اینجاست، دقیقاً چون فهرستی از شدتِ نظری نیست — فهرستی از چیزهایی است که همین الان در دنیای واقعی مورد بهرهبرداریاند، و اکثرش را مسائلی با راهحلِ منتشرشده تشکیل میدهند. در نتیجه، بهروزرسانیهای خودکارِ امنیتی، پربازدهترین پانزده ثانیهٔ کل این صفحهاند.
روی Debian و Ubuntu، unattended-upgrades را نصب کنید، با dpkg-reconfigure -plow unattended-upgrades فعالش کنید، و مطمئن شوید که مبدأِ امنیتیِ انتخابشده در /etc/apt/apt.conf.d/50unattended-upgrades همان است که باید باشد. با unattended-upgrades --dry-run --debug تأیید کنید که واقعاً کاری انجام میدهد؛ این دستور دقیقاً چاپ میکند کدام بستهها را میگیرد و چرا — صفحهٔ ویکیِ Debian همان مرجع است. روی Rocky و Alma، dnf-automatic بههمراه apply_updates = yes و تایمرِ فعالشده، همان کار را انجام میدهد. محدودکردن به مخزنِ امنیتی، عمدی است: این همان پیکربندیای است که تقریباً هرگز چیزی را خراب نمیکند، و تنها نوع اتوماسیونی است که مردم روشن نگهش میدارند.
بعد دربارهٔ ریبوتها صریحاً تصمیم بگیرید، چون اینجا همانجایی است که معمولاً کنترل نشت میکند. نصبِ یک بستهٔ کرنلِ جدید، هستهای را که در حال اجراست جایگزین نمیکند، و جایگزینکردنِ یک کتابخانهٔ اشتراکی، دیمنهایی را که نسخهٔ قدیمی را از قبل در حافظه نگاشت کردهاند ریاستارت نمیکند. needrestart روی Debian و Ubuntu دقیقاً فهرست میکند کدام سرویسها هنوز کتابخانههای حذفشده را نگه داشتهاند؛ یک بهروزرسانیِ هسته به یک ریبوت نیاز دارد — همین و بس — و live-patching یک ویژگیِ پولی است که به یک نمونهٔ $8.50 نمیرسد. یا Unattended-Upgrade::Automatic-Reboot را با یک Automatic-Reboot-Timeِ آرام تنظیم کنید، یا یک ریبوتِ ماهانه را در تقویمتان بگذارید و مثل نگهداری با آن رفتار کنید، نه مثل یک حادثه.
در آخر، بدانید بهروزرسانیهای خودکار چه چیزی را پوشش نمیدهند، چون همان شکاف است که مردم در آن گیر میافتند. هر چیزی که بیرون از مدیرِ بسته نصب شده باشد، برایش نامرئی است: یک باینریِ کشیدهشده از یک ریلیزِ GitHub، یک وابستگیِ runtime زبان از pip، npm یا cargo، یک قالب یا افزونه داخلِ یک اپلیکیشنِ وب، و مهمتر از همه، ایمیجهای کانتینر. میزبانی که میگوید صفر بهروزرسانیِ در انتظار دارد، میتواند در حال اجرای یک ایمیجِ پایهای باشد که دو سال پیش ساخته شده، و هیچچیزی روی آن ماشین این را به شما نمیگوید. اگر کانتینر اجرا میکنید، rebuild و re-pull کردن بهصورت زمانبندیشده بخشی از پچکردن است، نه یک فعالیتِ جدا.
| سیستم | مکانیزم | چه چیزی را پوشش میدهد | چه چیزی را بیسروصدا از قلم میاندازد |
|---|---|---|---|
| Debian / Ubuntu | unattended-upgrades، فقط مبدأ امنیتی | بستههای توزیع، هرشب اعمالشده | هسته و کتابخانههای در حال اجرا تا زمانِ ریبوت |
| Rocky / Alma | dnf-automatic با apply_updates = yes | همان کار، از طریق یک تایمرِ systemd | همان شکافِ ریبوت؛ dnf needs-restarting را بررسی کنید |
| کانتینرها | بهطور پیشفرض، هیچچیز | هیچچیز | همهچیز — ایمیج در زمانِ build منجمد شده |
| وابستگیهای زبان | بهطور پیشفرض، هیچچیز | هیچچیز | سطحِ حملهٔ واقعیِ اپلیکیشنتان |
کاربرها، sudo، و درهایی که بیسروصدا دوباره باز میشوند
یک کاربرِ عادی بسازید، آن را به sudo یا wheel اضافه کنید، کلیدتان را آنجا نصب کنید، و دیگر بهعنوان root وارد نشوید. دلیلش این نیست که root-با-کلید ذاتاً خطرناک است — این یک حسابِ تکعاملی با نامی همگانیشناختهشده و بدون هیچ سابقهای از اینکه کدام انسان از آن استفاده کرده است. یک حسابِ نامدار بههمراه sudo، یک ردِ قابلردیابی تولید میکند، اجازه میدهد بیش از یک اپراتور بدون بهاشتراکگذاشتنِ یک اعتبارنامه کار کنند، و پرسشِ نهاییِ «کی این را اجرا کرد» را قابلجواب میکند. با اینحال، به آن NOPASSWD برای همهچیز ندهید و بعد همان حساب را برای یک سرویسِ وب دوباره استفاده نکنید.
سرویسها هم نباید بهعنوان root اجرا شوند، و systemd نسخهٔ ارزانِ آن را تقریباً رایگان میکند. یک drop-in که با systemctl edit <unit> ساخته شده و شاملِ NoNewPrivileges=yes، ProtectSystem=strict، ProtectHome=yes و PrivateTmp=yes است، یک دیمنِ بهخطرافتاده را در یک دیدِ فایلسیستمِ فقطخواندنی بدون هیچ مسیری برای ارتقای دسترسی محصور میکند — چهار خط، که در مستنداتِ systemd.exec توضیح داده شدهاند، و بسیاری از آسیبپذیریهای اپلیکیشن را از «root روی میزبان» به «یک خطا در یک لاگ» تبدیل میکنند. systemd-analyze security به هر unit روی ماشین امتیاز میدهد و میگوید کدامها ارزشِ آن پنج دقیقه را دارند.
authorized_keys را صادق نگه دارید. یک کلید برای هر دستگاه، هرکدام با یک کامنتِ نامبرندهٔ همان دستگاه، و هر چیزی را که نمیتوانید شناسایی کنید حذف کنید. بعد از هر بار اجرای یک ابزارِ provisioning، دوباره فایل را بخوانید، چون cloud-init، پایپلاینهای imaging و پنلهای کنترل، همه ورودیهای خودشان را اضافه میکنند، و کلیدی که خودتان اضافه نکردهاید، کلیدی است که نمیتوانید ابطالش کنید. همین موضوع دربارهٔ حسابها هم صدق میکند: یک کاربرِ پیشفرض که یک ایمیج از خودش بهجا گذاشته، با رمزی که یک اسکریپت تنظیم کرده و از آنموقع هیچوقت استفاده نشده، یک راهِ ورود است که شما تحت نظرش ندارید.
ظریفترین موردِ این فهرست، agent forwarding است. ForwardAgent yes به یک پردازه روی میزبانِ ریموت اجازه میدهد تا زمانی که وصل هستید از کلیدِ خصوصیِ محلیِ شما استفاده کند — این دقیقاً همان کاری است که برایش طراحی شده، و همین دلیلِ آن است که root روی ماشینی که کاملاً کنترلش نمیکنید میتواند کلیدِ شما را قرض بگیرد تا به هر سرورِ دیگری که به آن اعتماد دارد برسد. در عوض از ProxyJump (ssh -J bastion target) استفاده کنید: همان قابلیتِ دسترسی را با تانلزدنِ اتصال به شما میدهد، و کلیدِ شما هرگز روی میزبانِ واسط قابلاستفاده نمیشود. اگر واقعاً باید یک agent را forward کنید، این کار را بهازای هر میزبان در ~/.ssh/config انجام دهید، هرگز بهطور سراسری.
پشتیبانگیری: کنترلی که یک روزِ بد را به یک بعدازظهر تبدیل میکند
همهٔ آنچه بالاتر گفته شد، احتمالِ یک روزِ بد را کم میکند. پشتیبانگیری تعیین میکند آن روزِ بد چقدر هزینه دارد، و روی ارائهدهندهای که عمداً نمیتواند هویتِ شما را تشخیص دهد، این موضوع بیش از حدِ معمول اهمیت دارد: هیچ مسیرِ پشتیبانیای وجود ندارد که ماشینِ شما را از روی کپیِ یک نفرِ دیگر بازسازی کند، هیچ فرایندِ بازیابیِ حسابی که بیسروصدا یک بازیابی را تحویلتان بدهد، و هیچ سابقهٔ صورتحسابی که یک شمارهتلفن به آن وصل باشد. آنچه دارید، همان چیزی است که خودتان گرفتهاید.
شکلی که واقعاً جواب میدهد، هیجانانگیز نیست. restic یا borg، رمزنگاریکردن روی خودِ سرور تا داده پیش از عبور از شبکه از قبل غیرقابلخواندن باشد، و pushکردنش به یک ماشینِ دوم — و ترجیحاً یک حوزهٔ قضاییِ دوم، که وقتی همان پنل ایسلند، هلند، رومانی و سوئیس را پیشنهاد میدهد، فقط یک تیک زدن است، نه یک پروژه. اسنپشاتهای ساعتیای که با هر پلن میآیند، یک خطِ دفاعیِ اول واقعاً خوباند و حالتِ «یک ساعت پیش پیکربندی را خراب کردم» را کامل پوشش میدهند. اما یک فایلی را که پنج هفته پیش حذف کردهاید پوشش نمیدهند، و روی همان زیرساختی زندگی میکنند که از آن محافظت میکنند. اسنپشات و پشتیبانگیری مسئلههای متفاوتی را حل میکنند؛ هر دو را انجام دهید.
مخزن را append-only کنید. همین جزئیات است که یک نسخهٔ پشتیبانِ واقعی را از یک کپیِ ساده جدا میکند: یک سرورِ بهخطرافتاده، اعتبارنامهٔ مقصدِ پشتیبانِ خودش را هم در اختیار دارد، و به هر چیزی که بتواند حذف کند، گفته خواهد شد حذف کند. هم borg serve --append-only و هم rest-server --append-only از restic، این را در سمتِ گیرنده اعمال میکنند، پس ماشین میتواند اسنپشاتهای جدید بنویسد ولی نمیتواند قدیمیها را حذف کند. هرسکردن را از یکجای دیگر، طبقِ یک زمانبندی، با اعتبارنامهای که سرور هرگز ندیده انجام دهید.
دو چیزِ آخر، که هر دو را مردم رد میکنند. گذرواژهٔ مخزن نباید فقط روی همان ماشینی زندگی کند که ازش محافظت میکند — آن را یادداشت کنید، در یک پسورد منیجر در جای دیگری بگذارید، با آن مثل همان seed phraseای که عملاً هست رفتار کنید. و یکبار بازیابیاش کنید. یک نسخهٔ پشتیبان که هرگز بازیابیاش نکردهاید، فقط یک فرضیه است؛ راهِ معمولِ فهمیدنِ اینکه یک قانونِ exclude بیسروصدا dump پایگاهداده را نادیده گرفته، معمولاً درست وسطِ همان بازیابیای است که اجازه ندارید اشتباه از آب دربیاید. یک نمونهٔ Starter راه بیندازید، رویش بازیابی کنید، داده را بررسی کنید، بعد نابودش کنید. این کار $8.50 و یک ساعت هزینه دارد، و تنها راهی است که بقیهٔ این صفحه بتواند یک تمرین باشد، نه یک تلاشِ اول.
سختسازی در برابر چه چیزی از شما محافظت نمیکند
این از دیسک در برابر کسی که خودِ دیسک را در دست دارد محافظت نمیکند. هر کنترلی در این صفحه فرض میکند سیستمعامل در حال اجراست و قوانینِ خودش را اعمال میکند؛ هیچکدامشان روی ماشینی که خاموش است و از فضای ذخیرهسازیاش ایمیج گرفته شده اعمال نمیشود. آن یک مسئلهٔ متفاوت با یک پاسخِ متفاوت است — ببینید رمزنگاری کاملِ دیسک، باز کردنِ قفل از راه دور، و اینکه یک توقیف واقعاً چه چیزی را بازیابی میکند — و خلاصهٔ صادقانه این است که سختسازی و رمزنگاری در برابر تهدیدهای کاملاً جدا از هم دفاع میکنند. انجامِ یکی، بخشی از دیگری را هم انجام نمیدهد.
این اپلیکیشنی را که اجرا میکنید درست نمیکند. یک سرور میتواند از هر بررسیای در این صفحه سربلند بیرون بیاید و همچنان بهخاطر یک فرومِ قدیمی، یک اندپوینتِ مدیریتی بدون احراز هویت، یک تمپلیتانجینی که ورودیِ کاربر را ارزیابی میکند، یا یک توکنِ کامیتشده در یک مخزنِ عمومی از دست برود. سختسازیِ میزبان، شعاعِ انفجارِ یک نفوذِ اپلیکیشنی را کم میکند — که دقیقاً همان کاری است که محصورسازیِ systemd در بالا برایش هست — اما کدِ شما را ممیزی نمیکند، و رایجترین راهی که یک سرورِ کوچک از دست میرود، همچنان همان چیزی است که خودِ اپراتور نصبش کرده.
این تغییری در اینکه چه کسی میداند شما این ماشین را اجاره کردهاید ایجاد نمیکند. یک جعبهٔ کاملاً سختسازیشده که از یک حسابِ صرافیِ هویتاحرازشده پرداخت شده و با یک ایمیلِ شخصی سفارش داده شده، یک جعبهٔ کاملاً سختسازیشده است که اسمِ شما رویش نوشته شده. لایهٔ امنیت و لایهٔ هویت، مستقل از هماند، و دومی در لحظهٔ پرداخت تعیین میشود، نه در شل — اینکه میزبانیِ پرداختشده با کریپتو واقعاً تا چه حد قابلردیابی است پوشش میدهد این پیوندها واقعاً کجا شکل میگیرند.
و هیچ کاری هم دربارهٔ تحلیلِ ترافیک انجام نمیدهد. ناظری که در جایگاهی برای دیدنِ اتصالهای شما قرار گرفته، شکلِ آنها را میفهمد — زمانبندی، حجم، چه کسی با چه کسی حرف میزند — صرفنظر از اینکه سرِ دو طرفِ اتصال چقدر خوب پیکربندی شده باشند. رمزنگاری محتوا را پنهان میکند، نه خودِ گفتوگو را، و هیچ مقدار تنظیمِ sshd این را عوض نمیکند.
چکلیست، به همان ترتیبی که باید اجرایش کنید
هشت قدم. چهارتای اول همان پانزده دقیقهٔ عنوانِ این صفحهاند و کل دستههایی از ریسک را حذف میکنند؛ چهارتای آخر بیشتر طول میکشند و همان چیزیاند که یک سرورِ قابلدفاع را از سروری که فقط پیکربندیاش کردهاید جدا میکنند. هر ردیف شاملِ این است که از کجا بفهمید کار کرده، چون یک قدمِ سختسازی که تأییدش نکردهاید، قدمی است که برنداشتهاید.
| # | قدم | از کجا بفهمید کار کرده |
|---|---|---|
| 1 | کنسولِ پنل را باز کنید، یک پرامپت ورود را تأیید کنید، یک اسنپشات بگیرید | روی سروری که خراب نبود، پرامپت را دیدید |
| 2 | یک کاربر non-root بسازید، کلید ed25519 خودتان را نصب کنید، sudo را تست کنید | یک ترمینالِ دوم با آن کاربر وارد میشود و دسترسی را ارتقا میدهد |
| 3 | احراز هویت با رمز عبور و keyboard-interactive را غیرفعال کنید | sshd -T مقدارِ passwordauthentication no را گزارش میدهد |
| 4 | فایروالِ رد پیشفرضِ ورودی که IPv4 و IPv6 را پوشش میدهد | nft list ruleset هر دو خانواده را نشان میدهد؛ یک اسکن از میزبانی دیگر هم تأییدش میکند |
| 5 | ساکتهای در حال گوشدادن را ممیزی کنید، سرویسهای داخلی را دوباره به loopback بایند کنید | ss -tulpn هیچ بایندِ wildcardِ بیتوجیه نشان نمیدهد |
| 6 | بهروزرسانیهای خودکارِ امنیتی و یک سیاستِ ریبوت را فعال کنید | unattended-upgrades --dry-run --debug مبدأ امنیتی را انتخاب میکند |
| 7 | سرویسها را با systemd محصور کنید، authorized_keys را مرتب کنید | امتیازِ systemd-analyze security بهتر میشود؛ هر کلید یک اسم دارد |
| 8 | پشتیبانِ رمزنگاریشده و append-only در جایی دیگر، که یکبار بازیابی شده | در یک نمونهٔ یکبارمصرف بازیابی کردید و داده آنجا بود |
هیچکدام از اینها عجیب نیست، و نکته دقیقاً همین است. کنترلهایی که روی یک سرورِ کوچک اهمیت دارند، همانهاییاند که یک دسته از مشکل را حذف میکنند، نه اینکه فقط مدیریتش کنند: نه رمز عبوری، نه شنوندهٔ غیرمنتظرهای، نه بستهٔ patchنشدهای، نه یک نسخهٔ تکیِ داده. هر چیزِ دیگری فقط سلیقه است، و سلیقه وقتی ارزان است که آن چهار تغییرِ باربر سرِ جایشان باشند و تأیید شده باشند.
اگر همین حالا دارید ماشین را provision میکنید، نه اینکه فقط جلوتر را میخوانید، این توالی بهراحتی در همان نشستِ اول جا میشود: یک نمونه دیپلوی کنید، یکبار کنسول را باز کنید تا کارکردنش را ثابت کنید، و پیش از نصبِ هر چیزی، آن هشت قدم را بهترتیب اجرا کنید. سروری که پیش از داشتنِ هر سرویسی روی آن سختسازی شده، هیچ میراثی برای دور زدن ندارد — که این تنها مزیتِ یک جعبهٔ تازه است، و حدود یک روز دوام میآورد.