storage issue in cache - asp.net

I ahve implemented brute force protection via limitation of failed login counts as here: http://madskristensen.net/post/Brute-force-protect-your-website.aspx
But i'm encountering two issues:
After certain amount of time ( in my case 2 minutes) record in cache is not expired and i'm unable to log in again. This means that when function checks the number of failed attempts, it still gets maximum allowed after this 5 minutes
cache from MSDN as I understood is single storage for application. From what i see in my application, it seems like cache is per application per IP. Why?
Any suggestions? Here's my code:
int CountOfFailedLoginAttempts()
{
if(Cache["L1|"+TextBox1.Text]==null)
{
return 0;
}
return (int) Cache["L1|" + TextBox1.Text];
}
void AddFailedAttempt()
{
if(Cache["L1|"+TextBox1.Text]==null)
{
Cache.Insert("L1|"+TextBox1.Text,1,null,System.Web.Caching.Cache.NoAbsoluteExpiration,new TimeSpan(0,2,0));
}
else
{
int tries = (int) Cache["L1|" + TextBox1.Text];
Cache["L1|" + TextBox1.Text] = tries + 1;
}
}
void ClearFailedAttemptCounter()
{
Cache.Remove("L1|" + TextBox1.Text);
}
protected void Button1_Click(object sender, EventArgs e)
{
if (CountOfFailedLoginAttempts() >= 5)
{
Label1.Text = "Login will be unavailable for 2 minutes";
}
else
{
SqlConnection con =
new SqlConnection("valid connection string");
SqlCommand cmd = new SqlCommand("Select top 1 password from users WHERE UserName=#UN", con);
cmd.CommandTimeout = 600;
cmd.Parameters.Add(new SqlParameter("UN", TextBox1.Text));
con.Open();
string res = (string) cmd.ExecuteScalar();
con.Close();
if (res == TextBox2.Text)
{
FormsAuthentication.RedirectFromLoginPage(TextBox1.Text, true);
ClearFailedAttemptCounter();
}
else
{
Label1.Text = "Wrong password. "+(5-CountOfFailedLoginAttempts()).ToString()+"more attempts and access will be suspended for 2 minutes.";
AddFailedAttempt();
}
}
}
}

You're using sliding expiration (of 2 minutes), which means that your cache item will remain while someone is still reading the value within that time. This means that your account will be blocked forever if you keep retrying every minute.
The cache is a cache, not a critical data storage. You can not count on items remaining for two full minutes, memory pressure at the server may force ASP.NET to evict items from the cache. There's also possibilities of web farms/gardens that will give you several worker processes (perhaps spread over several machine) which will all have their own cache.

Thanks for answers. As it turned out, the problem is in this line:
Cache["L1|" + TextBox1.Text] = tries + 1;
The mechanism is quite diffrent than i thought. Instead of replacing the value, it is removing the value of specified key in cache, and inserting new one, BUT with no expiration settings. Because of that it seemed like value never expires. That is relevant for both Absolute and Sliding expiry modes. I have solved problem like that:
void AddFailedAttempt()
{
if(Cache["L1|"+TextBox1.Text]==null)
{
Cache.Insert("L1|"+TextBox1.Text,1,null,System.Web.Caching.Cache.NoAbsoluteExpiration,TimeSpan.FromMinutes(2));
}
else
{
int tries = (int) Cache["L1|" + TextBox1.Text];
Cache.Remove("L1" + TextBox1.Text);
Cache.Insert("L1|" + TextBox1.Text, tries+1, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(2));
}
}
And in that way everything works properly.

Related

Why (on a specific server) would ADO,NET get a Time Out exception after db result is returned and the reader is being closed?

