- Notifications
You must be signed in to change notification settings - Fork75
ASP.NET MVC Throttling filter
License
stefanprodan/MvcThrottle
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
With MvcThrottle you can protect your site from aggressive crawlers, scraping tools or unwanted traffic spikes originated from the same location by limiting the rate of requests that a client from the same IP can make to your site or to specific routes.
You can set multiple limits for different scenarios like allowing an IP to make a maximum number of calls per second, per minute, per hour or per day. You can define these limits to address all requests made to your website or you can scope the limits to each Controller, Action or URL, with or without query string params.
The setup bellow will limit the number of requests originated from the same IP.If from the same IP, in same second, you'll make a call tohome/index andhome/about the last call will get blocked.
publicclassFilterConfig{publicstaticvoidRegisterGlobalFilters(GlobalFilterCollectionfilters){varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:1,perMinute:10,perHour:60*10,perDay:600*10){IpThrottling=true},Repository=newCacheRepository()};filters.Add(throttleFilter);}}
In order to enable throttling you'll have to decorate your Controller or Action withEnableThrottlingAttribute, if you want to exclude a certain Action you can applyDisableThrottlingAttribute.
[EnableThrottling]publicclassHomeController:Controller{publicActionResultIndex(){returnView();}[DisableThrottling]publicActionResultAbout(){returnView();}}
You can define custom limits using the EnableThrottling attribute, these limits will override the default ones.
[EnableThrottling(PerSecond=2,PerMinute=10,PerHour=30,PerDay=300)]publicActionResultIndex(){returnView();}
If, from the same IP, in the same second, you'll make two calls tohome/index, the last call will get blocked.But if in the same second you callhome/about too, the request will go through because it's a different route.
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:1,perMinute:10,perHour:60*10,perDay:600*10){IpThrottling=true,EndpointThrottling=true,EndpointType=EndpointThrottlingType.ControllerAndAction},Repository=newCacheRepository()};
Using theThrottlePolicy.EndpointType property you can chose how the throttle key gets compose.
publicenumEndpointThrottlingType{AbsolutePath=1,PathAndQuery,ControllerAndAction,Controller}
By default, when a client is rate limited a 429 HTTP status code is sent back along withRetry-After header. If you want to return a custom view instead of IIS error page you’ll need to implement your own ThrottlingFilter and override theQuotaExceededResult method.
publicclassMvcThrottleCustomFilter:MvcThrottle.ThrottlingFilter{protectedoverrideActionResultQuotaExceededResult(RequestContextcontext,stringmessage,HttpStatusCoderesponseCode){varrateLimitedView=newViewResult{ViewName="RateLimited"};rateLimitedView.ViewData["Message"]=message;returnrateLimitedView;}}
I’ve created a view named RateLimited.cshtml located in the Views/Shared folder and using ViewBag.Message I am sending the error message to this view. Take a look at MvcThrottle.Demo project for the full implementation.
If requests are initiated from a white-listed IP or to a white-listed URL, then the throttling policy will not be applied and the requests will not get stored. The IP white-list supports IP v4 and v6 ranges like "192.168.0.0/24", "fe80::/10" and "192.168.0.0-192.168.0.255" for more information checkjsakamoto/ipaddressrange.
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:2,perMinute:60){IpThrottling=true,IpWhitelist=newList<string>{"::1","192.168.0.0/24"},EndpointThrottling=true,EndpointType=EndpointThrottlingType.ControllerAndAction,EndpointWhitelist=newList<string>{"Home/Index"},ClientThrottling=true,//white list authenticated usersClientWhitelist=newList<string>{"auth"}},Repository=newCacheRepository()});
The Demo project comes with a white-list of Google and Bing bot IPs, take at look atFilterConfig.cs.
You can define custom limits for known IPs and endpoint, these limits will override the default ones.Be aware that a custom limit will only work if you have defined a global counterpart.You can define endpoint rules by providing relative routes likeHome/Index or just a URL segment like/About/.The endpoint throttling engine will search for the expression you've provided in the absolute URI,if the expression is contained in the request route then the rule will be applied.If two or more rules match the same URI then the lower limit will be applied.
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:1,perMinute:20,perHour:200,perDay:1500){IpThrottling=true,IpRules=newDictionary<string,RateLimits>{{"192.168.1.1",newRateLimits{PerSecond=2}},{"192.168.2.0/24",newRateLimits{PerMinute=30,PerHour=30*60,PerDay=30*60*24}}},EndpointThrottling=true,EndpointType=EndpointThrottlingType.ControllerAndAction,EndpointRules=newDictionary<string,RateLimits>{{"Home/Index",newRateLimits{PerMinute=40,PerHour=400}},{"Home/About",newRateLimits{PerDay=2000}}}},Repository=newCacheRepository()});
You can define custom limits for known User-Agents or event white-list them, these limits will override the default ones.
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:5,perMinute:20,perHour:200,perDay:1500){IpThrottling=true,EndpointThrottling=true,EndpointType=EndpointThrottlingType.AbsolutePath,UserAgentThrottling=true,UserAgentWhitelist=newList<string>{"Googlebot","Mediapartners-Google","AdsBot-Google","Bingbot","YandexBot","DuckDuckBot"},UserAgentRules=newDictionary<string,RateLimits>{{"Slurp",newRateLimits{PerMinute=1}},{"Sogou",newRateLimits{PerHour=1}}}},Repository=newCacheRepository()});
The above setup will allow the Sogou bot to crawl each URL once every hour while Google, Bing, Yandex and DuckDuck will not get rate limited at all.Any other bot that is not present in the setup will be rate limited based on the global rules defined in the ThrottlePolicy constuctor.
By default, rejected calls are not added to the throttle counter. If a client makes 3 requests per secondand you've set a limit of one call per second, the minute, hour and day counters will only record the first call, the one that wasn't blocked.If you want rejected requests to count towards the other limits, you'll have to setStackBlockedRequests to true.
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:1,perMinute:30){IpThrottling=true,EndpointThrottling=true,StackBlockedRequests=true},Repository=newCacheRepository()});
MvcThrottle stores all request data in-memory using ASP.NET Cache. If you want to change the storage toVelocity, MemCache or Redis, all you have to do is create your own repository by implementing the IThrottleRepository interface.
publicinterfaceIThrottleRepository{boolAny(stringid);ThrottleCounter?FirstOrDefault(stringid);voidSave(stringid,ThrottleCounterthrottleCounter,TimeSpanexpirationTime);voidRemove(stringid);voidClear();}
If you want to log throttled requests you'll have to implement IThrottleLogger interface and provide it to the ThrottlingFilter.
publicinterfaceIThrottleLogger{voidLog(ThrottleLogEntryentry);}
Logging implementation example
publicclassMvcThrottleLogger:IThrottleLogger{publicvoidLog(ThrottleLogEntryentry){Debug.WriteLine("{0} Request {1} from {2} has been blocked, quota {3}/{4} exceeded by {5}",entry.LogDate,entry.RequestId,entry.ClientIp,entry.RateLimit,entry.RateLimitPeriod,entry.TotalRequests);}}
Logging usage example
varthrottleFilter=newThrottlingFilter{Policy=newThrottlePolicy(perSecond:1,perMinute:30){IpThrottling=true,EndpointThrottling=true},Repository=newCacheRepository(),Logger=newDebugThrottleLogger()});
About
ASP.NET MVC Throttling filter
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.