Prevent query string manipulation by adding a hash? - asp.net

To protect a web application from query string manipulation, I was considering adding a query string parameter to every url which stores a SHA1 hash of all the other query string parameters & values, then validating against the hash on every request.
Does this method provide strong protection against user manipulation of query string values? Are there any other downsides/side-effects to doing this?
I am not particularly concerned about the 'ugly' urls for this private web application. Url's will still be 'bookmarkable' as the hash will always be the same for the same query string arguments.
This is an ASP.NET application.

I'm not sure this provides any sort of security. If a man-in-the-middle attacker wants to change the parameters, all they must do is change the query string and recompute the SHA-1 hash and send that request along to the server.
For example, the URL sent by the browser might be:
http://www.example.com/addUser.html?parameterA=foo&hash=SHA1("parameterA=foo")
If an attacker intercepts this, they can edit it in this way:
http://www.example.com/adduser.html?parameterA=bar&hash=SHA1("parameterA=bar")
Really, this boils down to the fact you can trust the hash only as much as the parameters themselves.
One way you could fix this would be if the user has a password that only they and the server knows, then it would be impossible for the attacker to recompute the hash if they change the parameters. For example:
http://www.example.com/addUser.html?parameterA=foo&hash=SHA1("parameterA=foo"+"theuserpassword")
But don't put the password as one of the parameters in the URL :)
It is important to note that this isn't the state of the art for verifying the integrity of messages passed between two parties. What is used today is a form of the Hash-based Message Authentication Code (HMAC) algorithm, which is pretty well described in HMAC, and definitively in RFC2104 and FIPS Pub 198-1.

My solution to prevent query string manipulation with no hash:
In the global.asax file
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// I take the url referer host. (manipulating the query string this value is null or your local address)
string strRefererHost = Request.UrlReferrer == null ? string.Empty : Request.UrlReferrer.Host;
// This is the host name of your application
string strUrlHost = Request.Url.Host;
// I read the query string parameters
string strQSPars = Request.Url.Query ?? string.Empty;
// If the referer is not the application host (... someone manipulated the qs)...
// and there is a query string parameter (be sure of this otherwise nobody can access the default page of your site
// because this page has always a local referer...)
if (strRefererHost != strUrlHost && strQSPars != string.Empty)
Response.Redirect("~/WrongReferer.aspx"); // your error page
}

You might consider using this little open source library:
http://www.codeproject.com/KB/aspnet/Univar.aspx
It uses a unique key for each client computer and comes with many other goodies.

I think is a good idea to add a parameter with a hash of all the other parameters. It prevents radically the querystring manipulation, but you have to think about the problem that means use those URLs in other pages of your application, send those URLs to the public or use them in any printed way. You need to have a very good way to order and to have them at hand speccially if those pages are not dynamically created, or if you just need to add those URLs by hand.
I don't see any other problem about it. Some one may tell you that the hash can be calculated, but you can play with the order of the parameters obtaining different hashes and making very difficult to guess.

One major problem with this is that javascript would have to do client-side SHA calculations just to link to pages, this of course depends on how much you use JS but it shouldn't be unresonable to think that a get argument might include pageNo=1, and to have a 'jump to page' input box, this would be made difficult if you add a hash. You could store in a session (server side) anything that you really don't want manipulated.

Related

How to protect cookies from an attack

