1

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!

Community
  • 1
  • 1
Kirill
  • 3,028
  • 1
  • 17
  • 18

3 Answers3

3

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.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thank you. Actually I do use forms authentication along with Membership. I need cookies here not for tracking a logged in user but for retrieving a user specific information from the DB, where it stored in tables with this custom userId as foreign key. I decided for this model as a result of my previous question and corresponding answers ([details here](http://stackoverflow.com/questions/6532418/how-to-combine-using-membership-api-with-own-application-related-data)). So unfortunately it's not enough to retrieve Identity.Name or even Membership guid UserName in standard way for my purposes. – Kirill Jul 10 '11 at 22:08
  • @Kirill, in this case use a [custom profile](http://weblogs.asp.net/jgalloway/archive/2008/01/19/writing-a-custom-asp-net-profile-class.aspx) which will allow you to store additional information about the currently logged in user. – Darin Dimitrov Jul 10 '11 at 22:11
  • If I get it right, you suggest to use a custom profile to store my custom userId. But afaik the profiles feature doesn’t integrate with any caching, so every request that uses profile data requires a database connection. And that is exactly what I'm trying to avoid. – Kirill Jul 10 '11 at 23:15
  • @Kirill, you are correct. If you want to avoid db access on each request you could use the `userData` part of the authentication ticket where you could store custom info. Here's a nice tutorial: http://man.ddvip.com/web/bsaspnetapp/LiB0057.html. In this examples they store some roles into it but you could use it to store whatever info you like. It will be encrypted and be part of the authentication cookie and sent on each request so it will be available. – Darin Dimitrov Jul 11 '11 at 05:54
  • And yet another possibility is to use the ASP.NET cache to store this information in order to avoid roundtrips to the database. – Darin Dimitrov Jul 11 '11 at 06:01
2

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Thank you for your answer. However it looks like a common question cookies vs. session which is broadly discussed for example [here](http://stackoverflow.com/questions/356562/web-authentication-state-session-vs-cookie-vs) or [here](http://forums.asp.net/t/1110947.aspx/1). In most cases it ends up with personal preferences of the developer, although in this particular case may be you're right and using session has more pros than cookies. May be I'll change my model to using session as you suggest but first I'd like to hear other opinions. – Kirill Jul 10 '11 at 21:49
  • @Kirill: The argument is very simple: Your DB's ID field value is nothing that the user needs to know, ever. It has no relevance on the client-side behaviour. Thus it naturally belongs on the server side. It's an implementation detail that should not be exposed to the user. – Kerrek SB Jul 10 '11 at 21:57
  • By the way, your [linked question](http://stackoverflow.com/questions/356562/web-authentication-state-session-vs-cookie-vs) just exemplifies in what a bad state the community is - the question itself is comparing apples and pears and doesn't make sense. It asks, "do you track the authentication state with a cookie or with a session state", as if this existed. What's a "session state"? You have to **identify** the session state somehow **within the HTTP request**. There aren't that many ways to do that, essentially either a cookie or a URL parameter, but the asker doesn't seem to grasp that. – Kerrek SB Jul 10 '11 at 22:00
  • That's true, but it's essentially up to you if you use a cookie or a URL parameter for storing SessionID. And it does not mean that in general case session is preferable. Although as I said in my case may be you're right. – Kirill Jul 10 '11 at 22:23
  • You're right that the choice between cookie and URL parameter is up to you. (Though URL parameters have so many issues that this is sort of a foregone conclusion.) What doesn't make sense is to ask whether to "track authentication state in a cookie or in a session".... hmm, ok, well, you *could* write a server that would simply accept `&i_am_authenticated=1` and grant you full access. OK, I hadn't thought to include that. Fair enough :-) – Kerrek SB Jul 10 '11 at 22:26
0

you can also encrypt the cookies with Secure Socket Layer HttpCookie cookie = new HttpCookie(); cookie.Secure = true;

Peter
  • 37,042
  • 39
  • 142
  • 198
Waqar Janjua
  • 6,113
  • 2
  • 26
  • 36
  • Thank you for your answer, but one must be using SSL with the application in order to use an SSL cookie, which was not my intention. – Kirill Jul 11 '11 at 13:18
  • Settings `cookie.Secure = true` does not encrypt the cookie per say, it just tells the browser to only send the cookie when using a secure channel. So if this is set to `true` then the cookie will not be sent at all when using http. – Peter Mar 21 '17 at 09:25