I've got a web service (ASP.NET 2.0) that I'm calling from javascript using jQuery's $.ajax(). I've been told that the session key is often used as a nonce in a situation like this for security purposes; the web service takes a key as one of its parameters and only returns data if it matches the current session key.
I'm attempting to accomplish this by setting the value of a hidden field to the current SessionID on every Page_Load (i.e. every postback), then grabbing it in the javascript to pass as a parameter. But it's never the same key as the web service's current key (Context.Session.SessionID).
Is this possible to resolve, or should I be doing this another way?
EDIT: code to set session in hidden field as requested.
hfSession.Value = Context.Session.SessionID;
That's in the Page_Load of a .ascx control, not under any conditional (i.e. not wrapped with if (!Page.IsPostBack).
I believe you are trying to prevent Cross Site Script Request Forgery (CSRF). The Session ID is actually sent across as a cookie and the attacker can set this. Rather than use the Session ID itself, you should use a randomly generated number stored in a Session variable.
String GetCSRFToken()
{
String token = (String)Session["CSRFToken"];
if( token == null )
{
token = GenerateLongRandomString();
Session["CSRFToken"] = token;
}
return token;
}
void AssertValidCSRFToken(String token)
{
if( token != GetCSRFToken() )
{
throw new Exception("Invalid Request!")
}
}
Here is a link to another question with more info on preventing this kind of attack:
CSRF Validation Token: session id safe?
Asp.net actually generates a new Session ID for every request until you use the Session State to store some value. This could be a reason why the values are different. Try and save something in the session. Perhaps
Session["SessionID"] = Context.Session.SessionID;
hfSession.Value = Context.Session.SessionID;
A new SessionID is generated each time the page loads until the session is actually allocated. So if you don't actually allocate anything to the session, the SessionID will change upon each page load.
Related
I currently implemented some "checkers" for my MVC application.
So far here is what i have,
Authorization(Form)
Authentication (Custom RoleProvider)
Action Filters(to make sure that the user won't put any bogus id numbers or try accessing the other's data by editing the GET url.
I have several questions regarding the best practices for cache on ASP MVC.
Here is my implementation of my login:
[HttpGet]
[ActionName("login")]
public ActionResult login_load()
{
return View();
}
[HttpPost]
[ActionName("login")]
public ActionResult login_post(string uname,string pword)
{
using (EmployeeContext emp = new EmployeeContext())
{
//h student log = new student();
int success = emp.login.Where(x => x.username == uname && x.password == pword).Count();
if (success == 1)
{
int id = (from logs in emp.login
join rol in emp.roles on logs.role equals rol.id
where logs.username == uname
select logs.id).First();
FormsAuthentication.SetAuthCookie(uname, false);
HttpRuntime.Cache.Insert("id", id);
return RedirectToAction("Details", "Enrollment", new { id = id});
}
return View();
}
}
(I plan on implementing H&S as soon)
Anyway, here are my concerns so far:
For security concerns, would it be fine to store something like id's on cache? or it's better if i use sessions ?
Let's say i successfully logged in, and i add another line of this code :
HttpRuntime.Cache.Insert("id", id);
Is it going to edit my previous record or it's going to add another entry?
I have this code from my Custom RoleProvider, HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration); and i believe that they are "fired" everytime i ask a controller with a protection of [Authorize(Role="users")]. So does it make a new entry or it edits the previous/existing one?
Should i worry about deleting/clearing my cache as soon as the user decided to log out? my role provider timeout is currently set to 20 minutes
I need the id because aside from the username, it is my unique identifier and i use it to compare on whatever id the user is trying to access.
I am thinking if it would be possible to edit the cache and use it against my application.
Don't worry about storing the ID, you need to go back and refactor to use the inbuilt identity stuff that's in the box for MVC. Looking at your code I can only assume that this system would store passwords in plain text. You will not pass any compliance with a system like this.
Rule of thumb when it comes to "is this secure" is don't write it yourself. Find a proven product and use that.
If for whatever reason the inbuilt identity system that is provided with MVC doesn't work for your requirements, have a look into this: https://github.com/brockallen/BrockAllen.MembershipReboot
FYI:
Identity system is the service that logs people in, out and manages the logged in user. Feel free to head over to this to learn more about the inbuilt system for MVC: http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity
For security concerns, would it be fine to store something like id's on cache? or it's better if i use sessions ?
In this case it doesn't make a lot of difference. However, the cache cannot be distributed across multiple web servers. Session state can by changing the <sessionState> section of the web.config file. So you are building an inherent limitation in scalability into your application by using cache.
Let's say i successfully logged in, and i add another line of this code :
HttpRuntime.Cache.Insert("id", id);
Is it going to edit my previous record or it's going to add another entry? I have this code from my Custom RoleProvider, HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration); and i believe that they are "fired" everytime i ask a controller with a protection of [Authorize(Role="users")]. So does it make a new entry or it edits the previous/existing one?
First of all, you have a major flaw in your code. Cache is intended to be shared between all users on the site. So, when you insert a value, such as HttpRuntime.Cache.Insert("id", id);, all of the users will see it. If you are using this value to lookup data later, then the user data will always be for the last user that signed in.
You can fix this by adding adding a value unique to the user to the key.
var key = this.User.Identity.Name + "|Id";
HttpRuntime.Cache.Insert(key, id);
Note I am using a pipe character as a separator here. This is assuming the user name doesn't allow a pipe character (which you would also need to ensure).
Secondly, using a proper cache pattern means that you will never need to worry about whether "id" exists because you have already made that check. Using the cache typically looks like this.
public static string GetUserID()
{
// Check whether the user is logged in
if (!HttpContext.Current.User.Identity.IsAuthenticated) {
return 0;
}
// Make a UNIQUE key that can be used for this scenario
var userName = HttpContext.Current.User.Identity.Name;
var key = userName + "|Id";
// Attempt to get the ID from the cache
var id = HttpRuntime.Cache[key];
// A null value indicates there was no value in the cache
if (id == null)
{
// No ID in the cache, look it up from the database
using (EmployeeContext emp = new EmployeeContext())
{
id = (from user in emp.login
where user.username = userName
select user.id).First();
}
// Store the ID from the database into the cache
HttpRuntime.Cache.Insert(key, id,
// No Dependencies
null,
// No absolute expiration (mimic the behavior of forms authentication)
System.Web.Caching.Cache.NoAbsoluteExpiration,
// Timeout 20 minutes after the last access
// (to mimic the behavior of forms authentication)
new TimeSpan(0, 20, 0),
// Setting to NotRemovable ensures that if the
// application pool restarts, you don't lose your cache
System.Web.Caching.CacheItemPriority.NotRemovable,
// No callback needed here
null);
}
return (string)id
}
Of course, you can improve performance if the value is available at login by inserting it into the cache directly, but you need to ensure you use the same key in that case.
Session is probably a better choice in this scenario, but either way you should make use of this pattern to double check you have a value before returning it to the user.
Should i worry about deleting/clearing my cache as soon as the user decided to log out? my role provider timeout is currently set to 20 minutes
If you use Session state instead of cache this is much easier. Simply call Session.Abandon() when the user logs out.
I have a multi key-value pair cookie. The cookie is a session cookie, i.e. not persistent. It contains several key-value pairs of data and well within the 4k size limit.
What's working: In my initialise action method (on Controller A), the cookie has a number of key-value pairs successfully added to it. All good so far.
Problem: When I navigate to another page, i.e. different action method (on Controller B), and then add a value to the cookie, I find all the previous values in the Cookie are now gone.
Please note, I verified the problem occurs only when a new value is added here. NOT adding a value to the cookie and navigating across many pages (and controllers) the existing values in the cookie are all preserved.
Investigations: I have spent one and a half days on this and tried a number of thing. In my cookie WriteCookie() method:
Making the Cookie persistent (by setting it's expiry to tomorrow). Verified the cookie in Firefox exists and has the correct date. But once I add a value to the cookie in Controller B, the cookie has lost all it's data.
Create a new cookie with the same name, added the previous values to this new cookie along with the new value. But again, navigating to another page (Controller B) and adding a value to the cookie has lost all it's previous data.
Code: Below is the original code I started with which doesn't contain the many heartbreaking attempts:
public static class CookieHelper
{
public static string ReadCookie(string key)
{
string value = string.Empty;
HttpCookie cookie = HttpContext.Current.Request.Cookies["mycookie"];
if (cookie != null)
{
value = HttpContext.Current.Request.Cookies["mycookie"].Values[key];
}
return value;
}
public static void WriteCookie(string key, string value)
{
HttpContext.Current.Response.Cookies["mycookie"].Values[key] = value;
}
}
Can anyone please help and explain why the cookie values are being lost. My guess, for some unknown reason subsequent writes to the cookie AFTER a HTTP Request is creating a new cookie and overwriting the existing cookie.
From MSDN:
You cannot directly modify a cookie. Instead, changing a cookie consists of creating a new cookie with new values and then sending the cookie to the browser to overwrite the old version on the client.
Modifying an individual subkey is the same as creating it.
To delete an individual subkey, you manipulate the cookie's Values collection, which holds the subkeys. You first recreate the cookie by getting it from the Cookies object. You can then call the Remove method of the Values collection, passing to the Remove method the name of the subkey to delete. You then add the cookie to the Cookies collection so it will be sent in its modified form back to the browser.
I have a webservice (.asmx) and I want it to retain a value on a per-user basis. Is this possible?
ie (pseudo-code)
MyWebservice
{
object perUserVariable = something;
[WebMethod]
public void myMethod()
{
if (something == null)
{
something = doBigExpensivedatabaseCall();
}
return something;
}
}
You can use ASP.NET's session mechanism.
Change your WebMethod attribute, so that it will look like that:
[WebMethod(EnableSession = true)]
This is normally achieved by cookies, or by sending the session id in the query string (both ways are completely handled by ASP.NET). The former is the default, to achieve the latter, just set cookieless="true" in your config.web file.
Yes, you would need to pass in some kind of user identifier to specify who the user is, do your operation and instead of storing it in something you will to use a durable or semi-durable store such as Cache or Session. Then look that value up from the Cache or Session instead of a local member.
Also fwiw the way you have that configured the fact something isn't static means it would be null on every single request because it would be newly initialized. Making it static however would then server the individual instance of something to each and every request there after.
This is why you need to use a store that can differentiate on the user such as Cache[userid+"something"] or the Session["something"] instead.
Your choices seem to be using some type of caching system like session or memcache.
Session will require a session id passed as a cookie to the requests. Other caching providers could probably key off of a post value like the userid.
I have a form. On submit of a button, it inserts a record into the db. If I go back in my browser and refresh the page it's resubmitting it causing all sorts of issues as you can imagine. How do I ensure that the user can't refresh the previous page and the state of that page does another automatic submit on refresh like this?
I'm not using ViewState. I actually have it disabled in the page directive
No, Response.Redirect does NOT solve this problem. The user can still go back with the browser's back button and refresh at the point where they submitted and it will still run the button's event sending the data downstream to my DL insert
The solution I use most often is this:
When the user first visits the page (Non-PostBack), generate a token value (GUID is easy). Take this token value and store it in their Session variables and a hidden field in the page. Being a field, this value should persist through round trips to the server without ViewState enabled (I could be mistaken on that count, so please check it!). Should the page be refreshed (and form values lost), a new value will be generated by the Non-PostBack initialization.
When the page is posted back, retrieve the hidden field value and compare it against the expected value in the user's Session variables:
If the tokens match, accept the submission as "genuine", remove the token from the Session variables, and continue your workflow.
If the token is missing from Session variables, the user is trying to submit the form again. - If the tokens do not match, the user is "replaying" an old submission of the form.
Example code to achieve this sort of solution:
public partial class MyPage : Page
{
protected HiddenField tokenField;
protected void Page_Load()
{
if(!IsPostBack)
CreateToken();
}
// Call this method to establish a token in session and on the page.
private void CreateToken()
{
string token = new Guid().ToString();
Session["dupeToken"] = token;
tokenField.Value = token;
}
// Call this method to validate the token before continuing workflow.
private bool TokenIsValid()
{
string expectedToken = (string)Session["dupeToken"];
if(expectedToken == null)
return false;
string actualToken = tokenField.Value;
return expectedToken == actualToken;
}
// Call this method when the page submission is complete to prevent re-submission.
private void ConsumeToken()
{
Session["dupeToken"] = null;
}
}
The easiest way would be to wrap the form in an < asp:UpdatePanel>. That way, all postbacks are done via AJAX and the browser will never ask you to re-submit your form.
The best two ways of doing this are:
Performing a check on a distinct field against the database
Create a hidden token in the form that uses a salt based on time. If you put logic in script to check for the existing time and compare it to the token, you can either allow the submission or define it. For example, if the form is drawn at a certain time, you store the token, and within 30-60 seconds you can submit, but after that you cannot.
i want a handler to redirect to a web-forms page, pre-filling in the values of some controls on the form.
i tried setting my current Request.Form data:
if (theyWantToDoSomething)
{
//pre-fill form values
context.Request.Form["TextBox1"] = "test";
context.Request.Form["ComboBox1"] = "test 2";
context.Request.Form["TextBox2"] = GetTheTextForTheThing();
//tell the client to go there
context.Response.Redirect("~/SomeWebForm.aspx");
return;
}
But i get an exception that Form values are read only.
What would be a way to send the client to another page, pre-filling form data?
Answer
i used the Session state to store values. It's important to note that by default a Handler doesn't have access to Session (the Session object will be null). You have to tell IIS to give you the Session object by adding the IRequiresSessionState marker interface to your handler class:
public class Handler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
...
if (theyWantToDoSomething)
{
//pre-fill form values
context.Session["thing1"] = "test";
context.Session["thing2"] = "test 2";
context.Session["thing3"] = GetTheTextForTheThing();
//tell the client to go there
context.Response.Redirect("~/SomeWebForm.aspx");
return; //not strictly needed, since Redirect ends processing
}
...
}
}
You can only populate your Response, the Request is input data and is indeed read-only.
If you are using ASP.NET, there are a variety of ways you could accomplish what you need:
The best way would probably be to pass the data you need to be pre-populated to SomeWebForm.aspx via the Session object, and on that pages Load method, populate your form. Keep in mind that when you do Response.Redirect, a 302 response is sent to the client with the URL the client should redirect to. The process is transparent to the user...but there is a full round trip involved.
Another alternative to populating the users Session would be to add GET parameters via a query string to the redirect to SomeWebForm.aspx.
If you need to transfer processing to the SomeWebForm.aspx page without round tripping, you could use Server.Transfer. This will transfer execution from the current page to the page you choose...however, this can cause some odd behavior on the client end because the URL does not update. As far as the user is concerned, it will still appear as though they are on the same page they started on.
A few ideas that might get you started:
Pass the values in the query string
Store the values in the session state or in a seperate cookie
Store the values in HttpContext.Items and use Server.Transfer instead of Response.Redirect
Another approach that hasn't been mentioned yet is using Server.Transfer makes it possible to use the Page.PreviousPage property, allowing access to the controls on the page that transferred control.
As jrista mentioned though, using Transfer doesn't update the URL shown to the user, which may or may not be an issue. For example, a user can't precisely bookmark a page they got transferred to since the URL will be that of the original transferring page.