I have some strange behavior occurring in an ASP.NET application that I am trying to fix.
I am getting the following error from code:
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
I am familiar with this error and many of it's causes. All of my typical avenues of trouble shooting have failed.
Here are some of the dynamics:
The server was recently re-built. So it could be a server configuration issue.
This error only happens on a specific web server. When I run the application it locally, and from other servers it is fast. I cannot re-pro perf issues with the proc in MSSMS from my machine.
This tells me that it is specific to this server.
When I run The same proc using the OSql command--line utility It works. Fast.
This indicates that it is likely something .NET related, NOT db related
Prior to this code executing, I have executed other Stored procedures on this server, and on this same DB.
This suggests the proc may be related , but is not a "server X cannot talk to server Y"
I have gotten confirmation From DB owners that the DB has received the command, executed (>1second )it and returned the data.
By tracing the code, I see the results are returned and it is not until I try to close the data reader that the error occurs.
In fact I see it takes 36 milliseconds to execute the stored procedure and iterate through the result.
It looks like the call to DataReader.Close is what is taking time, and eventually timing out.
I have increased the max Pool from 31 to 100.
Here is a sample of what my code looks like, how it is structured. I have been hacking at it for trouble shooting: I have added the explict close to ensure I know where the error occurs. There may be syntax issues: I have made it generic, and may have introduced bugs in doing so.
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
SqlDataReader dr= null;
try
{
dr = cmd.ExecuteReader();
s.Stop();
Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
s.Start();
if (dr != null)
{
if (dr.Read())
{
returnValue =
Conversion.DBNullToDouble(
dr[0]);
}
s.Stop();
Log.Log.WriteTrace("Timer", "dr.read done (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
}
}catch(Exception ex)
{
Log.Log.PersistException(ex);
}
//}
if(dr!=null && !dr.IsClosed)
dr.Close();// This times out
if (cn != null && cn.State !=ConnectionState.Closed)
cn.Close();
Log.Log.WriteTrace("DONE "),
;
}
}
return (returnValue);
}
UPDATE
dr.Close(); takes 2 minutes to execute. Just on this server. Locally it takes less than a second.
UPDATE
Per the accepted answer's comments: I have a proc that has multiple records. I am taking the fist one. Calling cmd.Cancel() has not fixed, but has drastically reduced the time taken to close the data reader. Exploring this should help me fix the problem. I do not know why this is only happening on this server, as the server is a dev server.
I see few problems in your code.
Try to use the Reader in a using statement it closes and disposes when finishing it.
You are closing 2 times the connection (when using
if (cn != null && cn.State !=ConnectionState.Closed)
cn.Close();
and in the using (SqlConnection cn = Connections.GetSqlConnection()) -- this one do it for you at the end of the statement
In your code you are not checking if the DataReader.HasRows So if your Sproc doesn't return a value it will throw an exception in the if (dr.Read()) so that could be the reason that's why you are getting sometimes the time out exception
If you only want to retrieve the value of the first column of the first row you should take a look to ExecuteScalar
Finally
I would rewrite your code like this (using DataReader)
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
using(SqlDataReader dr = cmd.ExecuteReader())
{
s.Stop();
Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
s.Start();
if (dr != null)
{
if(dr.HasRows)
{
if (dr.Read())
{
returnValue =
Conversion.DBNullToDouble(
dr[0]);
}
}
s.Stop();
Log.Log.WriteTrace("Timer", "dr.read done (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
}
}
}
}
return (returnValue);
}
Or (with ExecuteScalar)
public double GetMyData()
{
double returnValue;
// Used in logging to see if code was reached & how long it took.
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
using (SqlConnection cn = Connections.GetSqlConnection())
{
cn.Open();
using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
{
Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
s.Start();
try
{
returnValue = Conversion.DBNullToDouble(cmd.ExecuteScalar());
s.Stop();
}
catch(Exception ex)
{
Log.Log.PersistException(ex);
}
}
}
return (returnValue);
}

ASP.NET MySQL update multiple records

