I am posting this question using an automatic translation.
Please forgive any grammatical errors.
I have built an application using the .NET framework and the ASP.net Web API.
I have split the virtual path for each customer region within a site running on IIS and copied the same binary to run as separate applications.
The applications run in the same application pool.
Recently, some customers have been making a very large number of requests in a matter of minutes.
(I suspect a glitch in the system on the customer's end).
I am thinking of adding a static class to my current application that keeps track of the number of requests per customer in a given time period and blocks them if the threshold is exceeded.
From past StackOverFlow articles I have found that "information in the static class is lost if the application pool is recycled", but I have determined that this is not a problem in this case.
For my purposes, I only need to be able to retain information for a few minutes.
However, I still have a few questions that I can't find answers to, so I'd like to ask you all a few questions.
Even if the same binary is running in the same application pool, will the static class information be kept separately for different applications?
Will the static constructor of a static class be executed even after the application pool is recycled?
Is there a problem if I reference a field in Global.asax from within a static class?
Is there a problem with referencing the contents of web.config from within a static class?
Attached below is the source of my experimental implementation.
I plan to call the static method "ExcessiveRequestCheck.isExcessiveRequest" of this static class after the Web API receives the request and identifies the user ID.
Any advice would be sincerely appreciated.
P.S.
I understand that this approach does not work well in a load balancing environment. Currently my system only runs on one virtual machine. If you are moving to the cloud or deploying a load balancer, you will probably need a different approach than this one.
public static class ExcessiveRequestCheck
{
private static Dictionary<string, ExcessiveRequestInfo> dicExcessiveRequestCheckInfo = new Dictionary<string, ExcessiveRequestInfo>();
private static object initLock = new object();
private static object dicExcessiveRequestCheckInfoLock = new object();
//If possible, I want this process to be a static constructor
public static Dictionary<int, int> dicExcessiveRequestSkipConditions
{
get
{
lock (initLock)
{
if (ExcessiveRequestCheck._dicExcessiveRequestSkipConditions == null)
{
//if possible, I want to set this value from Web.config.
ExcessiveRequestCheck._dicExcessiveRequestSkipConditions = new Dictionary<int, int>() {
{ 5, 3 }, { 15, 5 }, { 45, 10 }, { 120, 20 }
};
}
return ExcessiveRequestCheck._dicExcessiveRequestSkipConditions;
}
}
}
private static Dictionary<int, int> _dicExcessiveRequestSkipConditions = null;
public const int BUFFER_CLEAR_MINUTES = 5;
public static bool isExcessiveRequest(string userId)
{
ExcessiveRequestCheck.refreshExcessiveRequestCheckInfo();
lock (ExcessiveRequestCheck.dicExcessiveRequestCheckInfoLock)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.ContainsKey(userId) == false)
{
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Add(userId, new ExcessiveRequestInfo() { countRequest = 1 });
return false;
}
bool doSkip = false;
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].countRequest++;
foreach (KeyValuePair<int, int> pair in ExcessiveRequestCheck.dicExcessiveRequestSkipConditions)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].lastRequesttTime.AddSeconds(pair.Key) > DateTime.Now)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].countRequest > pair.Value)
{
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].wasRequestSkip = true;
doSkip = true;
}
}
}
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].lastRequesttTime = DateTime.Now;
return doSkip;
}
}
public static void refreshExcessiveRequestCheckInfo()
{
lock (ExcessiveRequestCheck.dicExcessiveRequestCheckInfoLock)
{
var keyList = ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Keys;
foreach (string key in keyList)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.ContainsKey(key))
{
var value = ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[key];
if (value.lastRequesttTime.AddMinutes(BUFFER_CLEAR_MINUTES) < DateTime.Now)
{
if (value.wasRequestSkip)
{
//this NLog instance was created in Global.asax.cs
WebApiApplication.logger.Fatal("skip request! user id=" + key);
}
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Remove(key);
}
}
}
}
}
}
class ExcessiveRequestInfo
{
public DateTime requestStartTime { get; set; } = DateTime.Now;
public DateTime lastRequesttTime { get; set; } = DateTime.Now;
public int countRequest { get; set; } = 0;
public bool wasRequestSkip { get; set; } = false;
}
Your questions
Even if the same binary is running in the same application pool, will the static class information be kept separately for different applications?
Yes, they are separate
Will the static constructor of a static class be executed even after the application pool is recycled?
Yes, the static constructor is guaranteed to be called before any of the static methods are executed
Is there a problem if I reference a field in Global.asax from within a static class?
No more than accessing it from anywhere else
Is there a problem with referencing the contents of web.config from within a static class?
No more than accessing it from anywhere else
Your general approach
DoS
If you're trying to mitigate a denial-of-service attack or credential stuffing attack, your approach probably won't work, since requests to your service will still result in load being added to your server, and if they are performing a credential stuffing attack, it'll fill up your dictionary with millions of entries and possibly cause your application to crash.
If you want to mitigate a denial-of-service attack effectively, you will probably need a more network-oriented solution, such as a smart firewall or a WAF.
Rate limiting
If on the other hand you are attempting to throttle specific users' activities (i.e. rate limiting), again, your approach probably isn't the greatest, because it does not support load balancing-- your list is held in in-process memory. For per-user rate limiting you will probably need to track user activity in a central data store accessible to all of your servers.
Static constructors
As a general rule, you should try to avoid static constructors, or keep them very simple, as a failure in a static constructor will cause your entire application to fail to start. Be careful!
even if the same binary is running in the same application pool, will the static class information be kept separately for different applications?
If by different applications, you mean separate web sites? yes, it will be kept separate to each web site you have running for that app pool.
Will the static constructor of a static class be executed even after the application pool is recycled?
Hum, that's a bit confusing. The constructor will only be executed if you call the class and that given constructor. Since there is never a instance of the class created, then the "initialize/new" event is never used nor triggered. So, any method with parameters will run and work fine - including the constructor. I would suggest that there is not some "event" that gets triggered on first use - it would not and does not make sense in the context of a static class, since you never create an instance. So, if you have some methods with parameters then fine.
So, constructor in the context of new instance of the class makes no sense - (did not even think that is possible with static).
There is no concept of "new" event that triggers, so I fail to see how this issue can ever matter.
Is there a problem if I reference a field in Global.asax from within a static class?
Well, values in that class are global to ALL users. But, those values can go out of scope just about any old time you please. As a result, ZERO use of public members is practial. While a app-pool re-start will re-set those class values? They can go out of scope just about any old time. They are global to all and every user. So, persisting values, or attempting to persit values in a static class is NOT a viable choice for production code. You can have methods (code) in that class, but any public persisting values really can't be relied upon to persist correctly. I'm not 100% sure, but even just general .net garbage collection would likely cause a re-set.
If you need this information to persist, then you can't use static, you have to create a instance of that class and persist it in session(). And session is per user.
A static class public values will apply to EVERY user - not just the current user. In effect those values are global to all users - but without any real ccontrol or garrutee that the values will persit - you have no control over this and thus you can't adopt this concpet and design for any system of practial value.
Is there a problem with referencing the contents of web.config from within a static class?
Reading values? No problem. Update or modify values? - a MASSIVE different issue. You modify web.config, that will trigger a app pool restart.
So, you free to read any file - text files, xml or whatever, and that includes web.config. As long as you not modify such files, then no problems.
The main issue here?
It simple not practical to assume, or build a design in which public static class values are to persist. The ZERO control you have when such values may go out of scope is somthing you have ZERO control over, and thus such designs can't use nor rely on values persisting.
And of course on many web hosting systems? They are now adopting cloud computing. This means from one post back to the next, you might be using a different server, and again, that means such values can't persist in memory, since from one post-back to the next, or one web service call to the next? You may well be hitting a different server anyway (and they don't share memory) (so, this suggests say using SQL server based sessions, or at the very least persisting such values in a database).
In fact, if you need such persisting values and data? Then use a database. The WHOLE idea of web based software is you do NOT have state between post-backs. And you are attempting to go even down a worse road, but hope on a wing and a prayer that some global values "might" and "sort of" and "maybe" will persist between calls to the web site.
Answer:
You really can't do this with any realm of reliably.
So, most of your questions don't really matter. What matters is these values are to persist, and you can't rely on such a design. If you need some persisting values, then you have to adopt a system and design that supports that concept (viewstate, cookies, or session()).
Now, I suppose you can give this a try, and then come back with a detailed report and how your experience turned out. But, there are too many pit falls, and without any code or system control over persisting values in memory, I don't think I would go down this road.
In web land, it makes next to no sense to have public variables that you attempt to persist in a static class. You can have code, you can have cool methods, you can use session(). But, the concept of persisting values in static class is a design choice that does not make sense, and can't be relied upon.
Web software is assumed to be state-less, and that VERY much is the assumption you have to make in regards to a static class, or in fact general use of such code.
I created a class for authentication of my project.
I want to use it in my website and also in mobile application but why this class has a static constructor and should be get instance one time, it didn't work well, I mean I want to get instance for each application once.
I want to know how fix it?
It sounds to me like you're looking for a singleton pattern.
"I mean I want to get instance for each application once."
I assume you mean that you want to construct an instance once per application.
The easiest implementation is to use this:
public class MyAuth
{
private static readonly MyAuth instance = new MyAuth();
public static MyAuth Instance { get { return instance; } }
private MyAuth()
{
// initialization goes here and will be called once
}
// Members.
}
So first off, I'm not sure if this is a good solution. Authorization is usually updated from different applications, so it might be better to flush the authentication once in a while.
Second, note that member functions can be called from multiple threads. If you want to do it like this, you have to use locking for fields that you use (either directly or indirectly). Be sure to know what you're doing.
There is an alternative solution that solves all this in a neat way (and a ton of other issues). It's called a database; you might want to consider using that.
I have a simple web site built with asp.net. It typically only has 1 or 2 users at one time. My question is, is it ok to instantiate a class at the class level or should I be instantiating for each method. Here is an example. I have a class named Host with a name field and mac field. In my code behind for a specific page Is it ok to do this:
public partial class addhosts : Page
{
private Host host = new Host();
private HostDal dal = new HostDal();
protected void myMethod()
{
host.Name = "myname"
host.Mac = "mymac"
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
dal.AddHost(host)
}
}
First, what you are referring to are more typically referred to as global versus local variables.
In the simple case that you have listed, it would be better to create the variable on the submit click. The reason is if a user loads the object, but never calls the submit click, then you have instantiated the host object in memory when there was no need.
However, as many have said, it should not really matter one way or the other here. But, again, this is a simplistic example. Global variables can be dangerous and are often avoided as they can be modified from anywhere in your class. If one method expects a certain value that is then overrode, this can cause difficult to debug issues in more complex examples
Here is a wikipedia article that reiterates my above point:
They are usually considered bad practice precisely because of their
non-locality: a global variable can potentially be modified from
anywhere (unless they reside in protected memory or are otherwise
rendered read-only), and any part of the program may depend on it
To get rid of the globals, you could do this (using object initializers)
protected void btnSubmit_Click(object sender, EventArgs e)
{
var host = new Host
{
Name = "myname",
Mac = "mymac"
};
dal.AddHost(host)
}
It's completely OK to have user specific data as fields inside an ASP.Net Page instance. Every visit to a page creates a new instance of the Page class hence you'll not end up in a situation where data is incorrectly shared between users
It shouldn't matter.
Each request to your page is separate and has no knowledge of other requests therefore there is no chance of there being a "conflict" with other requests.
It's perfectly fine, since for each user new class is created (ASP.NET by design). It's in separate thread also, so even static variables would be acceptable in this scenario.
Cheers, Ivan
I want to store something in the Session object (i.e. a user ID), before anything else happens in a Page's Life Cycle.
I tried putting it in the Page's constructor, but the Session object isn't yet available then.
I tried overriding OnInit, but then other delegates hooked to the Init event might still be executed prior.
I'm really lost, even though this feels like very basic ASP.NET theory.
A more usual approach is to encapsulate access to the data in Session, and load it on demand the first time it's accessed. The basic pattern looks like the following:
public string UserId
{
get
{
string s = Session["UserId"];
if (s == null)
{
s = ... Get UserID from somewhere, e.g. database
Session["UserId"] = s;
}
return s;
}
}
In this way the data will be loaded the first time it's needed, and you don't need to care when that is in the page lifecycle.
You can override the OnInit() method, implement your code, and add a final call to base.OnInit() which raises the registered Init events.
A instance of a class is created in the partial class of an aspx page.Under page_load or button click method I'm trying to set the value to the class. but when each postback takes place new instance is created and I'm losing the previous value.
public partial class DatabaseSelection : System.Web.UI.Page
{
DBProperties dbpro;
Metadata obmeta;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dbpro = new DBProperties();
}
If you need this instance by application throw it in the Application or use a Singleton collection:
Application["Foo"] = new MyClass();
See other answer.
If you need this for a single request (which seems unlikely here):
HttpContext.Current.Items["Foo"] = new MyClass();
If you need this across requests the the following are all options depending on your scenario:
Serialize into Cookie (will be transfered on every request so if the pipeline is an issue don't use this). Good for per user data.
Store in Session: Session["Foo"] = new MyClass(); // I personally don't like this option because it tends to grow your memory pressure as your user base grows, but if this is small then this is a good option as it does not increase bandwidth consumption and performance. Good for per user storage. And persistence to DB or memory (and others) can be configured.
Store in Cache. Benefits are good control over lifetime and some flexibility on whether per user depending on what keys you use.
Store in ViewState. Good across a single request, scalable, but increases payload.
Store in hidden var (about the same as ViewState)
I am not really sure I would recommend the Singleton pattern. Technically singletons will stick around as long as your AppDomain effectively being similar to the Application variable.
Are you looking for Singleton pattern?
Just create a method that accesses the session state and tries to return it if it is already there. If not, it creates it, stores it in the session, and returns it.
If you want the object to persist between post backs you'll need to store it in either Session/View State or persist it to a database.