Kas yra rate limiting ir kodėl jis svarbus
Turbūt kiekvienas, kas administruoja serverius ar kuria web aplikacijas, bent kartą susidūrė su situacija, kai serveris tiesiog „krinta” nuo per didelio užklausų srauto. Kartais tai būna natūralus populiarumo šuolis, bet dažnai – kenkėjiška ataka. Rate limiting yra viena iš pagrindinių gynybos linijų prieš tokius incidentus.
Paprasčiausiai tariant, rate limiting – tai mechanizmas, kuris riboja, kiek užklausų tam tikras vartotojas ar IP adresas gali atlikti per nustatytą laiko tarpą. Pavyzdžiui, galite nustatyti, kad vienas IP adresas gali atlikti tik 10 užklausų per sekundę. Viršijus šį limitą, serveris tiesiog atmeta papildomas užklausas arba jas pristabdo.
NGINX šiuo atžvilgiu yra puiki priemonė, nes rate limiting funkcionalumas yra integruotas į patį serverį ir veikia labai efektyviai. Nereikia jokių papildomų įskiepių ar sudėtingų konfigūracijų – tiesiog kelis parametrus ir jūsų serveris jau turi bazinę apsaugą.
Kaip veikia NGINX rate limiting mechanizmas
NGINX naudoja taip vadinamą „leaky bucket” algoritmą. Įsivaizduokite kibirą su skylute apačioje – vanduo (užklausos) įpilamas į viršų, o iš apačios lašas po lašo (kontroliuojamu greičiu) išteka. Jei pilate per greitai, kibiras persipildo ir vanduo išsilieja (užklausos atmetamos).
Techniškai NGINX sukuria atmintinę zoną, kurioje saugo informaciją apie kiekvieno kliento užklausas. Šis procesas vyksta labai greitai ir neapkrauna serverio, nes visa informacija laikoma operatyviojoje atmintyje. Kai ateina nauja užklausa, NGINX patikrina, ar tas konkretus klientas (paprastai identifikuojamas pagal IP adresą) neviršijo nustatyto limito.
Svarbu suprasti, kad rate limiting veikia dviem lygmenimis: galite nustatyti maksimalų užklausų greitį (pvz., 10 užklausų per sekundę) ir maksimalų „burst” dydį – tai leidžia trumpam viršyti limitą, bet ne per daug. Tai naudinga teisėtiems vartotojams, kurie kartais gali atlikti kelias užklausas iš karto.
Bazinė NGINX konfigūracija DDoS apsaugai
Pradėkime nuo paprasčiausios konfigūracijos. Pirmiausia reikia apibrėžti rate limiting zoną jūsų nginx.conf faile arba atitinkamame http bloke:
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location / {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
Išnagrinėkime, kas čia vyksta:
limit_req_zone direktyva sukuria atmintinės zoną pavadinimu „mylimit”, kuri užima 10MB (tai pakanka maždaug 160,000 IP adresų saugoti). $binary_remote_addr reiškia, kad ribojame pagal kliento IP adresą, o rate=10r/s nustato limitą – 10 užklausų per sekundę.
burst=20 parametras leidžia trumpam „proveržiui” – jei vartotojas staiga atlieka 15 užklausų, jos nebus iš karto atmestos, bet bus apdorotos pagal nustatytą greitį. nodelay reiškia, kad užklausos bus apdorotos iš karto, o ne laukiamos eilėje.
Jei norite griežtesnės apsaugos, galite pašalinti burst parametrą arba sumažinti jo reikšmę. Taip pat galite nenaudoti nodelay – tuomet viršijusios užklausos bus pristabdomos, o ne atmestos.
Pažangesnės rate limiting strategijos
Realybėje viena konfigūracija visiems atvejams neveikia. Skirtingi endpoint’ai turi skirtingus poreikius. Pavyzdžiui, prisijungimo puslapis turėtų turėti daug griežtesnį limitą nei paprasto turinio peržiūra.
Štai kaip galite sukurti kelis skirtingus limitus:
http {
# Bendras limitas
limit_req_zone $binary_remote_addr zone=general:10m rate=50r/s;
# Griežtas limitas prisijungimui
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# API limitas
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
server {
location / {
limit_req zone=general burst=100 nodelay;
proxy_pass http://backend;
}
location /login {
limit_req zone=login burst=2 nodelay;
proxy_pass http://backend;
}
location /api/ {
limit_req zone=api burst=200 nodelay;
proxy_pass http://backend;
}
}
}
Atkreipkite dėmesį, kad prisijungimo endpoint’as turi limitą 5 užklausos per minutę (5r/m) – tai apsaugo nuo brute force atakų, bet netrukdo teisėtiems vartotojams.
Dar vienas naudingas triukas – riboti ne tik pagal IP, bet ir pagal kitus parametrus. Pavyzdžiui, galite riboti pagal sesiją arba API raktą:
limit_req_zone $cookie_session_id zone=session_limit:10m rate=20r/s;
Klaidų tvarkymas ir vartotojo patirtis
Kai vartotojas viršija limitą, NGINX pagal nutylėjimą grąžina 503 (Service Unavailable) statusą. Bet tai ne visada optimalu – geriau grąžinti 429 (Too Many Requests), kuris tiksliau atspindi situaciją:
location / {
limit_req zone=mylimit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
Dar geriau – sukurti custom error puslapį, kuris paaiškina vartotojui, kas vyksta:
error_page 429 /rate_limit_error.html;
location = /rate_limit_error.html {
root /usr/share/nginx/html;
internal;
}
Šiame rate_limit_error.html faile galite įdėti draugišką pranešimą, pavyzdžiui: „Atsiprašome, bet jūs atlikote per daug užklausų. Prašome palaukti minutę ir bandyti dar kartą.”
Taip pat naudinga pridėti Retry-After headerį, kuris nurodo, po kiek laiko vartotojas gali bandyti dar kartą:
location / {
limit_req zone=mylimit burst=20 nodelay;
limit_req_status 429;
add_header Retry-After 60 always;
proxy_pass http://backend;
}
Whitelist ir blacklist valdymas
Ne visi IP adresai turėtų būti ribojami vienodai. Jūsų monitoringo sistemoms, administratoriams ar patikimiems partneriams gali reikėti neriboto prieigos. NGINX leidžia lengvai sukurti išimčių sąrašą:
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/16 0;
203.0.113.0/24 0; # Patikimi partneriai
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;
Šioje konfigūracijoje naudojame geo modulį, kuris nustato $limit kintamąjį pagal IP adresą. Jei IP patenka į whitelist, $limit yra 0, ir $limit_key tampa tuščia eilute – tokios užklausos nebus ribotos.
Blacklist’ui galite naudoti paprastesnį metodą – tiesiog blokuoti tam tikrus IP:
location / {
deny 192.0.2.1;
deny 198.51.100.0/24;
allow all;
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
Bet jei turite didelį blacklist’ą, geriau jį laikyti atskirame faile:
include /etc/nginx/blacklist.conf;
Monitoringas ir logai
Rate limiting veikia tik tada, kai jūs žinote, kas vyksta. NGINX leidžia nustatyti skirtingus log lygius rate limiting įvykiams:
limit_req_log_level warn;
Galite pasirinkti: info, notice, warn arba error. Rekomenduoju pradėti nuo warn, kad matytumėte, kas vyksta, bet neperpildytumėte logų.
Norėdami geriau analizuoti, galite sukurti atskirą log failą rate limiting įvykiams:
http {
log_format rate_limit '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time';
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
access_log /var/log/nginx/rate_limit.log rate_limit;
location / {
limit_req zone=mylimit burst=20 nodelay;
proxy_pass http://backend;
}
}
}
Šie logai padės identifikuoti, ar jūsų limitai nėra per griežti (daug teisėtų vartotojų blokuojami) arba per švelnūs (atakos vis dar praeina).
Galite naudoti įrankius kaip fail2ban, kurie automatiškai analizuoja logus ir blokuoja įtartinus IP adresus. Arba sukurti paprastą bash skriptą, kuris periodiškai tikrina logus:
#!/bin/bash
tail -n 1000 /var/log/nginx/rate_limit.log | \
grep "limiting requests" | \
awk '{print $1}' | \
sort | uniq -c | sort -rn | head -10
Šis skriptas parodys 10 IP adresų, kurie dažniausiai viršija limitą.
Kai rate limiting neužtenka: papildomi apsaugos sluoksniai
Rate limiting yra puiki pirmoji gynybos linija, bet rimtesnėms DDoS atakoms to gali neužtekti. Štai keletas papildomų priemonių, kurias galite integruoti su NGINX:
**Connection limiting** – ribojate ne tik užklausų skaičių, bet ir aktyvių prisijungimų skaičių:
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /download/ {
limit_conn addr 2; # Tik 2 aktyvūs prisijungimai
limit_rate 500k; # 500KB/s per prisijungimą
}
}
**Request size limiting** – ribojate užklausų dydį, kad apsisaugotumėte nuo „body” atakų:
client_body_buffer_size 1K; client_header_buffer_size 1k; client_max_body_size 1k; large_client_header_buffers 2 1k;
**Timeout optimizavimas** – sumažinę timeout’us, sumažinsite atakų poveikį:
client_body_timeout 10; client_header_timeout 10; keepalive_timeout 5 5; send_timeout 10;
**ModSecurity integracija** – tai Web Application Firewall (WAF), kurį galite integruoti su NGINX. Jis suteikia daug pažangesnes apsaugos galimybes, įskaitant OWASP Core Rule Set.
Jei naudojate NGINX Plus (mokamą versiją), turite prieigą prie dar pažangesnių funkcijų, tokių kaip dinaminis blacklisting, health checks ir advanced monitoring.
Praktiniai patarimai iš realaus gyvenimo
Dirbdamas su rate limiting, susiduriu su keliomis tipinėmis klaidomis, kurių verta vengti:
**Per griežti limitai** – pradedantieji dažnai nustato per mažus limitus, manydami, kad tai geriau apsaugos. Rezultatas – teisėti vartotojai negali normaliai naudotis svetaine. Pradėkite nuo švelnesių limitų ir palaipsniui juos griežtinkite, stebėdami logus.
**Ignoruojami CDN ir proxy** – jei naudojate CloudFlare, AWS CloudFront ar kitus proxy, visi užklausos ateis iš jų IP adresų. Tuomet rate limiting pagal IP neveiks. Sprendimas – naudoti X-Forwarded-For headerį:
set_real_ip_from 103.21.244.0/22; # CloudFlare IP range real_ip_header X-Forwarded-For; limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
**Neatsižvelgiama į bot’us** – teisėti botai (Google, Bing) taip pat gali viršyti limitus. Galite juos identifikuoti ir taikyti skirtingus limitus:
map $http_user_agent $is_bot {
default 0;
~*(googlebot|bingbot|yandex) 1;
}
map $is_bot $limit_key {
0 $binary_remote_addr;
1 "";
}
limit_req_zone $limit_key zone=mylimit:10m rate=10r/s;
**Užmirštas testavimas** – prieš įjungdami rate limiting production’e, būtinai išbandykite su įrankiais kaip ab (Apache Bench) arba wrk:
ab -n 1000 -c 10 http://your-site.com/
Tai padės suprasti, kaip jūsų konfigūracija veikia realių apkrovų metu.
Kai viskas suveikia kartu: holistinis požiūris į apsaugą
Rate limiting NGINX yra galingas įrankis, bet jis veikia geriausiai kaip dalies platesnės saugumo strategijos. Tikrai verta derinti jį su kitomis priemonėmėmis: firewall taisyklėmis tinklo lygmenyje, aplikacijos lygio validacija, reguliariais saugumo auditais.
Svarbu suprasti, kad tobula konfigūracija neegzistuoja – ji turi evoliucionuoti kartu su jūsų aplikacija ir grėsmių kraštovaizdžiu. Reguliariai peržiūrėkite logus, analizuokite tendencijas, koreguokite limitus. Kartais gali prireikti skirtingų konfigūracijų skirtingu paros metu arba skirtingais savaitės dienomis.
Pradėjus naudoti rate limiting, pastebėsite, kad serverio apkrova sumažėja, o stabilumas padidėja. Tai ypač jaučiama, kai ateina tikra ataka – vietoj to, kad serveris „krintų”, jis tiesiog ramiai atmeta perteklines užklausas, o teisėti vartotojai toliau gali normaliai naudotis paslauga.
Nepamirškite, kad rate limiting – tai ne tik techninė priemonė, bet ir dalies geros vartotojo patirties. Tinkamai sukonfigūruotas, jis apsaugo jūsų infrastruktūrą, bet netrukdo teisėtiems vartotojams. O tai ir yra tikrasis saugumo meno esmė – apsaugoti, bet netrukdyti.
