Hacking the Code ASP.NET Web Application Security [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Hacking the Code ASP.NET Web Application Security [Electronic resources] - نسخه متنی

James C. Foster, Mark M. Burnett

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Enhancing ASP.NET State Management

ASP.NET state management is convenient, but it does not meet the criteria mentioned at the beginning of this chapter. For example, session and authentication tokens are not tightly bound to user sessions, and ASP.NET will accept any token the client provides. This section explains how to use the built-in state management features combined with client-side cookies to provide an additional layer of security. This section covers:



Creating tokens



Establishing sessions



Terminating sessions



It is important to note that although it will not stop all attacks, using SSL will further enhance the techniques described in this section.


Creating Tokens












Summary:


Use additional code to increase token security


Threats:


Session hijacking, account hijacking, session fixation, information leakage


The token you use for session management should be a strong random number and should not directly identify a user, and the application should be able to prove that it is authentic.

Random numbers are an important aspect of security, but they must have certain properties to be considered truly random. For instance, the number should not be predictable or reproducible, and it should be large enough to prevent someone guessing it. When ASP.NET creates a session token, it uses a strong random-number generator to create a 120-bit key, which is certainly adequate for most purposes. This session token is stored in a cookie named ASP.NET_SessionId or on the URL if you’re using a cookieless state. This session token is also available through the SessionID property of the Session object.

The only weakness with the ASP.NET session token is that it is just a random number and ASP.NET will accept any token you supply of the correct length, making it vulnerable to session fixation attacks. To compensate for this weakness, we must create a MAC for the session token based on a random number stored with the server’s state mechanism, as shown in Figure 3.10 (C#) and Figure 3.11 (VB.NET).







private void enhanceSessionIdWithMac() 
{
string SessionMacCookieValue;
// Check if there is a SessionMac cookie
if (Request.Cookies["SessionMac"] == null)
{
SessionMacCookieValue = null;
}
else
{
SessionMacCookieValue = Request.Cookies["SessionMac"].Value;
}
// Create and store hash
if (Session["key"] == null && SessionMacCookieValue == null)
{
Session["key"] = CreateKey();
string hash = createHMASCHA1(Encoding.Unicode.GetBytes(
Session.SessionID),
Encoding.Unicode.GetBytes(
(string)Session["key"]));
HttpCookie SessionMac = new HttpCookie("SessionMac", hash);
Response.Cookies.Add(SessionMac);
}
// Check against stored hash
else
{
string createdHash = createHMASCHA1(
Encoding.Unicode.GetBytes(
Session.SessionID),
Encoding.Unicode.GetBytes(
(string)Session["key"]));
if (createdHash != Request.Cookies["SessionMac"].Value)
{
Session.Abandon();
Response.Redirect("default.aspx");
}
}
}






Figure 3.10: Enhancing the Session ID with a MAC:C#








Private Sub enhanceSessionIdWithMac()
Dim SessionMacCookieValue As String
' Check if there is a SessionMac cookie
If Request.Cookies("SessionMac") Is Nothing Then
SessionMacCookieValue = Nothing
Else
SessionMacCookieValue = Request.Cookies("SessionMac").Value
End If
' Create and store hash
If Session("key") Is Nothing And SessionMacCookieValue Is Nothing Then
Session("key") = createKey()
Dim hash As String
hash = createHMASCHA1(Encoding.Unicode.GetBytes(Session.SessionID), _
Encoding.Unicode.GetBytes(Session("key")))
Dim SessionMac As HttpCookie
SessionMac = New HttpCookie("SessionMac", hash)
Response.Cookies.Add(SessionMac)
Else
' Check against stored hash
Dim createdHash As String
createdHash = createHMASCHA1(__
Encoding.Unicode.GetBytes(Session.SessionID), _
Encoding.Unicode.GetBytes(Session("key")))
If (createdHash <> Request.Cookies("SessionMac").Value) Then
Session.Abandon()
Response.Redirect("default.aspx")
End If
End If
End Sub






Figure 3.11: Enhancing the Session ID with a MAC:VB.NET


This code creates a MAC based on the session ID using a random number stored on the server as the key. It saves this MAC in a cookie on the client. When a client request comes in, you again compute a MAC and see if it matches the MAC stored in the client cookie. If the two match, you know it is a valid session ID; otherwise, you abandon the current session and force the user to acquire a new session ID. After a session times out on the server, the key will no longer be available and the MAC based on that key will no longer be valid. If the client sends a cookie for a preexisting session without providing a MAC, the server abandons the session and starts over with a new session ID.

For forms authentication tickets, you don’t need to add our own MAC if you always use encryption and validation using the protection=”All” attribute of the forms element in the web.config file. This ensures that an attacker cannot view or modify the forms authentication ticket. The ticket is encrypted and hashed based on the decryptionKey and validationKey attributes of the machineKey element in the machine.config file. On a default installation, both of these are set to “AutoGenerate,IsolateApps”, which means that ASP.NET will generate random values for these keys. The IsolateApps setting is important because it ensures a unique key for every ASP.NET application on the server. This is especially important on a shared host because without it, one Web site could produce a valid forms authentication ticket for another site on the same server.





Tip

If you explicitly set custom decryptionKey and validationKey values to allow forms authentication in a Web farm, you can still use the IsolateApps setting to ensure unique keys for each application on the server.


Binding to the Client


Once you establish a secure token and send it to the client, you want to make sure that only that client can use that token. Although it is difficult to absolutely prevent someone else from hijacking that cookie, there are some things you can do to limit the scope enough to block many types of attacks.

There are various ways that an attacker can obtain the session token and hijack the user’s session. Some techniques an attacker could use are:



Man-in-the-middle attack involving ARP or DNS spoofing



Obtaining the session token on the URL stored in the cache, in a proxy server’s logs, or listed as the referrer in a server’s Web logs



Gaining access to the client’s system and stealing the browser’s cookie; Trojans fall into this type of attack, too



Using cross-site scripting to obtain the browser’s cookie or the token in the URL



The reason so few sites bind to the user’s session is that it is difficult to reliably bind to a single session, and even if you can, it does not protect against all types of attacks. You need to not only obtain the token but make it difficult to use that token from another client.

There are several ways that you can create a stronger bind between the session and the client. The most obvious of these is using the client’s IP address. The main problem with this choice is that some ISPs use load-balanced caching proxy servers. This means first that more than one user will have the same IP address and second that a user might not have the same IP address for each request. To overcome this, you might be able to use the first one or two octets of the IP address. So, if the client comes from 192.168.10.58, you can bind the session to the first two octets, 192 and 168. Another method is to bind to the User Agent string provided by the client. However, some proxies randomly change the User Agent string to help protect user privacy, and it is possible for an attacker to discover and fake this string. Yet another option is to bind to the current ASP session cookie. None of these methods is perfect, but using a combination of these certainly provides more security than using none.

Figure 3.12 (C#) and Figure 3.13 (VB.NET) show how to obtain information about the client and store it in the session state. With each request from that client, you compare the client information with the saved information and abandon the session if they do not match. You will likely need to test these to see if they work with your client base.







private void bindingToTheClient() 
{
// get the first two octets
string[] userIpArray = Request.UserHostAddress.Split('.');
string firstTwoOctets = userIpArray[0] + "." + userIpArray[1];
if (Session["firstTwoOctets"] == null &&
Session["userAgent"] == null)
{
Session["firstTwoOctets"] = firstTwoOctets;
Session["userAgent"] = Request.UserAgent;
}
else
{
if ((string) Session["firstTwoOctets"] != firstTwoOctets ||
(string) Session["userAgent"] != Request.UserAgent)
{
Session.Abandon();
Response.Redirect("default.aspx");
}
}
}






Figure 3.12: Binding to the Client: C#








Private Function bindingToTheClient()
Dim userIpArray() = Split(Request.UserHostAddress, ".")
Dim firstTwoOctets As String = userIpArray(0) + "." + userIpArray(1)
If Session("firstTwoOctets") Is Nothing And _
Session("userAgent") Is Nothing Then
Session("firstTwoOctets") = firstTwoOctets
Session("userAgent") = Request.UserAgent
Else
If Session("firstTwoOctets") <> firstTwoOctets Or _
Session("userAgent") <> Request.UserAgent Then
Session.Abandon()
Response.Redirect("default.aspx")
End If
End If
End Function






Figure 3.13: Binding to the Client:VB.NET

Security Policies




Always use a strong random-number generator for session tokens.



Use a token that does not identify the user to anyone but the server.



Use an additional MAC to ensure the authenticity of a session token.






When using forms authentication on a shared server, always use the IsolateApps setting of the machineKey element in the machine.config file.



Save client variables to help ensure binding to the same client.




Terminating Sessions












Summary:


Carefully choose a token mechanism that provides users with the best security


Threats:


Session hijacking, account hijacking, token keep-alive


At some point a session must end, at which time you should destroy the session token. There are several ways a Web session can end:



The client closes the Web browser.



The client keeps the browser open but browses to a different Web site.



The client browser sits idle long enough for the token to expire.



The server revokes the token.



When the client closes the browser, the browser discards any session tokens not marked as persistent. However, if the user just browses to a different Web site, the session tokens remain in the browser’s memory. This means that if users browse back to your site, their session tokens may still be valid. This could be a risk on a shared public computer or if the user leaves a browser window open for an extended period of time. To counter this danger, ASP.NET automatically expires a session after a predefined time, usually 20 minutes.

But if the user is on a Web page that automatically refreshes every few minutes, the session never sits idle long enough to expire. An attacker can use this capability with a session fixation attack to keep the token alive long enough to carry out the attack.

The best countermeasures are to provide three different expiration factors:



After a specific amount of idle time



After an absolute time since the token was issued



After a specified number of uses



Using these factors, you could, for example, expire a session after idle for 10 minutes, after two hours have passed since issuing the token, or after 50 hits to your Web site. Under any of these conditions, you should create a new session token or reauthenticate the user. This type of practice is commonly implemented in online banking Web sites.

Since ASP.NET automatically handles idle session timeouts, we can use session state to store the other two variables. Figure 3.14 (C#) and Figure 3.15 (VB.NET) show how to use, store, and check the absolute expiration and hit counter.







private void expiringSessions() 
{
if (Session["absoluteExpiration"] == null &&
Session["maxHitCount"] == null)
{
Session["absoluteExpiration"] =
DateTime.Now.AddHours(2); // Two hours from now
Session["maxHitCount"] = 3; // Fifty hits to the website
Session["hitCount"] = 0;
}
else
{
Session["hitCount"] = (int) Session["hitCount"] + 1;
// Check if session has expired, or exceeded max hit count
if (DateTime.Now > (DateTime) Session["absoluteExpiration"] ||
(int) Session["hitCount"] > (int) Session["maxHitCount"])
{
Session.Abandon();
Response.Redirect("default.aspx");
}
}
}






Figure 3.14: Expiring Sessions: C#








Private Function expiringSessions()
If Session("absoluteExpiration") Is Nothing And _
Session("maxHitCount") Is Nothing Then
' Two hours from now
Session("absoluteExpiration") = DateTime.Now.AddHours(2)
' Fifty hits to the website
Session("maxHitCount") = 50
Session("hitCount") = 0
Else
Session("hitCount") = Session("hitCount") + 1
' Check if session has expired, or exceeded max hit count
If DateTime.Now > Session("absoluteExpiration") Or _
Session("hitCount") > Session("maxHitCount") Then
Session.Abandon()
Response.Redirect("default.aspx")
End If
End If
End Function






Figure 3.15: Expiring Sessions: VB.NET

This code checks to see if the current session has an absolute expiration and hit count. If it doesn’t, the code creates one and saves it with the session state. When the next request comes in, it again checks those values until the client exceeds either. At that point, we expire the session and require the user to again authenticate to the server.

Security Policies




Use timeouts to expire idle sessions.



Use absolute timeouts to set a limit on active sessions.



Use hit counters to limit the use of any single token.



/ 96