Rate limit-ს ჩვეულებრივ დატვირთვისგან დაცვად ხსნიან, რაც მის მნიშვნელობას აკნინებს. ჩვეულებრივ ტრაფიკს ლიმიტი არ სჭირდება. ლიმიტი არსებობს იმ ერთი კლიენტისთვის, რომელიც წუთში ათი ათას მოთხოვნას გააგზავნის - განზრახ თუ ვინმეს შემთხვევით დაწერილი ციკლის გამო - და იმ თავდამსხმელისთვის, რომელიც გაჟონილი პაროლების სიას ერთ-ერთ ანგარიშზე ერთხელ გადის. მთელი აუდიტორია სწორედ ეს ორია.
ლიმიტი ოთხი გადაწყვეტილებაა: რას ითვლი, რას უკავშირებ თვლას, რა დროის ფანჯარაში და რას აკეთებ, როცა ზღვარი გადაილახა. თუ მეორეს შეცდები, ლიმიტი ან არაფერს აკეთებს, ან მთელ ოფისს ბლოკავს. ეს პოსტი ოთხივეს ეხება, ასევე ალგორითმებს მათ უკან, nginx-ისა და აპლიკაციის კონფიგურაციას, რომელიც მათ ახორციელებს, საწყის რიცხვებს და იმ თავდასხმებს, რომლებსაც rate limit ვერ შეეხება.
სად უნდა იდგეს ლიმიტები, იმ თანმიმდევრობით, როგორითაც უნდა დაამატო#
ლიმიტი ყველა route-ზე არ გჭირდება. გჭირდება იქ, სადაც ერთ კლიენტს შეუძლია ფული, რეპუტაცია ან ანგარიში დაგიკარგოს. დაახლოებით იმის მიხედვით, რამდენ უსიამოვნებას იწვევს ლიმიტის გარეშე:
- Login. აქ მოდის credential stuffing: botnet, რომელიც გაჟონილ ელფოსტა-პაროლის წყვილებს შენს ფორმაზე იმეორებს. ლიმიტის გარეშე ეს თავდამსხმელისთვის უფასოა.
- ყველაფერი, რაც აგზავნის ელფოსტას, SMS-ს ან push-ს. პაროლის აღდგენა, მოწვევები, საკონტაქტო ფორმები. თითოეული ფული ღირს, და რამდენიმე ათასი მათგანი შენს გამომგზავნ დომენს დაბლოკავს.
- რეგისტრაცია. თუ არ გიყვარს იმ ანგარიშების მოდერაცია, რომლებიც არავის განზრახ არ შეუქმნია, და მათ მიერ დაპოსტილი კონტენტის წმენდა.
- ძვირი წაკითხვები. ძებნა, ექსპორტი, ანგარიშები, ყველაფერი, რაც ცხრილს სკანირებს ან PDF-ს ქმნის. ერთი კლიენტი ციკლში აქ გათიშვისგან არ განირჩევა.
- ზოგადად write endpoint-ები. კომენტარები, ატვირთვები, შეტყობინებები. სპამი ჯერ სიხშირის პრობლემაა და მერე - შინაარსის.
- ყველაფერი დანარჩენი, როგორც ფართო უკანა ზღუდე, რომ გაქცეულმა სკრიპტმა რაღაცას შეეჯახოს, სანამ შენს ბაზას მიაღწევს.
საწყისი რიცხვების ცხრილი ჩვეულებრივი ვებ აპლიკაციისთვის, რამდენიმე ათასი მომხმარებლით. ისინი განზრახ გულუხვადაა შერჩეული - პირველი ლიმიტის მიზანი ბოროტად გამოყენების დაჭერაა და არა ენთუზიაზმის პოლიცია.
| Endpoint | საწყისი ლიმიტი | რას ეფუძნება |
|---|---|---|
POST /login | 5 წარუმატებელი მცდელობა 15 წუთში | IP და username ერთად |
POST /register | 3 საათში | IP |
| პაროლის აღდგენა, მოწვევა, კონტაქტი | 3 საათში | ანგარიში, შემდეგ IP |
| ძებნა, ექსპორტი, ანგარიში | 10 წუთში | ანგარიში |
| საჯარო წაკითხვის API | 60 წუთში | API გასაღები |
| ყველაფერი დანარჩენი | 300 წუთში | IP |
შენიშნე, რომ პირველი რიგი წარუმატებლობებს ითვლის და არა მოთხოვნებს. მომხმარებელმა, რომელიც ოცჯერ სწორად შედის, არაფერი დააშავა; კლიენტი, რომელიც ხუთჯერ ვერ შედის და მაინც აგრძელებს, ხვდება. წარუმატებლობების თვლა ასევე ნიშნავს, რომ წარმატებულ შესვლას მთვლელის განულება შეუძლია, რაც ცრუ დადებითების უმეტესობას აქრობს.
ოთხი ალგორითმი და რომელი გამოიყენო#
ყველა limiter ამათგან ერთ-ერთია, ბიბლიოთეკა როგორც უნდა უწოდებდეს.
Fixed window. თვლი მოთხოვნებს ერთ გასაღებზე კალენდარულ წუთში; წუთის დასრულებისას ნულდება. ერთი მთვლელი, ერთი ვადა, შენახვა თითქმის უფასოა. მისი ნაკლი საზღვარია: კლიენტს შეუძლია მთელი ნორმა 10:59:59-ზე გაგზავნოს და მთელი ნორმა ისევ 11:00:00-ზე, ამიტომ რეალური უარესი შემთხვევა შენს მიერ დაყენებულ რიცხვზე ორჯერ მეტია. login ლიმიტებისთვის ეს უმნიშვნელოა. ფასიანი API-სთვის, სადაც ეს რიცხვი დაპირებაა, - არა.
Sliding window log. ინახავ ყოველი მოთხოვნის დროის ნიშნულს და ითვლი იმათ, რომლებიც ბოლო N წამშია. ზუსტია, მაგრამ მეხსიერება იმ ტრაფიკის პროპორციულად იზრდება, რომლის შეზღუდვასაც ცდილობ, ანუ არასწორი მიმართულებით. კარგია login მთვლელისთვის ხუთი ჩანაწერით, არასწორია გლობალური ლიმიტისთვის.
Sliding window counter. ინახავ მიმდინარე ფანჯრის და წინა ფანჯრის მთვლელს და წინას წონას ანიჭებ იმის მიხედვით, რამდენად შორს ხარ მიმდინარე ფანჯარაში. ორი მთვლელი ერთ გასაღებზე, საზღვრის აფეთქება, რომლისაც უნდა გეშინოდეს, არ არსებობს, სიზუსტე - ერთი-ორი პროცენტის ფარგლებში. სწორედ ამას აკეთებს უმეტესი CDN და უმეტესი კარგი ბიბლიოთეკა, და ეს გონივრული ნაგულისხმევია.
Token bucket. ვედრო იტევს N ტოკენს და r ტოკენს წამში ივსებს. ყოველი მოთხოვნა ერთს იღებს. უქმ კლიენტს N-მდე დაუგროვდება და ერთდროულად დახარჯვა შეუძლია; დაკავებული კლიენტი r-ზე დაწყნარდება. ეს ერთადერთი ალგორითმია, რომელიც გამოხატავს იმას, რაც ჩვეულებრივ გინდა: „წუთში სამოცი, მაგრამ ერთდროულად ათი არ მაწუხებს“. მისი ნათესავი leaky bucket იმავე არითმეტიკას აკეთებს, ოღონდ ჭარბს უარყოფის ნაცვლად რიგში აყენებს - ტრაფიკს არ უარყოფს, არამედ არბილებს.
Token bucket API-სთვის, sliding window counter ზოგადი დაცვისთვის, fixed window login მთვლელებისთვის. ეს ფარავს ყველა შემთხვევას, რაც პატარა სერვისს ექმნება.
გასაღების არჩევა, რაც რიცხვის არჩევაზე რთულია#
გასაღები ის არის, რასაც ითვლი, და სწორედ ის წყვეტს, სამართლიანია თუ არა შენი ლიმიტი.
- IP მისამართი ერთადერთი გასაღებია, რომელიც ავთენტიფიკაციამდე ხელმისაწვდომია, ამიტომ login და რეგისტრაციის ლიმიტები მას უნდა იყენებდეს. ის საერთო მისამართებს სჯის: ოფისს, სკოლას, მობილურ ოპერატორს CGNAT-ის უკან და მთელი ქვეყნის ტრაფიკს ზოგიერთი კორპორატიული proxy-ის უკან. რიცხვები გულუხვად დატოვე და მოთხოვნების ნაცვლად წარუმატებლობები დათვალე.
- IPv6-ს პრეფიქსი სჭირდება და არა მისამართი. ერთ მომხმარებელს ჩვეულებრივ
/64ეძლევა, ხშირად/56ან/48./128-ზე ლიმიტი ნიშნავს, რომ ერთი კავშირის მქონე თავდამსხმელს პრაქტიკულად უსაზღვრო გასაღებები აქვს. hash-მდე შეკვეცე/64-მდე. - მომხმარებლის ან ანგარიშის ID სამართლიანი და ზუსტია, მაგრამ მხოლოდ ავთენტიფიკაციის შემდეგ არსებობს. გამოიყენე ყველაფერზე, რაც login-ის უკანაა.
- API გასაღები API-სთვის სწორი ერთეულია, რადგან ის ასევე ის ერთეულია, რომელსაც ანგარიშს უწერ და რომელსაც გააუქმებ.
- კომბინაციები. login საუკეთესოდ სამ მთვლელზე ერთდროულად იზღუდება: IP-ზე, username-ზე და IP-username წყვილზე. წყვილი ჩვეულებრივ თავდამსხმელს იჭერს, username-ის მთვლელი - ერთ ანგარიშზე განაწილებულ თავდასხმას, IP-ის მთვლელი კი - ბევრ ანგარიშზე სკანირებას.
უკუპროქსი ის ადგილია, სადაც rate limiting ჩუმად უსარგებლო ხდება. თუ შენი აპლიკაცია proxy-ს, load balancer-ს ან CDN-ს უკან დგას, კავშირის მისამართი, რომელსაც ხედავს, proxy-ს მისამართია, ამიტომ მსოფლიოს ყველა ვიზიტორი ერთ გასაღებს იზიარებს. ნამდვილი მისამართი X-Forwarded-For-ში მოდის და შენს framework-ს უნდა უთხრა, რომ წაიკითხოს. იგივე header ყველაზე იოლად გასაყალბებელია, ამიტომ მას მხოლოდ მაშინ უნდა ენდო, როცა კავშირი შენს მიერ მართული proxy-დან მოვიდა.
// Express: trust exactly one proxy hop, not "true"app.set("trust proxy", 1);# nginx: replace $remote_addr with the last untrusted hopset_real_ip_from 10.0.0.0/8;real_ip_header X-Forwarded-For;real_ip_recursive on;RE:NODE-ზე app და web გეგმები proxy slot-ს შეიცავს და კლიენტის ნამდვილი მისამართი X-Forwarded-For-ში მოდის - ამიტომ ორივე ზემოთ მოცემული პარამეტრი ყველაფერზე მუშაობს, რასაც იქ განათავსებ. რას აკეთებს reverse proxy ხსნის დანარჩენ header-ებს, რომლებიც გზაზე იცვლება.
429-ის სწორად დაბრუნება#
კავშირის გაწყვეტა ყველაზე ცუდი პასუხია. კარგად მოქცეული კლიენტი ლიმიტს გათიშვისგან ვერ არჩევს და მაშინვე იმეორებს; ცუდად მოქცეული ისედაც გაიმეორებდა; შენ კი არ გაქვს ჟურნალის ხაზი, რომელიც მიზეზს იტყოდა.
სწორი პასუხია 429 Too Many Requests Retry-After header-ით, მოკლე სხეულით, რომელსაც კლიენტი გააანალიზებს, და ჟურნალის ჩანაწერით. Retry-After იღებს ან წამების რაოდენობას, ან HTTP თარიღს.
HTTP/1.1 429 Too Many RequestsRetry-After: 30Content-Type: application/json{"error":"rate_limited","retry_after":30}კიდევ ორი რამ, რაც გასწორებას ღირს:
- უთხარი კლიენტებს, სად არიან, სანამ კედელს მიადგებიან. დიდი ხნის კონვენციაა
X-RateLimit-Limit,X-RateLimit-RemainingდაX-RateLimit-Resetყოველ პასუხზე და არა მხოლოდ უარყოფილებზე. IETF-ის draft იმავე იდეასRateLimit-*სახელებით ამკვიდრებს, მაგრამ მისი ფორმა არაერთხელ შეიცვალა, ამიტომ აირჩიე ერთი ნაკრები, დააფიქსირე დოკუმენტაციაში და გაფრთხილების გარეშე ნუ შეცვლი. - დაამატე jitter reset-ს. თუ ათასს კლიენტს ზუსტად ოცდაათ წამში გაიმეორების ეტყვი, ყველა ერთ მილიწამში დაბრუნდება. გაგზავნილი მნიშვნელობა თითო კლიენტზე რამდენიმე პროცენტით შეცვალე და შენს კლიენტებს უთხარი, რომ ექსპონენციალურად და jitter-ით უკან დაიხიონ.
გამოიყენე 429, როცა კლიენტია დამნაშავე, და 503 Service Unavailable, როცა შენ ხარ. განსხვავება მნიშვნელოვანია, რადგან მონიტორინგი, კლიენტები და საძიებო სისტემების crawler-ები მათ სხვადასხვანაირად ეპყრობიან. 429 login endpoint-ზე შენი სისტემის მუშაობაა; 503 - მისი მარცხი.
Rate limiting nginx-ში#
თუ შენი აპლიკაციის წინ ვებ სერვერი დგას, სტეკში ყველაზე იაფი ლიმიტი ის არის, რომელიც შენს კოდამდე არასოდეს აღწევს. nginx leaky bucket-ს limit_req-ში ახორციელებს, ხოლო პირდაპირ კავშირების ზღვარს - limit_conn-ში.
# Shared memory zones live in the http block. 1 MB holds roughly# 16,000 states keyed by $binary_remote_addr.limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;limit_conn_zone $binary_remote_addr zone=conns:10m;server { limit_req_status 429; limit_conn_status 429; location / { limit_req zone=general burst=20 nodelay; limit_conn conns 20; } location = /login { limit_req zone=login burst=5; }}რაც ხალხს აბნევს:
- უარყოფის ნაგულისხმევი სტატუსი
503-ია და არა429. დააყენეlimit_req_status 429, თორემ მონიტორინგი გეტყვის, რომ საიტი გათიშულია ყოველ ჯერზე, როცა ვინმე scraper-ს გაუშვებს. rate=10r/sმილიწამების სიზუსტით მოქმედებს: ეს ნიშნავს ერთ მოთხოვნას ყოველ 100 ms-ში და არა ათს ყოველი წამის დასაწყისში.burst-ის გარეშე მეთერთმეტე მოთხოვნა წამში უარყოფილია, მიუხედავად იმისა, რომ წამი ჯერ არ დასრულებულა.burst=20ლიმიტს ზემოთ ოც რიგში მდგომ მოთხოვნას უშვებს.nodelay-ის გარეშე ისინი ნორმას მისადაგებით ყოვნდება, რაც login ფორმაზე გინდა.nodelay-ით ისინი მაშინვე მოიშვება და მხოლოდ შემდეგ ითვლება, რაც API-ზე გინდა.- უარყოფები ნაგულისხმევად
errorდონეზე იწერება. დააწიეlimit_req_log_level notice-ით, თუ ხმაური რეალურ შეცდომებს მალავს, მაგრამ არ გამორთო - ჟურნალი გეუბნება, რომელი გასაღები იქცევა ცუდად. ჟურნალები, რომლებიც ღირს შენახვა ხსნის, რა უნდა გააკეთო მათთან მოგვიანებით. limit_connსხვა იარაღია სხვა ბოროტად გამოყენებისთვის: კლიენტი, რომელიც ასობით ნელ კავშირს ხსნის და ინარჩუნებს. ერთ მისამართზე ოცი ბრაუზერისთვის სავსებით საკმარისია.
Rate limiting შენს აპლიკაციაში#
ყველაფერი, რაც მომხმარებლებზე, ანგარიშებზე ან წარუმატებლობებზე უნდა იცოდეს, აპლიკაციაში უნდა იყოს, სადაც მდგომარეობაა.
// Express, with express-rate-limitimport rateLimit from "express-rate-limit";const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 5, // called max in older versions skipSuccessfulRequests: true, // count failures, not attempts standardHeaders: true, legacyHeaders: false,});app.post("/login", loginLimiter, handler);# Django REST framework: rates in settings, throttles per viewREST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "rest_framework.throttling.UserRateThrottle", ], "DEFAULT_THROTTLE_RATES": {"anon": "60/minute", "user": "600/hour"},}დეტალი, რომელიც წყვეტს, იმუშავებს თუ არა ეს ყველაფერი, არის ის, სად ცხოვრობს მთვლელი. ამ ბიბლიოთეკების ყველა ნაგულისხმევად პროცესის შიდა საცავს იყენებს, რაც ნიშნავს, რომ ყოველი worker თავის საკუთარ ანგარიშს ინახავს. ოთხი Node პროცესი ხუთის ლიმიტით ოცის ლიმიტია, ხოლო გადატვირთვა მას ნულავს. ერთ სერვერზე ერთი პროცესისთვის ეს კარგია და არაფერი უნდა დაამატო. ერთზე მეტი პროცესისთვის მთვლელი გაზიარებული უნდა იყოს - სტანდარტული პასუხია Redis INCR ვადით, ხოლო Redis და გჭირდება თუ არა ის ჯერ ხსნის, როგორ გაუშვა ის მეორე პრობლემის ყიდვის გარეშე. შენი არსებული ბაზაც გამოდგება, ყოველ მოთხოვნაზე ერთი ჩაწერის ფასად.
კონკრეტულად login მთვლელისთვის უფრო მარტივი ვარიანტიც არსებობს: შეინახე failed_attempts და locked_until მომხმარებლის რიგში, რომელსაც პაროლის შესამოწმებლად ისედაც კითხულობ. არც ახალი სერვისი, არც ახალი მარცხის რეჟიმი, და გადატვირთვას გადაიტანს.
ბოროტად გამოყენება, რომელიც HTTP არ არის#
გეიმ სერვერები იგივე მოპყრობას სხვა მხრიდან იღებენ და უმეტესობა ისეთი არ არის, რისთვისაც კოდს წერ.
- Query პორტები. პატარა UDP პორტი თამაშის პორტის გვერდით პასუხობს „რომელი რუკა, რამდენი მოთამაშე“ ნებისმიერს, ვინც კითხულობს. რადგან პასუხი კითხვაზე დიდია, query პროტოკოლები reflection თავდასხმებისთვის გამოიყენება, შენი სერვერი გამაძლიერებლად, ვიღაც სხვა კი მსხვერპლად. Source-engine სერვერები ახლა მიმდინარე ვერსიებზე
A2S_INFO-ს პასუხამდე challenge-ს ელიან. პრაქტიკული რჩევაა - გაუშვი მიმდინარე ვერსიები და ნუ გამოყოფ query პორტს, რომელსაც არ იყენებ; გეიმ სერვერის პორტები ახსნილი ჩამოთვლის, რომელ თამაშს რა სჭირდება. - Join flood-ები. ბოტები, რომლებიც ციკლში უკავშირდებიან და წყდებიან, ავთენტიფიკაციის გზაზე რეალურ CPU-ს ხარჯავენ. Minecraft-ს ჩაშენებული packet limiter აქვს:
rate-limitserver.properties-ში ადგენს, წამში რამდენი პაკეტი შეუძლია გაგზავნოს ერთ კავშირს გაძევებამდე, ხოლო0მას ითიშავს. Paper ცალკე spam limiter-ებს ამატებს tab completion-ისა და რეცეპტების მოთხოვნებისთვის. - RCON. უმეტეს იმპლემენტაციას არც ლიმიტი აქვს და არც დაბლოკვა, რაც პაროლს ერთადერთ რამედ ხდის თავდამსხმელსა და შენს კონსოლს შორის. ნუ გამოაჩენ მას ინტერნეტში და სხვა რამის გაკეთებამდე წაიკითხე RCON-ის უსაფრთხოდ გამოყენება.
- SSH და ადმინ პანელები. სწორედ ამისთვისაა
fail2ban: ის ჟურნალს უყურებს და M წუთში N წარუმატებლობის შემდეგ მისამართს firewall-ზე ბლოკავს. ეს rate limiter-ია, რომელიც iptables წესებით არის დაწერილი. fail2ban-ის გზამკვლევი ფარავს jail-ებს, რომლებიც ღირს ჩართვა, ხოლო firewall წესები, რომლებიც მნიშვნელოვანია - იმას, რა უნდა იყოს საერთოდ ხელმისაწვდომი.
RE:NODE-ის პანელის მხარეც იმავენაირადაა შეზღუდული: login-ის წინ captcha და rate limit-ებია, პაროლები bcrypt hash-ებად ინახება, API გასაღებები შეიძლება კონკრეტულ მისამართებზე შეიზღუდოს, და ყოველი სესია ჩამოთვლილია, რომ დანარჩენები გამოგიშვა. ორფაქტორიანი ავთენტიფიკაციის ჩართვა შენი ანგარიშისთვის მთელ კატეგორიას აქრობს.
რას ვერ აკეთებს rate limit#
ჭერის გულწრფელად აღიარება ლიმიტის ქონის აზრია.
- Volumetric flood-ები. თუ uplink სავსეა, შენი limiter არასოდეს გაეშვება, რადგან პაკეტები, რომლებიც მოთხოვნებს ატარებს, არ მოდის. ეს upstream-ის პრობლემაა და upstream უნდა გადაწყდეს. RE:NODE-ზე ფორმულირება განზრახ ვიწროა: upstream filtering, რომელიც აშკარა volumetric flood-ებს, reflection-ს და დეფექტურ ტრაფიკს იცილებს. თავდასხმებს, რომლებიც ნამდვილ მოთამაშეებს ჰგავს, არ ფილტრავს, რადგან ვერაფერი განასხვავებს მათ ისე, რომ ნამდვილი მოთამაშეებიც არ გააგდოს. რას ვაკეთებთ თავდასხმების წინააღმდეგ და DDoS თავდასხმები გეიმ სერვერებზე ახსნილი განიხილავს, რა არის და რა არ არის აქ შესაძლებელი.
- განაწილებული ნელი ბოროტად გამოყენება. ათი ათასი მისამართი, რომელიც წამში თითო მოთხოვნას აგზავნის, per-IP limiter-ისთვის დაკავებული შუადღეა, შენი ბაზისთვის კი - გათიშვა. მის წინააღმდეგ დაცვა ქცევით სიგნალებს მოითხოვს და არა მთვლელებს.
- ნელი endpoint-ის სწრაფად ქცევა. ლიმიტი ხელს უშლის ნელ endpoint-ს, რომ ყველაფერი თან წაიღოს. ის ვერ ხდის მას სწრაფს, და მისი ინდექსის ნაცვლად გამოყენება ისეთი გზაა, რომლითაც ორივე პრობლემა გრჩება.
- საკუთარი თავისგან დაცვა. შენი cron ამოცანები, განმეორების ციკლები და health check-ები შენს დაწერილ ყველა ლიმიტს უვლის გვერდს, რადგან ისინი შიგნით არიან. ისინი ცალკე გაითვალისწინე.
კიდევ ერთი ღირს ხსენებად - თავად limiter-ის ფასი. შემოწმება, რომელიც გაზიარებულ საცავთან ქსელურ მოგზაურობას ჯდება, ამ მოგზაურობას ყოველ მოთხოვნას ამატებს, მათ შორის დიდ უმრავლესობას, რომელიც კარგია. სწრაფი გზა ლოკალურად დატოვე, სადაც შეგიძლია - პროცესის შიდა token bucket ფართო უკანა ზღუდისთვის, გაზიარებული საცავი მხოლოდ იმ endpoint-ებისთვის, სადაც სიზუსტე მნიშვნელოვანია.
რიცხვების შერჩევა და მათი დამტკიცება#
ორჯერ ნუ გამოიცნობ. გაზომე ერთხელ და ლიმიტი იმაზე მაღლა დააყენე, რასაც ნამდვილი მომხმარებლები აკეთებენ.
- ნახე, რა ხდება ახლა. აიღე ერთი კვირის access ჟურნალი და იპოვე მოთხოვნების 99-ე პერცენტილი წუთში ერთ მისამართზე, ასევე ყველაზე დაკავებული ლეგიტიმური კლიენტი, რომლის იდენტიფიცირებაც შეგიძლია. შენი ლიმიტი ამ რიცხვზე კომფორტულად მაღლა უნდა იყოს.
- დაიწყე observe რეჟიმით. ჩაწერე, რა იქნებოდა უარყოფილი, უარყოფის გარეშე. გაუშვი რამდენიმე დღე. თითქმის ყოველი პირველი მცდელობა რაღაცას იჭერს, რასაც არ ელოდი - მობილურ აპს, რომელიც poll-ს აკეთებს, პარტნიორულ ინტეგრაციას, შენს საკუთარ მონიტორინგს.
- შემდეგ ამოქმედე და უყურე უარყოფებს.
429-ების სტაბილური წვეთოვანი ნაკადი ჯანმრთელია. მკვეთრი ზრდა ან თავდასხმაა, ან კლიენტი, რომელიც ახლახან გატეხე, და ჟურნალის ხაზი გეუბნება, რომელია, რადგან გასაღებს შეიცავს. - გამიზნულად გატესტე. გააგზავნე ლიმიტს პლუს ერთი მოთხოვნა სატესტო მისამართიდან და დარწმუნდი, რომ მიიღებ
429-ს,Retry-After-ს და ჟურნალის ჩანაწერს. მეორე მისამართიდან დარწმუნდი, რომ გლობალურად შეზღუდული არ ხარ, რაც არასწორად დაყენებული proxy header-ის კლასიკური სიმპტომია. - გააფრთხილე თანაფარდობაზე და არა რაოდენობაზე. უარყოფები მოთხოვნების წილად რიცხვია, რომელიც ტრაფიკის ყველა დონეზე ერთსა და იმავეს ნიშნავს. მონიტორინგი, რომელიც რამეს გეუბნება სწორედ ისეთი მეტრიკების შერჩევაზეა, რომლებიც ასე იქცევიან.
გადახედე რიცხვებს, როცა ტრაფიკის ფორმა იცვლება - გაშვება, ახალი მობილური კლიენტი, საინტეგრაციო პარტნიორი - და ჩაწერე, რატომ არის ყოველი ლიმიტი ისეთი, როგორიც არის. ლიმიტს, რომელსაც მიზეზი არ აწერია, ის გააორმაგებს, ვინც შემდეგი ნახავს 429-ს ჟურნალში.
FAQ#
რომელი სტატუს კოდი უნდა დააბრუნოს შეზღუდულმა მოთხოვნამ?
429 Too Many Requests, Retry-After header-ით, რომელიც გეუბნება, რამდენი უნდა დაელოდო. 503 გამოიყენე მხოლოდ მაშინ, როცა შეცდომა შენია. nginx limit_req-ზე ნაგულისხმევად 503-ს აბრუნებს, ამიტომ პირდაპირ დააყენე limit_req_status 429.
უნდა შევზღუდო IP მისამართით თუ მომხმარებლით?
მომხმარებლით, სადაც მომხმარებელი არსებობს, რადგან ეს სამართლიანი და ზუსტია. IP-ით, სადაც არ არსებობს - login, რეგისტრაცია, საჯარო endpoint-ები - გულუხვი რიცხვებით, რადგან მისამართები საერთოა. login-ისთვის გაუშვი ორივე, პლუს username-ის მთვლელი, და მოთხოვნების ნაცვლად წარუმატებლობები დათვალე.
რატომ ბლოკავს ჩემი rate limiter ყველას ერთდროულად?
თითქმის ყოველთვის იმიტომ, რომ შენი აპლიკაცია proxy-ს უკან დგას და ყოველ მოთხოვნაზე proxy-ს მისამართს ხედავს, ამიტომ ყველა ვიზიტორი ერთ მთვლელს იზიარებს. დააყენე trusted proxy პარამეტრი შენს framework-ში და წაიკითხე X-Forwarded-For - მაგრამ ამ header-ს ენდე მხოლოდ იმ მისამართებიდან, რომლებსაც შენ აკონტროლებ.
ჩერდება თუ არა DDoS თავდასხმა rate limit-ებით?
არა. ლიმიტი იცავს მის უკან არსებულ სამუშაოს მას შემდეგ, რაც მოთხოვნა მოვიდა. volumetric flood კავშირს შენი კოდის გაშვებამდე ავსებს, ამიტომ ის upstream უნდა მოიხსნას. ლიმიტები აჩერებს ბოროტად გამოყენებას, რომელიც ნორმალური სიჩქარით მოდის: credential stuffing, scraping, სპამი და გაქცეული კლიენტები.
სად უნდა იყოს მთვლელი, თუ რამდენიმე პროცესს ვუშვებ?
სადმე, სადაც ყველა მათგანი ხედავს. Redis INCR-ით და ვადით ჩვეულებრივი არჩევანია; შენი არსებული ბაზა ყოველ მოთხოვნაზე ერთი ჩაწერის ფასად გამოდგება. პროცესის შიდა მთვლელი სწორია მხოლოდ ერთი პროცესისთვის, სხვა შემთხვევაში ის ჩუმად ამრავლებს შენს ლიმიტს worker-ების რაოდენობაზე.
როგორ შევზღუდო გეიმ სერვერი?
უმეტესად არ ზღუდავ, კოდში. თავს არიდებ query და RCON პორტების გამოჩენას, რომლებიც არ გჭირდება, სერვერის build-ს განახლებულს ინახავ, რომ მისი საკუთარი დაცვა მოქმედებდეს, იყენებ თამაშის საკუთარ packet limiter-ს, სადაც აქვს, და flood-ებისთვის upstream filtering-ს ეყრდნობი. აპლიკაციის სტილის ლიმიტები სერვერის გარშემო არსებულ ვებ ნაწილებს ეკუთვნის და არა თამაშის პროტოკოლს.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.