Có hai thời điểm người ta thực sự hiểu cách chống DDoS hoạt động. Thời điểm đầu diễn ra bình thản, lúc mua hàng, khi đọc một danh sách tính năng ghi "đã bao gồm chống DDoS" rồi lặng lẽ mặc định rằng câu đó bao trùm tất cả. Thời điểm thứ hai là lúc ba giờ sáng, khi trang web đã sập, các biểu đồ trông sai một cách vô lý, và lớp bảo vệ "đã bao gồm" đó — đúng như thiết kế của nó — chẳng làm gì cả.
Cả hai thời điểm đều xoay quanh cùng một sản phẩm và cùng một sự thật: nhà cung cấp hosting lọc được các cuộc tấn công đến dưới dạng khối lượng thô, vì họ sở hữu đường truyền mà các gói tin đó đi qua, còn bạn thì không. Họ không thể lọc được các cuộc tấn công đến dưới dạng request trông bình thường, vì dưới góc nhìn của mạng, đó đúng là những request bình thường. Ranh giới đó — giữa đợt lũ mà host của bạn hấp thụ và đợt lũ mà chính bạn phải tự mình sống sót qua — là toàn bộ chủ đề của bài này. Mọi thứ bên dưới đây là để xác định bạn đang đứng ở phía nào của ranh giới đó, và phải làm gì ở mỗi phía.
Hai kiểu tấn công khác nhau chung một cái tên
"DDoS" là một từ dùng chung cho hai vấn đề gần như chẳng có điểm nào giống nhau ngoài hậu quả. Chúng bị chặn ở hai nơi khác nhau, bởi hai nhóm người khác nhau, bằng hai bộ công cụ khác nhau, và việc nhầm lẫn giữa chúng chính là lý do phần lớn công sức chống đỡ lại đổ nhầm vào tầng.
| Khối lượng lớn — tầng 3 và 4 | Ứng dụng — tầng 7 | |
|---|---|---|
| Thứ ập đến | SYN flood, khuếch đại UDP qua các reflector DNS, NTP hay memcached để ngỏ, ACK flood, các gói tin rác thuần túy | Các request HTTP trông bình thường: GET flood, POST flood, slow-loris, chuỗi query phá cache |
| Đo bằng | Gigabit và hàng triệu gói tin mỗi giây | Request mỗi giây — thường chỉ vài nghìn |
| Băng thông cần để gây hại cho bạn | Cực lớn. Đây là một cuộc thi về năng lực | Gần như không cần gì. Một chiếc laptop cũng đủ nếu endpoint đủ đắt đỏ |
| Nơi phải chặn nó lại | Ở thượng nguồn, bởi nhà cung cấp của bạn. Vì khi gói tin chạm tới cổng của bạn thì thiệt hại đã xảy ra rồi | Trên server của bạn, bởi chính bạn, hoặc trên một proxy do bạn kiểm soát đặt trước nó |
| Trông ra sao trên máy | Interface bão hòa, bộ đếm gói tin phi lý, CPU có thể vẫn rảnh | Băng thông khiêm tốn, nhưng mọi worker đều bận, load tăng dần, hàng đợi database phình to |
| Ai xử lý nó | Scrubbing của host bạn, tự động, thường chỉ trong vài giây | Cấu hình của bạn — giới hạn tốc độ, cache, giới hạn kết nối |
Hãy đọc lại hai hàng cuối, vì chúng chứa đựng điểm mấu chốt thực tế. Nếu interface đã bão hòa, có gõ gì vào server cũng vô ích: gói tin đã chiếm hết cổng rồi, và bên duy nhất có thể loại bỏ chúng là bên sở hữu router ở thượng nguồn. Nếu interface vẫn im ắng mà trang web vẫn sập, điều ngược lại mới đúng — host của bạn chẳng thấy gì bất thường vì, ở tầng của họ, quả thực không có gì bất thường, và việc khắc phục hoàn toàn là việc của bạn.