I have a web page that needs to update multiple records. This page gets all the information and then begins a transaction sending multiple UPDATE queries to the data base.
foreach row
{
Prepare the query
Hashtable Item = new Hashtable();
Item.Add("Id", Id);
Item.Add("Field1", Field1);
Item.Add("Field2", Field2);
Item.Add("Field3", Field3);
...
}
Then we launch the ytransaction
DO CHANGES()
public void execute_NonQuery_procedure_transaction(string StoredProcedure, List<Hashtable> Params)
{
using (MySqlConnection oConnection = new MySqlConnection(ConfigurationManager.AppSettings[DB]))
{
MySqlTransaction oTransaction;
bool HasErrors = false;
oConnection.Open();
oTransaction = oConnection.BeginTransaction();
try
{
MySqlCommand oCommand = new MySqlCommand(StoredProcedure, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
oCommand.Transaction = oTransaction;
foreach (Hashtable hParams in Params)
{
oCommand.Parameters.Clear();
IDictionaryEnumerator en = hParams.GetEnumerator();
while (en.MoveNext())
{
oCommand.Parameters.AddWithValue("_" + en.Key.ToString(), en.Value);
oCommand.Parameters["_" + en.Key.ToString()].Direction = ParameterDirection.Input;
}
oCommand.ExecuteNonQuery();
}
}
catch (Exception e)
{
HasErrors = true;
throw e;
}
finally
{
if (HasErrors)
oTransaction.Rollback();
else
oTransaction.Commit();
oConnection.Close();
}
}
}
Is there another way to do this or this is the most efficient way?
It depends on the situation, like if you have multiple row updates or adding new rows or deleting some rows or a combination of these, which modifies the database table then, the efficient way to do this is to have Batch Update...
Please go through this link Batch Update
Hope this helps...
it looks fine to me, you could eventually do not clear the Command.Parameters list but just assign the values on following iterations but probably this leads to no visible improvements.
pay attention your throw is wrong, in C# don't use throw e; but simply throw;.

ASP.net Cache Absolute Expiration not working

I am storing a single integer value in HttpContext.Cache with an absolute expiration time of 5 minutes from now. However, after waiting 6 minutes (or longer), the integer value is still in the Cache (i.e. it's never removed even though the absolute expiration has passed). Here is the code I am using:
public void UpdateCountFor(string remoteIp)
{
// only returns true the first time its run
// after that the value is still in the Cache
// even after the absolute expiration has passed
// so after that this keeps returning false
if (HttpContext.Current.Cache[remoteIp] == null)
{
// nothing for this ip in the cache so add the ip as a key with a value of 1
var expireDate = DateTime.Now.AddMinutes(5);
// I also tried:
// var expireDate = DateTime.UtcNow.AddMinutes(5);
// and that did not work either.
HttpContext.Current.Cache.Insert(remoteIp, 1, null, expireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
else
{
// increment the existing value
HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;
}
}
The first time I run UpdateCountFor("127.0.0.1") it inserts 1 into the cache with key "127.0.0.1" and an absolute expiration of 5 minutes from now as expected. Every subsequent run then increments the value in the cache. However, after waiting 10 minutes it continues to increment the value in the Cache. The value never expires and never gets removed from the Cache. Why is that?
It's my understanding that an absolute expiration time means the item will get removed approximately at that time. Am I doing something wrong? Am I misunderstanding something?
I'm expecting the value to be removed from the Cache after 5 minutes time, however it stays in there until I rebuild the project.
This is all running on .NET 4.0 on my local machine.
It turns out that this line:
HttpContext.Current.Cache[remoteIp] = ((int)HttpContext.Current.Cache[remoteIp]) + 1;
removes the previous value and re-inserts the value with NO absolute or sliding expiration time. In order to get around this I had to create a helper class and use it like so:
public class IncrementingCacheCounter
{
public int Count;
public DateTime ExpireDate;
}
public void UpdateCountFor(string remoteIp)
{
IncrementingCacheCounter counter = null;
if (HttpContext.Current.Cache[remoteIp] == null)
{
var expireDate = DateTime.Now.AddMinutes(5);
counter = new IncrementingCacheCounter { Count = 1, ExpireDate = expireDate };
}
else
{
counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
counter.Count++;
}
HttpContext.Current.Cache.Insert(remoteIp, counter, null, counter.ExpireDate, Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
This will get around the issue and let the counter properly expire at the absolute time while still enabling updates to it.
Try using DateTime.UtcNow to calculate your timeout period instead of datetime.Now . You may be running into the issue described below:
absoluteExpiration Type:
System.DateTime The time at which the
inserted object expires and is removed
from the cache. To avoid possible
issues with local time such as changes
from standard time to daylight saving
time, use UtcNow rather than Now for
this parameter value. If you are using
absolute expiration, the
slidingExpiration parameter must be
NoSlidingExpiration.
There's a simpler answer than what smoak posted. Using that example as a starting point, the updated code below works and doesn't require a re-insert. The reason this works is because classes are reference types. Thus, when you update the counter inside the class instance it doesn't cause the cache to trigger an update.
public class IncrementingCacheCounter
{
public int Count;
}
public void UpdateCountFor(string remoteIp)
{
IncrementingCacheCounter counter = null;
if (HttpContext.Current.Cache[remoteIp] == null)
{
counter = new IncrementingCacheCounter { Count = 1};
HttpContext.Current.Cache.Insert(remoteIp, counter, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
else
{
counter = (IncrementingCacheCounter)HttpContext.Current.Cache[remoteIp];
counter.Count++;
}
}

Nhibernate in asp,net ISession help

We're using nhibernate in and asp.net MVC application.
We are implementing the Session per Request pattern, via a httpModule.
It looks pretty straight forward, but when we run with NHibernate Profiler, it clearly shows that the
sessions are never getting closed.
the pattern seems straight forward...but I don't understand why the sessions are never closing.
here's the code i think is important.
set up the event handler:
context.EndRequest += new EventHandler(this.context_EndRequest);
in the handler dispose the Session
private void context_EndRequest(object sender, EventArgs e)
{
netLogHdl.ArchDebug("NHibernateHttpModule.context_EndRequest() ");
Dispose(0);// we are hitting 2 dbs and thus keep one session for each.
Dispose(1);
HttpContextBuildPolicy.DisposeAndClearAll();
}
private void Dispose(int sessionIndex)
{
netLogHdl.ArchStart("NHibernateHttpModule.Dispose", "int sessionIndex=\" + sessionIndex + \")");
try
{
//close the DB session
string sessManagerName = "";
string jcdcManager = "JCDC Manager";
string spamisManager = "Spamis Manager";
if (sessionIndex == 0)
sessManagerName = jcdcManager;
else
{
sessManagerName = spamisManager;
}
ISession oneSession = sessionPerDB[sessionIndex];
if (oneSession != null)
{
if (sessManagerName == jcdcManager) netLogHdl.ArchDebug(sessManagerName + " oneSession is NOT null");
if (oneSession.IsOpen)
{
// Don't flush - all saves should use transactions and calling Commit does the flush.
if (sessManagerName == jcdcManager) netLogHdl.ArchDebug(sessManagerName + " Closing the session");
//This will overrite it with the exact same session, if they don't match something weird is going on - EWB
oneSession = CurrentSessionContext.Unbind(factoryPerDB[sessionIndex]);
oneSession.Close();
}
else
{
if (sessManagerName == jcdcManager) netLogHdl.ArchDebug(sessManagerName + " Session is NOT open");
}
//if ( sessManagerName == jcdcManager ) netLogHdl.ArchDebug( sessManagerName + " Session got Dispose()-ing" );
//oneSession.Dispose();
}
else
{
if (sessManagerName == jcdcManager) netLogHdl.ArchDebug(sessManagerName + " Session is NULL");
}
sessionPerDB[sessionIndex] = null;
}
catch (Exception)
{
throw;
}
netLogHdl.ArchEnd();
}
Can anyone point me in the right direction? What shoud I look at, is the pattern not implemented correclty?
I'm flummoxed
Thanks!
E-
You should call dispose and not disconnect or close. System.IDisposable implementing objects should always be disposed by calling the dispose method, or by a using block.
You also might want to have a look at the code on Ben Day's blog post about session management.

primary key violation if case sensitive

Suppose I've the 'dom' table which contains two fields
code
name
Code should be primary key. In case if I enter values('SD', 'domnic')
then again if I enter ('SD', 'domnic1')
in asp.net I've wrote validation so i can receive alert message.
protected void ButtonSave_Click(object sender, EventArgs e)
{
try
{
if (Mode == "Add")
{
primarykeyValidation();-------------->validation
if (strpkval == TextBoxWorkshopid.Text)
{
Alert.Show("code Already Exists");
TextBoxWorkshopid.Text = string.Empty;
TextBoxWorkshopid.Focus();
return;
}
}
...
public void primarykeyValidation()
{
DataSet dspkval = new DataSet();
try
{
objaccess.Option = "P";
objaccess.code= TextBoxWorkshopid.Text;
dspkval = objaccess.retriveOutsideWorkshops();
if (dspkval != null && dspkval.Tables.Count != 0 && dspkval.Tables[0].Rows.Count != 0)
{
strpkval = dspkval.Tables[0].Rows[0]["CODE"].ToString();
}
}
catch (System.Exception ex)
{
throw ex;
}
}
In case I enter('sd','domnic') it won't show the message just error thrown due to violation of primary key.
In "P" option I've wrote query as
select code from xxx where code=#code
so if i enter small case'sd' then i sholud receive alert message that "code aleady exits but it wouldnt show the
message........
1) Configure your database for case sensitivity. Known as collation. It does affect the entire database, though.
2) You can force a given query to have specific collation: http://web.archive.org/web/20080811231016/http://sqlserver2000.databases.aspfaq.com:80/how-can-i-make-my-sql-queries-case-sensitive.html.
3) You could modify the business logic so that all PK values are made upper case before being sent to the database in the first place. You'd have to do this for updates/inserts as well as queries though, so it could get messy (and be careful that your queries are still sargable i.e. don't put UPPER(#code) in your queries - actually modify the value before putting it in the parameter).

Resources