I want to use cookies for storing userId during the session which lets to avoid unnecessary roundtrips to the data base. This userId is used to access some user specific information. As cookies can be easily edited I'm now conserned with the security issue.
In order to forbid an logged in user to edit their userId and so get access to other users' information I use a pretty straightforward method. I add one more cookie at the userId cookie creation moment which stores a hashed value for it. While hashing I use a hard coded 64 byte key. When retrieving the userId from the cookie it is always checked if it matches with its hashed value.
Here is basically my code:
public static int GetUserId(Page page)
{
int userId;
if (page.Request.Cookies["userId"] != null && page.Request.Cookies["userIdHashed"] != null)
{
string userIdHashed = page.Request.Cookies["userIdHashed"].Value;
string userIdCoockie = page.Request.Cookies["userId"].Value;
string coockie = (userIdCoockie + "945AFF2FD0F1D89B4B1DBEB1B0C5D3B8B5DCE000AAEA331EB0C3F3A68C3865EFA73BC6EBF30C8DF1AD6B9ECB7094DA5B0C1AF36B5BBD096E3D873E9589E3F664").GetHashCode().ToString();
if (userIdHashed == coockie)
{
userId = Int32.Parse(userIdCoockie);
}
else
{
throw new Exception("UserId does not match!");
}
}
else
{
userId = ...//here userId is being retrieved from the data base and than:
page.Response.Cookies["userId"].Value = userId.ToString();
page.Response.Cookies["userId"].HttpOnly = true;
string userIdHashed = (userId.ToString() + "945AFF2FD0F1D89B4B1DBEB1B0C5D3B8B5DCE000AAEA331EB0C3F3A68C3865EFA73BC6EBF30C8DF1AD6B9ECB7094DA5B0C1AF36B5BBD096E3D873E9589E3F664").GetHashCode().ToString();
page.Response.Cookies["userIdHashed"].Value = userIdHashed;
page.Response.Cookies["userIdHashed"].HttpOnly = true;
}
return userId;
}
So my questions are:
Can such an approach be considered
reliable enough in this situation?
If not should I modify it and how or
should I look for something different
(e.g. encryption/decryption via
System.Security.Cryptography as
recommended here)?
And additional question: Does it really make sense to set HttpCookie.HttpOnly = true to prevent javascript from accessing the cookie given that it can also easily be modified by the user?
UPDATE
Great thanks for answers to Kerrek SB and Darin Dimitrov who share the opinion that it does not make sense to try to protect cookies on my own taking into account that there are already built in protected mechanisms of storing of such kind of information between postbacks.
Options suggested are:
Using the ASP.NET cache (but I believe it is generally supposed to
store information which should be shared between users, so I look at
other two options).
Adding a custom string with userId into UserData part of the
FormsAuthenticationTicket.
Using the Session State.
So currently I'm deciding between the latter two.
Changing the FormsAuthenticationTicket is not really straightforward. Additionally it does not work with the Cookieless Forms Authentication (as stated here).
Using the Session State is much easier but it can affect the performance because it stores the values in the server memory. However may be in my case it is not so dramatic because we store only userId of type int.
So for now the last option looks much better for me. However I would greatly appreciate if anybody else could comment and support or criticise any of options discussed.
Thanks in advance!
You seem to be reinventing some wheels here. While this could be acceptable when doing some standard code, when security is involved this almost always leads to catastrophic consequences.
To track actively logged in user I would recommend you using forms authentication (and here's another useful tutorial). It uses authentication cookies to track users. Those cookies are securely encrypted by the framework so that they cannot be modified using the <machineKey> section of the server machine.config file.
Form your code all you need to do to access the currently logged in user name is the following:
public static string GetUserId(HttpContextBase context)
{
if (context == null || !context.User.Identity.IsAuthenticated)
{
return null;
}
return context.User.Identity.Name;
}
You really shouldn't be handling all this stuff manually especially when the ASP.NET framework has a built-in mechanism for it.
That's terribly roundabout and obscure. If you already have an active session, why don't you just store this kind of data (which the client never needs to know, mind you) in your server-side session data?
All you should ever need to exchange with the client is the session ID, really.
you can also encrypt the cookies with Secure Socket Layer
HttpCookie cookie = new HttpCookie();
cookie.Secure = true;

asp.net webcontrol caching

I'm writing a webcontrol in asp.net which has to go and fetch information from an API. It's not critical to be up-to-date, and retrieving the information is quite slow, so I'd rather cache the information and update it every 5 minutes.
It strikes me as potentially risky to use the Context.Cache as potentially someone could use the same name, but I can't find another way to do caching within a control.
Does anyone have any other ideas?
(using asp.net 2.0).
It sounds like the Context.Cache is exactly what you need to store this kind of information. The cache object is localized to your application so it would only be changeable by other code in your application.
You can always give your cache key a very long and presumably useless name etc and store that key within your class...
Private const CacheKey as String = "e92a627b-3a9f-46da-a182-d73b44fe87ad" ' A random guid
Cache.Item(CacheKey) = TheRefreshedData ' etc
As I was writing this answer I realised maybe you were talking about your conntrol being re-used within someone else's application...as in your control is redistributed or provided for download. In this case, you could always prefix the cache key with your control to be "sensibly" unique...
Private const CacheKey As String = "MyCompany.MyControl.MyData"
Cache.Item(CacheKey) = TheRefreshedData ' etc
Either way, you should be able to come up with some fairly length string that has zero chance of being reused...

Asp.net, where to store the username of logged in user?

When a user log into my asp.net site I use the following code:
FormsAuthentication.RedirectFromLoginPage(userid, false);
As I often need to use the userid I can then later get the userid by:
string userid = System.Web.HttpContext.Current.User.Identity.Name;
Now I also want to show the logged in username on each page and my questions is therefore where do I place the username best if I need to use it on every page. User.Identity.Name is already taken by the userid so I can't use that one. Another solution would be to get the username from the database on each page, but that seems like a bad solution.
So: Is the best way to use Sessions to store the username?
There are essentially 6 different ways to store information, each with it's own benefits and drawbacks.
Class member variables. These are only good for the life of one page refresh.
HttpContext variables. Like class member variables, only good for one page refresh.
ViewState, these are passed from page to page to keep state, but increase the size of the downloaded data. Also, not good for sensitive information as it can be decoded.
Cookies. Sent on each page request. Also not good for sensitive information, even encrypted.
Session. Not passed to the end user, so good for sensitive information, but it increases the resource usage of the page, so minimizing usage for busy sites is important.
Authentication Cookie User Data - This is like like cookies, but can be decoded with the authentication data and used to create a custom IIdentity provider that implements your desired Identity information, such as Name or other profile information. The size is limited, however.
You can store just about anything in SessionState in asp.net. Just be careful and store the right things in the right places (you can also use ViewState to store variables.
Check this out for how to use SessionState to store and retrieve variables across postbacks.
public string currentUser
{
get { return Session["currentUser"] as string; }
private set { Session["currentUser"] = value; }
}
Using sessions isn't a bad idea but make sure to check for NULL when retrieving the values for when the sessions time out.
Or you could pass the variable through in the URL e.g
/Set
Response.Redirect("Webform2.aspx?Username=" + this.txtUsername.Text);
/Read
this.txtBox1.Text = Request.QueryString["Username"];

Passing IDs between web applications

We have several web applications that create a shopping cart, save it to a database, then redirect to a centralized web application to process and accept payment for the shopping cart. Right now, we are using GUIDs for the shopping cart IDs and passing those GUIDs in the querystring to the payment application. We are using GUIDs so that a user cannot easily guess the shopping cart ID of another user and simply plug that ID into the URL.
Now, using GUIDs in the database is bad for indexing and using GUIDs in the URL does not truly prevent a user from accessing another cart. However, using passing integers around would make it too easy.
What is the best and most secure way to pass the IDs from the individual applications to the centralized payment application?
I know that some people may say, "Who cares if someone else wants to pay for someone else's shopping cart?" However, we have the same concern when passing IDs to the page that displays the receipt and that page includes the customer's name.
You could pass the ID as an integer along with a "token" which would be a (cryptographically strong) hash of the cart ID and a random secret string. The payment processor would know the secret so it could perform the hash itself and compare to see if it is valid.
For example you can use the following (untested) code to create the token:
public static string GenerateHash(long CartID)
{
string SourceText = CartID.ToString();
//Salt the source text (secret)
SourceText += "5E95C91F7F947BD92ACA2CF81C3ADBD9B563839D85EA69F9DEA5A2DC330D0F50";
//Create an encoding object to ensure the encoding standard for the source text
UnicodeEncoding Ue = new UnicodeEncoding();
//Retrieve a byte array based on the source text
byte[] ByteSourceText = Ue.GetBytes(SourceText);
//Instantiate an MD5 Provider object
System.Security.Cryptography.SHA1CryptoServiceProvider SHA1 = new System.Security.Cryptography.SHA1CryptoServiceProvider();
//Compute the hash value from the source
byte[] ByteHash = SHA1.ComputeHash(ByteSourceText);
//And convert it to String format for return, also modify for URL use
return Convert.ToBase64String(ByteHash).Replace("=", "").Replace("+", "-").Replace("/", "_");
}
Pass the result of this function, along with your cart ID, since a hash is a one-way function that cannot be reversed. On the payment processor you would call the same function on the passed in cart ID and compare it to the token.
This will prevent tampering with the query string yet allow you to use integers.
Had you thought of POSTing to the central system and passing the values that way? Then they wouldn't be visible in your query string.
If you have to pass the GUID in the querystring, you could encrypt it to make it a little more secure. It will add a little overhead, also, to your processing.
You could also tie the user's cart to a cookie, then the GUID wouldn't be visible in the querystring and would be a little harder to detect (although using fiddler or some other tool like that would show what's being passed up and down).
I'd stick the identifier in a cookie, some other header or, if you have to POST, as a hidden value (like Lazarus suggested). I'd avoid having it on the querystring.
I would use methods similar to the Anti Forgery Token in ASP.NET MVC.
http://davidhayden.com/blog/dave/archive/2009/04/29/AntiForgeryTokenInMVCFramework.aspx
EG. In addition to your GUID, save a random id in a cookie and in the db tied to a user. Each time the user makes a http request check that the cookie matches with the database. It would be hard to get both the GUID and cookie correct.

What options to I have to pass a value between two asp.net pages

I want to know what the best practice is for passing values (sometimes multiple values) between two asp.net pages
In the past I have used query strings to pass a value in asp like this:
href='<%# Eval("TestID","../Net/TestPage.aspx?TestID={0}") %>'><%#Eval("Title")%> </a>
I assume you can do this in the code behind but I do not know the best way.
I also assume it is possible to pass more than one value.
Could someone provide me with a VB snippet which would give me an idea of how to go about this?
You have many options for passing data and all of them can pass multiple values between pages.
You can use the Request.Form collection to capture values that have been submitted from an HTML form with the POST verb (i.e. " method="POST">).
The code looks something like:
Dim formvalue As String
formValue = Request.Form("FormField1")
You can also use parameters in a URL query string (much like you example):
Dim queryStringValue As String
queryStringValue = Request.QueryString("QueryStringValue1")
You can set a cookie (it's lifetime will depend on the Expiry property value that you set):
Setting a cookie (note: you use the HttpResponse object here. The user's browser stores the cookie when it receives the Set-Cookie HTTP header value from the response to a request)
Response.Cookies("CookieValue") = "My Cookie Data"
Response.Cookies("CookieValue").Expires = DateTime.Now.AddDays(1) ' optional, expires tomorrow
Retrieving a cookie value (we use the HttpRequest object here):
Dim cookieValue As String
cookieValue = Request.Cookies("CookieValue")
You can use the HttpSessionState object (accessible via the Session property of a page). To set a session variable:
Session["SessionValue"] = "My Session Value"
To retrieve a session value:
Dim sessionValue As String
sessionValue = Session["SessionValue"]
There's another way to pass page state between pages using Page.Transfer (see How to: Pass Values Between ASP.NET Web Pages), but I'd try and get comfortable with the above before looking into that.
As far as best practices go it really depends on what data you're passing.
Don't pass sensitive data via URLs (query strings), forms or cookies. These can intercepted in various ways
Pass sensitive data using a server-side store (like session state or a database) but consider how to keep the session ID safe.
Never trust data from outside your application (data that users have entered via a form, information read from a database, etc.). Always encode this information before displaying it again in your pages. This prevents against Cross-Site Scripting (a.k.a XSS) attacks.
Don't use sequential IDs in query strings where you're passing user-specific identifiers between pages. Say you create an Orders.aspx page that lists all orders for a customer. You pass in a CustID parameter via a query string: Orders.aspx?CustID=123. It's easy for someone to change the URL to Orders.aspx?CustID=124 and view information they shouldn't. You can get around this by doing a check that the current user is allowed to see the information, you can use an identfier that can't be easily guessed (commonly a GUID) or pass the information on the server-side.
It would help you to check out the following links:
a. Cross page postbacks
b. How to pass values between ASP.NET pages (MSDN)
c. Another article by Steve C. Orr on Passing values.
You can use a session, cookies, the query string, hidden form fields in a post request.

Resources