"Đã bao gồm chống DDoS" thực sự mua được những gì
Bảo vệ ở tầng mạng là có thật, có giá trị, và hầu như luôn bị hiểu sai. Khi một nhà cung cấp quảng cáo lọc L3/L4, họ muốn nói rằng mạng của họ theo dõi lưu lượng hướng đến địa chỉ của bạn, và khi phát hiện một đợt lũ, lưu lượng đó được chuyển hướng qua phần cứng scrubbing để loại bỏ phần độc hại và chuyển tiếp phần trông hợp lệ. Việc này diễn ra mà không cần mở ticket hỗ trợ, và thường bạn cũng chẳng nhận ra gì ngoài một cú chớp nhoáng.
Một câu đó thôi đã gánh rất nhiều ý nghĩa, nên đáng để bóc tách xem nó bao gồm gì và không bao gồm gì:
- Nó chặn được các cuộc tấn công mà một mình bạn không thể chống đỡ. Một đợt lũ khuếch đại 200 Gbps nhắm vào một server có cổng 1 Gbps không phải là vấn đề cấu hình. Đó là bài toán số học đơn thuần. Scrubbing ở thượng nguồn là câu trả lời duy nhất tồn tại.
- Nó không biết gì về trạng thái ứng dụng của bạn. Bộ lọc không biết URL nào của bạn tốn kém, khách nào đã đăng nhập, hay một request tới endpoint tìm kiếm của bạn tốn gấp bốn trăm lần một request tới logo.
- Nó phản ứng theo một ngưỡng, không theo mức độ bạn đang khổ sở. Việc phát hiện được kích hoạt dựa trên khối lượng lưu lượng. Một cuộc tấn công không bao giờ vượt ngưỡng đó sẽ không bao giờ kích hoạt nó, dù nó đã đánh sập trang của bạn triệt để đến đâu.
- Trong trường hợp cực đoan, nó có thể null-route trong chốc lát. Mạng nào cũng có một giới hạn trên. Nếu một cuộc tấn công đe dọa hạ tầng dùng chung, địa chỉ của bạn có thể bị ngắt trong một khoảng thời gian — đây là thông lệ chuẩn, áp dụng ở khắp mọi nơi, và đáng để biết trước khi nó xảy ra hơn là biết trong lúc nó đang xảy ra.
Tóm gọn trong một dòng: host của bạn bảo vệ mạng của họ, và bạn hưởng lợi từ đó. Nó không bảo vệ ứng dụng của bạn, và cũng không có cách nào làm được điều đó. Tầng 7 không phải một gói nâng cấp bị giữ lại khỏi tay bạn — đó là một tầng mà nhà cung cấp của bạn không thể nhìn vào nếu không giải mã TLS của bạn, và với bất kỳ ai hosting offshore, đó là một sự đánh đổi có cái giá riêng rất nghiêm trọng.
Trước tiên, hãy xác định đây có thực sự là một cuộc tấn công không
Một tỷ lệ đáng kể các sự cố tưởng là DDoS thực ra là một thứ khác khoác lên chiếc áo đó, và cách khắc phục không thể dùng thay cho nhau. Trước khi giới hạn tốc độ bất cứ thứ gì, hãy dành hai phút loại trừ những kẻ mạo danh — chẩn đoán sai ở bước này tốn của bạn cả tiếng đồng hồ, và đôi khi là cả những người dùng thật của bạn.
- Bạn nổi tiếng lên. Một liên kết trên một trang tổng hợp lớn tạo ra hình dạng lưu lượng giống hệt một đợt lũ tầng 7, chỉ khác là referrer đều có thật và các request là cho những trang một con người thực sự muốn xem. Đây là vấn đề về năng lực với một nguyên nhân đáng mừng; giới hạn tốc độ nó chẳng khác nào tự làm hại chính mình.
- Một crawler đã mất lịch sự. Các scraper hung hãn và bot huấn luyện AI có thể dễ dàng vượt quá sức một server nhỏ. User-agent thường tự khai ra thủ phạm, và cách khắc phục là
robots.txtcộng với một giới hạn nhắm đúng đối tượng, chứ không phải một giới hạn áp dụng chung. - Bạn đã làm hỏng thứ gì đó. Một lần deploy vô tình tắt cache, một cron job chạy mất kiểm soát, một database bị mất index — tất cả đều biểu hiện giống hệt "load tăng đột ngột, không rõ nguyên nhân". Nếu thời điểm trùng khớp với một thay đổi bạn vừa thực hiện, hãy tin vào thay đổi đó.
- Chính hệ thống giám sát của bạn là đợt lũ. Hiếm gặp, đáng xấu hổ, và phổ biến hơn nhiều so với những gì người ta thừa nhận. Một vòng lặp health-check thử lại liên tục mà không có backoff có thể tạo ra một tốc độ request thực sự ấn tượng.
Câu hỏi để phân biệt rất đơn giản: lưu lượng này có đang muốn thứ gì đó không? Tải thật — kể cả tải thật trông có vẻ thù địch — luôn có một hình dạng nhất định. Nó chạm vào những trang thực sự tồn tại, đi theo các liên kết, tải các asset, và đến từ một dải mạng có vẻ hợp lý. Một cuộc tấn công thường chẳng buồn làm điều đó.
Đọc hiểu cuộc tấn công ngay trên server
Bạn không cần một dashboard để phân loại chuyện gì đang xảy ra. Bốn lệnh, chạy theo đúng thứ tự, sẽ cho bạn biết mình đang chống đỡ ở tầng nào trong chưa đầy một phút — và biết được điều đó quyết định mọi việc bạn làm tiếp theo.
Đường truyền có đầy không? Theo dõi bộ đếm của interface. Nếu throughput bị ghim gần sát giới hạn của cổng, bạn đang gặp một cuộc tấn công khối lượng lớn, và việc cần làm là mở ticket hỗ trợ, không phải sửa cấu hình:
vnstat -tr 10— throughput trung bình trong mười giây, cách đọc nhanh và trung thực nhất về mức độ bão hòa.cat /proc/net/devchạy hai lần, cách nhau một giây — chênh lệch gói tin và byte theo từng interface, không cần công cụ nào thêm.
Có phải một đợt SYN flood không? Các kết nối nửa-mở chất đống ở trạng thái SYN-RECV. Một vài kết nối là bình thường; hàng nghìn thì không:
ss -s— dòng tóm tắt, số lượng kết nối theo từng trạng thái, nhìn một phát là biết.ss -tn state syn-recv | wc -l— con số cụ thể thực sự quan trọng.
Có phải tầng 7 không? Nếu băng thông không có gì bất thường nhưng mọi thứ đều chậm, hãy đếm số request theo từng client trong access log của bạn. Một địa chỉ duy nhất với hàng chục nghìn lượt truy cập là một kẻ nghiệp dư; một trăm nghìn địa chỉ, mỗi địa chỉ chỉ ba lượt, mới là hàng thật:
tail -n 100000 access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -30- Đổi
$1thành$7để xếp hạng theo đường dẫn được yêu cầu thay vì địa chỉ. Nếu một endpoint tốn kém chiếm áp đảo, bạn vừa tìm ra mục tiêu và có sẵn luôn một nửa cách khắc phục.
Thứ gì thực sự đã cạn kiệt? Load average tự nó nói lên rất ít. Hãy tìm giới hạn cụ thể mà bạn vừa chạm tới: các tiến trình con PHP-FPM đều đang bận, kết nối database đã chạm giới hạn, file descriptor cạn kiệt, hoặc worker bị kẹt ở trạng thái D chờ đĩa. Chính giới hạn đó — chứ không phải lưu lượng — mới là thứ đánh sập trang của bạn, và nâng nó lên thường nhanh hơn nhiều so với việc đi lọc bất cứ thứ gì.
Hãy ghi nhớ điều này trong lúc tìm hiểu: nếu bạn vận hành một dịch vụ chú trọng quyền riêng tư, một cuộc tấn công chính là lúc bạn dễ bị cám dỗ bật log lên mức chi tiết tối đa và giữ nguyên như vậy. Hãy bật lên nếu thực sự cần, rồi tắt lại và xoay vòng (rotate) các file log thật quyết liệt ngay sau đó. Một sự cố để lại cả tháng log truy cập chi tiết đầy đủ trên đĩa chỉ là đánh đổi một vấn đề lấy một vấn đề khác tệ hơn và tồn tại lâu hơn. Hướng dẫn OpSec máy chủ của chúng tôi trình bày kỷ luật này thuộc về đâu.
Mười phút đầu tiên
Dưới áp lực, người ta có xu hướng với lấy đòn bẩy lớn nhất có sẵn, và đòn bẩy lớn nhất thường lại sai. Đây là thứ tự giúp giới hạn thiệt hại, xếp gần đúng theo mức độ nhanh và an toàn giảm dần:
- Phân loại trước khi hành động. Interface bão hòa nghĩa là khối lượng lớn; interface im ắng mà worker lại bận nghĩa là tầng 7. Ba mươi giây bỏ ra ở đây giúp bạn khỏi mất cả tiếng đồng hồ sửa nhầm tầng.
- Nếu là tấn công khối lượng lớn, hãy mở ticket ngay lập tức, kèm theo địa chỉ đích, thời điểm bắt đầu, và các số liệu từ bộ đếm interface của bạn. Sau đó ngừng gõ bất cứ thứ gì vào server — bạn không thể khắc phục chuyện này từ bên trong nó.
- Nếu là tầng 7, hãy cache trước tiên. Bật cache toàn trang mạnh tay cho khách ẩn danh là cách nhanh nhất để biến một sự cố sập trang thành một cái nhún vai, và đây cũng là biện pháp duy nhất trong danh sách này giúp ích ngay cả với lưu lượng thật.
- Rồi mới giới hạn tốc độ — nhắm vào endpoint bị nhắm trước, mọi thứ còn lại sau. Hãy bắt đầu thận trọng. Một giới hạn đánh văng cả người dùng thật của chính bạn chỉ là một cách tự tiếp tay cho cuộc tấn công.
- Chỉ chặn những gì rõ ràng không thể nhầm lẫn. Một chục địa chỉ, mỗi địa chỉ gửi một trăm nghìn request, một user-agent giả mạo lộ liễu, một quốc gia bạn chẳng có người dùng nào. Hãy cưỡng lại ý muốn viết ra các luật phức tạp trong lúc đang bị tấn công; sang tháng sau bạn sẽ chẳng nhớ nổi chúng làm gì.
- Chủ động xả bớt tải nếu buộc phải làm vậy. Phục vụ một trang tĩnh "đang bảo trì" cho khách chưa xác thực giúp máy chủ sống sót, giữ API của bạn hoạt động, và mua thêm thời gian để suy nghĩ. Tự quyết định cái gì phải hy sinh vẫn tốt hơn là để nó bị quyết định thay bạn.
- Ghi lại những gì bạn đã làm. Mỗi luật tạm thời bạn thêm vào là một quả mìn cho chính bạn trong tương lai. Luật nào có tác dụng thì giữ lại vĩnh viễn; phần còn lại nên gỡ bỏ ngay ngày hôm sau.
Giới hạn tốc độ trụ vững, và sai lầm hầu như ai cũng mắc
Giới hạn tốc độ là công cụ chủ lực cho tầng 7, và nginx làm việc này tốt với hai directive giải quyết hai vấn đề thực sự khác nhau. limit_req giới hạn tốc độ request — tần suất một client được phép hỏi. limit_conn giới hạn số kết nối đồng thời — số kết nối mà một client được phép giữ mở cùng lúc. Các đợt lũ cần đến cái đầu tiên; các cuộc tấn công slow-loris, vốn giữ hàng nghìn kết nối gần như không hoạt động để rút cạn worker pool của bạn, cần đến cái thứ hai. Chỉ triển khai một trong hai thì bạn mới chỉ được bảo vệ trước một nửa vấn đề.
Ba chi tiết phân biệt một giới hạn thực sự có tác dụng với một giới hạn chỉ mang tính hình thức:
- Hãy dùng burst, và dùng
nodelay. Trình duyệt thật vốn hoạt động theo từng đợt (bursty) — một lượt xem trang bắn ra cả chục request tài nguyên gần như cùng lúc. Một giới hạn không có khoảng dư sẽ bóp nghẹt khách thật, trong khi một kẻ tấn công biết tiết chế ở ngay dưới ngưỡng lại ung dung lọt qua. - Giới hạn riêng các endpoint tốn kém. Trang tìm kiếm, form đăng nhập, đặt lại mật khẩu, và bất kỳ endpoint nào ghi vào database đều xứng đáng có một ngân sách chặt hơn nhiều so với các tài nguyên tĩnh. Kẻ tấn công tìm ra chúng mà chẳng cần cố gắng, vì đó chính là những chỗ gây đau nhất.
- Trả về 429, không phải 503. Mã trạng thái này là tín hiệu cho các client biết điều và cho các công cụ tìm kiếm rằng đây là việc hãm tốc độ chứ không phải lỗi — và nó giúp một buổi chiều tồi tệ không biến thành một vấn đề về thứ hạng tìm kiếm.
Sai lầm âm thầm vô hiệu hóa tất cả những điều trên: nếu có bất cứ thứ gì đứng trước server của bạn — một CDN, một load balancer, hay reverse proxy của chính bạn — thì mọi request đều đến từ địa chỉ của nó, không phải của khách truy cập. Một giới hạn tốc độ theo từng client khi đó sẽ tính cả internet là một client duy nhất, và nó sẽ hoặc chẳng làm gì cả, hoặc cấm luôn toàn bộ khán giả của bạn cùng một lúc. Bạn phải cấu hình nguồn real-IP của mình (trong nginx là set_real_ip_from cho các dải địa chỉ của proxy, cộng với real_ip_header cho header mà nó gửi) trước khi mọi giới hạn có ý nghĩa gì. Cũng hãy giới hạn điều đó chỉ trong đúng các dải địa chỉ của proxy: tin tưởng một header do client tự cung cấp từ internet mở cho phép kẻ tấn công giả mạo một danh tính mới cho mỗi request và ung dung đi xuyên qua mọi giới hạn bạn dựng lên.
Cache là biện pháp chống đỡ rẻ nhất bạn từng triển khai
Một giới hạn tốc độ từ chối công việc. Một cache khiến công việc đó không cần tồn tại nữa. Với bất cứ thứ gì một khách ẩn danh nhìn thấy, cache toàn trang thay đổi hẳn bài toán kinh tế của toàn bộ cuộc tấn công: một request lẽ ra tốn một lượt round-trip tới database, một lượt dựng template và một PHP worker giờ chỉ còn là một lượt đọc file tính bằng micro giây. Chính chiếc server từng sập ở bốn trăm request động mỗi giây giờ có thể phục vụ hàng chục nghìn request đã cache mà chẳng hề hấn gì.
Những điều quan trọng khi bạn bật nó lên trong lúc nước sôi lửa bỏng:
- Chỉ cache cho khách ẩn danh. Bỏ qua cache khi có session cookie. Phục vụ nhầm trang của một người dùng đã đăng nhập cho một người khác là một sự cố còn tệ hơn nhiều so với việc sập trang bạn đang cố khắc phục.
- Chủ động phục vụ nội dung cũ (stale).
proxy_cache_use_stalecủa nginx kết hợp vớiupdating error timeoutnghĩa là khi backend của bạn đang gặp khó khăn, khách truy cập sẽ nhận được một trang hơi cũ thay vì một lỗi. Trong lúc bị tấn công, đây chính là khác biệt giữa một trang trông vẫn ổn và một trang trông như đã chết. - Gộp các lượt cache-miss trùng nhau.
proxy_cache_lockđảm bảo rằng một nghìn request đồng thời cho cùng một trang chưa được cache chỉ tạo ra một request tới backend, chứ không phải một nghìn. Thiếu nó, một cuộc tấn công phá cache sẽ xuyên thẳng qua cache và dội thẳng toàn lực vào database của bạn. - Vô hiệu hóa các query string phá cache. Thủ thuật tiêu chuẩn là thêm
?cùng một giá trị ngẫu nhiên để mỗi request trở thành một khóa duy nhất và luôn miss cache. Hãy chuẩn hóa cache key của bạn để bỏ qua các tham số query mà ứng dụng của bạn thực ra không hề dùng đến.
Có một sự bất đối xứng dễ chịu ở đây, đáng để khắc cốt ghi tâm: mỗi giờ bạn bỏ ra cho việc cache cũng đồng thời khiến trang web nhanh hơn vào ngày đẹp trời nhất của nó, rẻ hơn để vận hành, và giỏi sống sót hơn trước chính thành công của mình. Gần như không tuyến phòng thủ nào khác trả cổ tức cho bạn khi chẳng có gì bất ổn xảy ra cả.
Những giới hạn quyết định bạn có gục ngã hay không
Hầu hết server không chết vì hết CPU. Chúng chết vì chạm phải một giới hạn vô hình mà chẳng ai chủ đích đặt ra — một giá trị mặc định từ cả chục năm trước, từng hợp lý trên loại phần cứng giờ chẳng ai còn dùng. Khi bị tấn công, đây mới là những thứ thực sự gãy trước tiên:
- Worker ứng dụng.
pm.max_childrencủa PHP-FPM, số worker Python, hay kích thước cluster Node của bạn. Đây chính là giới hạn kết nối đồng thời thực sự của trang web bạn. Khi tất cả đều bận, mỗi khách truy cập thêm vào sẽ phải xếp hàng, và trang web coi như sập bất kể CPU trông rảnh rỗi đến đâu. Chỉ nên nâng nó lên đến mức bộ nhớ còn cho phép — swap còn tệ hơn việc phải xếp hàng chờ. - Hàng đợi chấp nhận kết nối (accept queue).
net.core.somaxconnvà backlog lắng nghe của bạn quyết định có bao nhiêu kết nối được phép chờ để được chấp nhận. Một backlog nhỏ biến một đợt burst lẽ ra chịu được thành hàng loạt kết nối bị từ chối. - SYN cookie.
net.ipv4.tcp_syncookiescho phép kernel trả lời một đợt SYN flood mà không cần cấp phát trạng thái cho các kết nối sẽ không bao giờ hoàn tất. Các kernel hiện đại bật sẵn tùy chọn này theo mặc định; hãy kiểm tra lại thay vì mặc định tin vào điều đó, vì việc kiểm tra chẳng tốn gì mà lại cứu bạn khỏi loại lũ phổ biến nhất tồn tại. - File descriptor. Mỗi kết nối là một descriptor. Giới hạn
nofilemặc định thường thấp hơn số kết nối bạn đang cố phục vụ, và kiểu lỗi mà nó gây ra — accept thất bại trong khi mọi thứ trông vẫn khỏe mạnh — thực sự gây bối rối khi việc đó xảy ra lúc 3 giờ sáng. - Kết nối database. Nâng số worker mà không nâng connection pool chỉ đơn giản là dời hàng đợi sang một chỗ khó nhìn thấy hơn. Hai con số này phải được điều chỉnh cùng nhau.
Hãy tinh chỉnh những thứ này vào một ngày yên bình, không phải trong lúc sự cố đang diễn ra. Ý nghĩa của việc biết trước các con số này là khi trang web sập, bạn có thể gọi đúng tên giới hạn nào vừa bị chạm tới thay vì phải đoán mò — và một giới hạn đã được gọi tên là một giới hạn đã được ấn định.
Đặt một lớp đứng trước origin
Mọi thứ ở trên đều diễn ra ngay trên server. Bước tiếp theo là quyết định liệu server có nên là thứ trực tiếp nhận lưu lượng hay không. Có ba lựa chọn trung thực, và câu trả lời đúng phụ thuộc vào thứ bạn đang host nhiều hơn là vào số tiền bạn có thể bỏ ra.
| Cách tiếp cận | Bạn được gì | Bạn phải trả giá gì |
|---|---|---|
| Origin lộ trực tiếp, đã được gia cố | Đơn giản, không bên thứ ba, không có việc kết thúc TLS nằm ngoài tầm kiểm soát của bạn | Địa chỉ của bạn công khai và vĩnh viễn. Tầng 7 hoàn toàn là việc của bạn |
| CDN thương mại hoặc dịch vụ scrubbing | Năng lực hấp thụ cực lớn, một trang thử thách chỉ cách một cú nhấp, cache trên phạm vi toàn cầu | Một bàn khiếu nại có ý kiến riêng về nội dung của bạn, và một công ty có thể nhìn thấy lưu lượng của bạn. Với các dự án offshore hay nhạy cảm với DMCA, đây có thể là mắt xích yếu nhất trong một cách bố trí vốn dĩ đã rất cẩn trọng |
| Node mặt tiền của riêng bạn — một VPS nhỏ chạy nginx, proxy về một origin đã được rào bằng firewall | Toàn quyền kiểm soát, không bên thứ ba nào nằm trên đường đi của request, một địa chỉ bạn có thể đốt bỏ và thay thế bất cứ lúc nào, và một địa chỉ thật luôn được giữ kín | Giới hạn năng lực của riêng nó, và thêm một cỗ máy nữa phải vận hành. Hai hoặc ba mặt tiền đặt ở các mạng khác nhau khiến việc đánh sập trở nên khó khăn hơn hẳn |
Node mặt tiền tự vận hành xứng đáng được chú ý nhiều hơn mức nó thường nhận được, đặc biệt với bất kỳ ai chọn hosting offshore vì những lý do mà một CDN lớn chưa chắc đã cảm thông. Mô hình này chẳng có gì hào nhoáng: các node proxy giá rẻ đặt phía trước, origin được rào bằng firewall để chỉ chấp nhận kết nối từ đúng các node đó, DNS trỏ vào các mặt tiền. Nếu một mặt tiền bị tấn công, bạn thay nó bằng một địa chỉ mới trong vài phút và origin chẳng hề hay biết. Phiên bản đầy đủ của kiến trúc này — bao gồm cả sáu cách một địa chỉ origin vẫn có thể rò rỉ dù đã làm mọi thứ — là chủ đề của hướng dẫn ẩn IP origin server của bạn.
Một origin được giấu kín đáng giá hơn mọi bộ lọc
Điều này đáng nói thẳng ra, vì nó đảo ngược thứ tự ưu tiên thông thường: biện pháp chống DDoS rẻ nhất mà bạn có được là một địa chỉ mà kẻ tấn công không hề có. Lọc lưu lượng là việc bạn làm khi điều đó đã thất bại rồi.
Điều này quan trọng hơn vẻ ngoài của nó, bởi địa chỉ origin rò rỉ liên tục và âm thầm. Các bản ghi DNS lịch sử từ trước khi bạn đặt một proxy ra phía trước vẫn tồn tại lâu hơn sự thay đổi đó hàng năm trời. Mail gửi trực tiếp từ ứng dụng mang theo địa chỉ trong header của nó. Một chứng chỉ TLS cấp cho địa chỉ thô được công bố vĩnh viễn trong các log certificate transparency. Một trang lỗi, một redirect, hay một subdomain hẻo lánh chưa từng được proxy — tất cả đều để lộ nó ra. Nếu bạn từng đặt một CDN trước một server vốn đã từng lộ diện, hãy mặc định rằng địa chỉ cũ đã bị biết cho đến khi bạn thực sự đổi nó.
Hệ quả tất yếu là một luật firewall, và đây là dòng giá trị nhất trong toàn bộ hướng dẫn này: một khi đã có gì đó đứng trước, origin nên từ chối mọi kết nối trên cổng 80 và 443 từ bất cứ đâu ngoại trừ các địa chỉ của lớp mặt tiền đó. Thiếu điều này, proxy chỉ còn là một gợi ý — bất kỳ ai biết được địa chỉ thật đơn giản là đi vòng qua nó và tấn công thẳng vào bạn, và mọi thứ bạn đã cấu hình ở lớp mặt tiền trở thành vô nghĩa, chỉ còn tác dụng trang trí.
Chọn phần cứng và địa điểm để các cuộc tấn công trở nên nhàm chán
Một phần của việc này được quyết định từ trước khi bất kỳ cuộc tấn công nào xảy ra, ngay tại thời điểm bạn chọn gói dịch vụ. Ba đặc tính sau quan trọng hơn nhiều so với những gì bảng thông số kỹ thuật gợi ý:
- Băng thông không giới hạn. Trên một gói tính theo dung lượng, một cuộc tấn công không chỉ là sự cố sập trang — đó còn là một hóa đơn. Lưu lượng bạn chưa từng yêu cầu và không thể từ chối vẫn bị tính vào hạn mức của bạn. Băng thông không giới hạn biến một rủi ro tài chính thành một vấn đề thuần kỹ thuật, và đó là một loại vấn đề dễ chịu hơn nhiều.
- Cổng mạng có thực sự thuộc về riêng bạn không. Trên một host ảo hóa dùng chung, một hàng xóm đang bị tấn công có thể kéo tụt hiệu năng của bạn, và giới hạn bảo vệ của chính bạn cũng là thứ dùng chung. Phần cứng chuyên dụng với cổng mạng riêng loại bỏ cả hai hiệu ứng này. Với một dự án dự kiến sẽ hứng chịu sự chú ý thù địch, đây là lý do rõ ràng nhất để nâng cấp từ một VPS — rõ ràng hơn cả lý do về số lõi CPU hay RAM.
- Mạng đặt ở đâu. Một mạng châu Âu kết nối tốt với năng lực transit thực sự sẽ hấp thụ được một đợt lũ mà một mạng peering kém sẽ không chống nổi, và thẩm quyền pháp lý bạn chọn vì lý do pháp lý cũng có những đặc tính mạng riêng của nó. Cả hai điều này đều đáng để kiểm tra khi chọn trong số các địa điểm hiện có.
Ở đây còn có một lập luận về quy mô âm thầm nghiêng về phía sự đơn giản. Một trang tĩnh đặt sau một lớp cache trên một server khiêm tốn thì cực kỳ khó đánh sập; cũng nội dung đó nhưng chạy trên một CMS nặng nề với một endpoint tìm kiếm không được cache thì có thể bị đánh gục bởi một người quyết tâm với một đoạn script. Giảm bớt phần động của trang web là một biện pháp chống đỡ, và nó hoàn toàn miễn phí. Nếu dự án của bạn thực sự sống dưới tải liên tục, các ghi chú về hosting cho lưu lượng cao của chúng tôi sẽ đề cập tới khía cạnh chọn cấu hình phù hợp cho cùng câu hỏi này.
Năm điều không nên làm
Các kiểu thất bại ở đây đủ nhất quán để liệt kê ra, và mỗi kiểu đều từng khiến ai đó mất toi một cuối tuần:
- Đừng tự null-route chính mình. Việc chặn đứng hoàn toàn (blackhole) địa chỉ của chính bạn chấm dứt cuộc tấn công theo đúng nghĩa đen triệt để nhất có thể — không ai chạm tới được bạn nữa, kể cả người dùng của bạn. Đây là công cụ chỉ dùng khi hết cách của nhà cung cấp, không phải hành động bạn tự nguyện thực hiện.
- Đừng coi fail2ban là biện pháp chống DDoS. Đó là một công cụ tốt để chống các nỗ lực brute-force từ một vài địa chỉ. Trước một đợt lũ phân tán, nó phản ứng trong vài phút với thứ đến trong vài giây, và một luật cấm hàng nghìn địa chỉ có thể tốn nhiều tài nguyên xử lý firewall hơn cả chính cuộc tấn công.
- Đừng trả tiền chuộc. Phần lớn áp đảo các email tống tiền đe dọa một cuộc tấn công tàn khốc đến từ những kẻ chẳng có năng lực gì, chỉ gửi hàng nghìn tin nhắn giống hệt nhau. Số ít thực sự có khả năng ra tay sẽ quay lại tìm bạn lần nữa, vì bạn đã chứng minh rằng mình chịu trả tiền.
- Đừng trả đũa. Ngoài việc gần như ở đâu cũng phạm pháp, các nguồn tấn công đều là những bên thứ ba đã bị xâm nhập. Bạn sẽ đang tấn công chính các nạn nhân, và làm điều đó từ một địa chỉ rõ ràng thuộc về bạn.
- Đừng di chuyển trong hoảng loạn. Chuyển host giữa lúc đang bị tấn công đồng nghĩa với việc địa chỉ mới công khai chỉ trong vài phút và bạn chưa có cấu hình nào hoạt động ổn định. Hãy ổn định tình hình trước, rồi di chuyển một cách có chủ đích sau — và nếu bạn thực sự di chuyển, hướng dẫn di chuyển không downtime của chúng tôi tồn tại chính là để cuộc di chuyển đó không trở thành một sự cố thứ hai.
Bản tóm tắt
Lược bỏ hết phần lý giải, mô hình thực chiến gói gọn trong tám dòng:
- Phân loại trước tiên. Interface bão hòa nghĩa là tấn công khối lượng lớn, thuộc về host của bạn. Interface im ắng mà worker cạn kiệt nghĩa là tầng 7, thuộc về bạn.
- Với tấn công khối lượng lớn, hãy mở ticket kèm địa chỉ, mốc thời gian và các số liệu bộ đếm của bạn — rồi ngừng động vào server.
- Cache mạnh tay cho khách ẩn danh, phục vụ nội dung cũ khi backend chịu áp lực, và gộp các lượt cache-miss trùng nhau. Đây là thay đổi mang lại đòn bẩy lớn nhất bạn có thể thực hiện.
- Giới hạn theo cả tốc độ request lẫn số kết nối đồng thời, chặt nhất ở các endpoint tốn kém nhất, và trả về 429.
- Sửa cấu hình real-IP trước khi làm bất cứ điều gì khác, nếu không mọi giới hạn theo từng client phía sau một proxy sẽ hoặc vô dụng, hoặc gây thảm họa.
- Nắm rõ các giới hạn của bạn — worker, backlog, descriptor, kết nối database — và chủ động nâng chúng lên vào một ngày yên bình.
- Giữ bí mật địa chỉ origin và chỉ mở firewall cho các node mặt tiền của bạn. Điều này đáng giá hơn tất cả các bộ lọc cộng lại.
- Mua băng thông không giới hạn để lưu lượng bạn không hề yêu cầu không bao giờ biến thành một hóa đơn.
Không điều nào trong số này khiến bạn miễn nhiễm, và bất kỳ ai rao bán sự miễn nhiễm đều đang bán một thứ gì đó khác. Điều những biện pháp này thực sự làm là đưa bạn ra khỏi nhóm những người bị đánh sập bởi một cậu nhóc rảnh rỗi, và đưa bạn vào nhóm đòi hỏi nguồn lực thật sự và chủ đích thật sự mới có thể quấy rối được — và với đại đa số dự án, điều đó chẳng khác gì an toàn tuyệt đối. Phần còn lại chỉ là những công việc chẳng có gì hào nhoáng, vốn khiến một server trở nên tốt ở mọi mặt khác: được gia cố ngay từ ngày đầu tiên, khôi phục được vào ngày tồi tệ nhất, và chạy ở một nơi coi lưu lượng của bạn là công việc kinh doanh của chính họ.