
Asp Net 2.0 Security Membership And Role Management
.pdf
Chapter 7
Session State Mode |
Required CAS Permissions |
Required Trust Level |
|
|
|
In process |
None |
Minimal |
Sql Server |
None |
Medium |
State Server |
None |
Medium |
Custom |
Depends on custom provider |
Minimal. Custom |
|
implementation |
providers can be more |
|
|
restrictive if desired. |
|
|
|
Serialization and Deserialization Requirements
Session state is a lot like no-compile pages in ASP.NET 2.0; both features involve only trusted ASP.NET code running on the stack, which means that without extra protections, a savvy and malicious developer could trick ASP.NET into running privileged code. If you think back to the discussion on processRequestInApplicationTrust in Chapter 3, the solution for no-compile pages (and for that matter any type of .aspx page) was for ASP.NET to call PermitOnly on the PermissionSet representing the permissions granted in the application’s trust policy.
Session state also internally checks the value of processRequestInApplicationTrust. If this setting is true (by default it is true, and unless there is a specific reason for it, you should not change this setting), session state calls PermitOnly prior to either serializing or deserializing session state data. This means that any types deployed in the GAC that also implement custom serialization logic are still restricted to the permission set defined by the application’s trust policy when the types are serialized or deserialized by the Session state feature. Because session state uses binary serialization, this means any GAC’d types with custom implementations of ISerializable cannot be lured into performing a privileged operation through the use of session state in a partial trust application.
This protection closes a potential loophole with storing an instance of a GAC’d type in session state. If enough was understood about the internals of the GAC’d type, then when either of the out-of-process session state providers serialized the GAC’d type prior to saving it, session state would inadvertently trigger privileged code inside of the GAC’d type’s serialization logic. With the PermitOnly in effect though, a developer can no longer use session state to make an end-run around the application’s trust policy.
To highlight this, you can create a simple class that attempts to connect to SQL Server:
[Serializable()]
public class SomeObject : ISerializable
{
public SomeObject() { }
protected SomeObject(SerializationInfo info, StreamingContext context)
{
SqlConnection conn =
new SqlConnection(“server=.;database=pubs;Integrated Security=true”); SqlCommand cmd = new SqlCommand(“select * from authors”, conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
conn.Close();
302

Session State
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(“foo”, “bar”);
SqlConnection conn =
new SqlConnection(“server=.;database=pubs;Integrated Security=true”);
SqlCommand cmd = new SqlCommand(“select * from authors”, conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
conn.Close();
}
}
The sample class is marked with the Serializable attribute, indicating that it supports being binary serialized. Inside the ISerializable method associated with serialization, and in the special ISerializable constructor, the class attempts to execute a command against SQL Server. This operation results in a demand for SqlClientPermission, which you can use to show the effects of enforcing the application trust policy.
After marking the class’s assembly with the APTCA attribute, signing it with a strong name, and adding it to the GAC, you can create a sample web application that makes use of this class. The web application will be configured to run in partial trust and use SQL Server session state.
<trust level=”Medium_Custom”/>
<sessionState mode=”SQLServer” sqlConnectionString=”server=.;Integrated Security=true” timeout=”30”/>
<compilation debug=”true”> <assemblies>
<add assembly=”BusinessObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9cd23ad80158bbfe”/>
</assemblies>
</compilation>
Using SQL Server–based session state means that the session state feature will use binary serialization to load and store any objects placed inside of session state. A simple page that makes use of the GAC’d type is shown here.
protected void Page_Load(object sender, EventArgs e)
{
if (Session[“ObjectReference”] != null)
{
object o2 = Session[“ObjectReference”];
}
SomeObject obj = new SomeObject(); Session[“ObjectReference”] = obj;
}
303

Chapter 7
The page stores a reference to the GAC’d type inside of Session[“ObjectReference”]. Because the page attempts to get a value first, this triggers deserialization of the object instance within the session state feature. In ASP.NET 2.0, there was a slight optimization added to the out-of-process session state providers. These providers load only the raw blob data when the AcquireRequestState event occurs in the HTTP pipeline. However, the session state providers will not attempt to deserialize the blob into an actual object instance until a piece of code runs and explicitly accesses the session state variable.
Attempting to get an instance of the GAC’d type from session state triggers this lazy deserialization. The page also creates an instance of the GAC’d type and stores it in session state so that later during either the ReleaseRequestState or EndRequest phase the session state provider will have to serialize the object instance.
If you run the page code while the custom trust policy still includes SqlClientPermission, the page runs without a problem. However, if you remove the SqlClientPermission from the trust policy file, the next time you run the page it will fail. Depending on whether you run the page for a brand new session, or run the page after session data already exists in the database, the attempt to retrieve an instance of SomeObject fails, or the request fails after the page has run when an attempt is made to serialize the instance of SomeObject.
Overall, the sample highlights the fact that you should not use GAC’d types with out-of-process session state in partially trusted applications if the trusted type carries out any kind of privileged operation using custom serialization. Realistically, this scenario probably will not affect most developers because normally serializable types don’t access external resources from inside of custom serialization logic. However, you may encounter custom types written by a development organization or third-party vendor that have this behavior.
If you have an application that was working with OOP session state under full trust, but the application stops working after you drop to High trust or lower, the new application trust policy enforcement in ASP.NET 2.0 session state may be the problem. Also note that although the sample shown earlier used custom serialization with ISerializable, the same issue arises if you implement custom serialization using the new version tolerant serialization (VTS) mechanism in the 2.0 version of the Framework. Essentially different methods are involved, but you still have the same effect with ASP.NET 2.0 enforcing a PermitOnly prior to any VTS-related methods being called.
Database Security for SQL Session State
SQL Server session state is the most common out-of-process session state mode used by developers. As a result of its popularity, a few quick notes around the database store are in order. The thing to keep in mind when using SQL Server session state is that the information sitting in the session state database is effectively a snapshot of various pieces of application data associated with individual users. If you have sensitive information or privacy related information stored in session, the potential exists for other malicious code to reach into the SQL Server session state store and retrieve it.
Prior to ASP.NET 2.0, you could store session state inside of tempdb or inside of a specific database called ASPState. Both of these deployment options open up the potential for session data in one application being accessible from another application. The specific risk is that each ASP.NET application that is pointed either tempdb or ASPState has to be configured with dbo-level credentials. The entire schema created by the SQL Server-based session state feature is owned by the dbo user. Furthermore, the code inside of the SQL Server session state provider prepends all of the stored procedure names with dbo.
304

Session State
As a result, if multiple ASP.NET applications are configured to point at one of the common session state databases, page code inside of these ASP.NET applications can easily issue a select statement directly against the session state database. Take the following simple command:
Select * from ASPStateTempSessions
If a page in an application issues this command using ADO.NET, it now has a DataSet or SqlDataReader that contains the raw object data. In ASP.NET 2.0, the SessionItemShort and SessionItemLong columns contain the serialized representations of session state objects. The blob values in these columns are not directly usable with the binary formatter; however, with a little snooping around and reverse engineering, you can pretty easily tease out the basic structure of the data in these fields.
After a malicious user has done this, that user can read selected byte sequences from these columns and feed them to the BinaryFormatter. For a single application using one of the default session state databases, this isn’t a security problem because the single application is supposed to be able to manipulate its own session data. Jumping through hoops to do this through ADO.NET and the BinaryFormatter doesn’t expose any data. However, chances are that if multiple applications are using SQL Server session state, development team A did not intend to allow its data to be snooped by the application written by development team B.
And taking paranoia one step further, in scenarios where multiple applications share the same session state data store, it is also possible for one application to synthesize the byte representation for serialized data and inject it into one of the session state rows containing data for another application. For example, maybe a marketing oriented application uses the same session state database as a web-based loan application does. The marketing application could be crafted so that a malicious developer could write code to edit a row of session data associated with the loan application — maybe to do something along the lines of editing credit information that is temporarily being stored in the session state database for use by an online approver.
So, what does this really boil down to for developers using ASP.NET 2.0? Fortunately, ASP.NET 2.0 added the ability to deploy session state into any arbitrary database (not just tempdb and ASPState). As a result, it is very easy for ASP.NET 2.0 applications to segment session state stores and prevent different applications from peeking into another application’s session data. Locking down session state data in ASP.NET 2.0 should include the following steps:
1.Install the session state schema in separate databases when applications handling sensitive data may be storing some of this information temporarily into session state. You can use the Framework’s aspnet_regsql.exe tool, located in the install directory, to do this using the - sstype c and -d <database> options.
2.In the configuration for your web applications, add the new attribute allowCustomSqlDatabase to the <sessionState /> configuration element. Doing so allows you to enter the extra database information into the sqlConnectionString attribute of the <sessionState /> element. If you don’t set allowCustomSqlDatabase to true though and you attempt to use a custom database (something other than tempdb or ASPState), an exception is thrown at runtime.
3.Configure the connection credentials for the custom session state database so that other ASP.NET applications cannot access it. You can accomplish this by running the ASP.NET application in its own worker process with a unique identity, by setting a unique application impersonation identity for the application, or by using a unique set of standard SQL Server credentials in the connection string.
305

Chapter 7
A sample configuration that would allow you to isolate a session state database to a single ASP.NET application is shown here:
<sessionState mode=”SQLServer” allowCustomSqlDatabase=”True” sqlConnectionString=”server=.;Integrated Security=true;database=mycustomdb” />
<identity impersonate=”true” userName=”user” password=”password”/>
This configuration tells the session state feature that is allowable to have a database attribute in the connection string that points at something other than aspnetdb or ASPState. Because application impersonation is also configured, the SQL session state provider will connect to the database using the configured application impersonation credentials. As long as no other ASP.NET applications use the same set of application impersonation credentials, the session state data is limited to only one application.
As a side note, in ASP.NET 2.0 the impersonation behavior of the SQL provider was tweaked a bit. The SQL provider by default always suspends client impersonation prior to communicating with SQL Server. This means if you have client impersonation configured in your application (for example, you are using Windows authentication and <identity impersonate=”true” />), the SQL server provider reverts to the process identity (or application impersonation identity if application impersonation is in effect) prior to communicating with SQL Server. If for some reason you want to retain the old ASP.NET 1.1 impersonation behavior, you can use the new “useHostingIdentity” attribute on the <sessionState /> element and set it to false.
So as long as the underlying process identity of the ASP.NET application or the application impersonation identity has dbo privileges in the SQL Server session state database, you can safely use integrated security with the session state connection string. This eliminates the need to add all your Active Directory user accounts to the session state database if you choose to use integrated security with your session state database (though there were also other bugs in ASP.NET 1.1 that made it difficult to use integrated security with session state).
Security Options for the OOP State Ser ver
The out of process session state server runs as an NT service using the aspnet_state.exe executable. Because the state service itself simply listens on a socket, it doesn’t have any built-in security protections that prevent arbitrary hosts on the network from connecting to the state server. Unlike SQL Server, the OOP state server has no concept of integrated security. As a result, server administrators should use other network security mechanisms such as IP security (IPSEC) rules to prevent random machines from attempting to connect to the state server.
Beyond network layer security mechanisms, there are two other security options you should be aware of when using the OOP state server. The first thing you should do is change the default network port that the state server listens on. By default, the state server listens on port 42424. Because this is a well-known port for the state server, you can make the state server listen on a different port by finding the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters
306

Session State
Underneath this key, you can add a new DWORD registry value named Port. Set the actual value to a different port number that you want the state service to listen on. With this change a malicious network user now has to perform a port scan in order to find the state service as opposed to just connecting to port 42424.
Because the OOP state server is usually deployed to support multiple remote web servers, you will quickly find out that your remote OOP state server doesn’t work out of the box. The reason for this is that the ASP.NET state service by default only allows connections from localhost. This prevents server administrators from installing ASP.NET on machines and then unknowingly having state servers sitting around listening for remote connections on the network. To allow an instance of the ASP.NET state service to accept remote connections you can add another DWORD registry value under the Parameters key called AllowRemoteConection. Setting AllowRemoteConnection to “1” enables the state service to accept remote network connections.
Summar y
Although session state is usually considered just a handy item in the developer’s arsenal of ASP.NET tools, there are a number of subtle security issues to keep in mind. ASP.NET 2.0 introduced cookieless support for the session state feature. However, as with other features that support cookieless behavior, the potential to accidentally leak cookieless tickets is a risk. As a result, if you choose cookieless sessions, do not store any private or privileged information inside session state; this minimizes the impact of other users accidentally reusing a cookieless session ticket.
Session state has the concept of session ID reuse. In cookied modes, session IDs are shared across all applications running under a common DNS host name. This means that even if you call Session
.Abandon in one application, the session identifier remains in the cookie and the identifier continues to be used by all applications. However, in the application where Abandon was called, the session data is deleted, so you end up with fresh session data the next time the user returns to that specific application.
For applications that use cookieless session identifiers, ASP.NET session state doesn’t reuse session identifiers by default. Instead, if you call Abandon or access an application with an expired session identifier, session state detects this and issues a new session identifier. This behavior is intended to minimize the potential for a user to accidentally or intentionally use a cookieless session identifier that was originally issued to a different user.
If you use in-process session state or the out-of-process session state server, be aware of the potential for denial of service (DOS) attacks. DOS attacks can be launched against these types of session states in an attempt to force an excessive amount of memory consumption on your servers. A simple mitigation is to start using session state only after a user has logged in; prior to that point, if you never access session state, ASP.NET does not allocate any space in memory for session state data. Also, attackers usually want to remain anonymous and thus tend to avoid launching any of type of attack that requires an identifiable account on the website.
Last, be aware of the potential for exposing session state data in SQL Server to other applications that share the same back-end session state database. With the new support in ASP.NET 2.0 for custom databases, it is easy to give each application its own session state database, thus preventing one application from snooping around in the session data of another application.
307


Security for Pages and Compilation
A good deal of writing a secure page depends on often discussed topics like input validation, handling malicious input, preventing SQL injection attacks, and so on. However, ASP.NET provides some lesser known configurable security features that add a degree of extra security to your pages. This chapter will review some security features for pages and compilation that have been around since ASP.NET 1.1, as well as new security features in 2.0.
The topics that will be covered include:
Request validation and viewstate protection
Options for securing page compilation
Protecting against fraudulent postbacks
Site navigation security
Request Validation and Viewstate
Protection
Two well-known protection mechanisms for ASP.NET pages are request validation and viewstate protection. Request validation has always been a bit of a mystery to developers, so in this section you will see exactly how it works in ASP.NET 2.0. Viewstate protections have been around since ASP.NET 1.0, but there have been some new features added for viewstate protection in ASP.NET 2.0.

Chapter 8
Request Validation
Request validation is meant to detect strings posted to a web server that may contain suspicious character sequences. In general, request validation attempts to detect string information, which if subsequently rendered on a page, could result in a successful cross-site scripting attack. Request validation is not a gen- eral-purpose input validation mechanism. Constraining input to a valid set of values and preventing data from containing SQL injection attacks are still tasks the developer must implement.
By default, request validation is turned on. You can change the request validation settings with either the validateRequest attribute of the <pages /> element or the ValidateRequest attribute of the @Page directive. In general, you should keep request validation turned on, and turn it off on selected pages where you are encountering problems. The request validation feature checks the following Request collections for suspicious strings:
Form variables
Query string variables
The Cookie collection
The actual string checks are pretty straightforward. Request validation looks for character sequences such as:
< followed by an exclamation point — For example, <! is not allowed.
< followed by the letters a through z — The theory is that a character sequence that starts out looking like <s could potentially be the beginning of a <script> element for example. So in general, the request validation feature pessimistically rejects these types of character sequences.
& followed by a pound sign — So, the sequence “{” would be rejected. This prevents encoding based attacks, where a person attempts to submit script code as a sequence of HTMLencoded characters in the hope that it will subsequently be accidentally decoded prematurely.
At one point in the ASP.NET 2.0 development cycle, there were many more stringent checks added to request validation. However, these checks were backed out because for every case that ASP.NET was protecting against, you could come up with an innocuous reason for submitting the string in a form. For example, at one point with ASP.NET 2.0 if you submitted text in a form that said “The onclick event looks like ‘onclick=alert(‘hello world’)’” the server would reject it. Unfortunately, that level of parameter checking ended up causing early developers to turn off request validation entirely in an attempt to get their forms working. So instead, request validation was reverted to a simpler set of validation checks — the idea being that it was better to have everyone benefit from some level of request validation rather than forcing many developers to turn off the feature.
Even with the basic set of request validations, you can still run into problems if you are writing a control like a rich text box. Many of the rich text editors allow users to type in basic HTML tags such as <b>. Of course if you try this with request validation turned on, the page request will promptly fail because ASP.NET detects the < characters followed by a letter. If you implement rigorous input validation in your application though, you could safely turn off request validation for this case.
However, a more secure approach to this problem is to pre-encode strings on the client using your own custom mapping. For example, if you write a rich text editor that supports bold and italic characters, just before the form is submitted you could convert all instances of <b> to [html bold] and all instances of
310

Security for Pages and Compilation
<i> to [html italic]. Then on the server side you would search for that string token and convert it the correct HTML markup. Doing this is a bit laborious because you have to preprocess and postprocess all of the strings that you care about. But it does have the benefit of allowing request validation to stay in place. Also, this type of development work will make it very clear to you the specific subset of strings that you want to allow in your application.
Securing viewstate
The ability to protect viewstate with a hash signature and encryption has been available since ASP.NET 1.0. You are probably very familiar with how it works by now, so rather than rehashing the basics, I will cover what’s new in ASP.NET 2.0 as well as one dusty corner of viewstate security that some developers don’t know about.
By default, all pages have their EnableViewStateMac property set to true. Combined with the default <machineKey /> setting of SHA1 for the validation attribute, this means .aspx pages include a hash value along with their viewstate data. The only new thing in this regard for ASP.NET 2.0 is the addition of the AES algorithm to the <machineKey /> section. Although it looks a bit strange, you can now set the validation attribute in ASP.NET 2.0 to SHA1, MD, 3DES, or AES. Because older versions of ASP.NET overloaded the validation attribute for viewstate protection and forms authentication protection, you end up with options for specifying symmetric encryption algorithms in an attribute that theoretically references one-way hashing algorithms.
Forms authentication ignores the nonhashing options for the validation attribute — but the Page class does make use of the encryption options. If you set either 3DES or AES in the validation attribute, then assuming your .aspx pages have EnableViewStateMac set to true, ASP.NET will first hash the page’s viewstate data using SHA1 (HMACSHA1 to be precise), and then it will encrypt both the viewstate and the hash value using either 3DES or AES. Unlike the companion decryption attribute in <machineKey />, for the validation attribute you have to explicitly choose the type of encryption algorithm you want to use. There is no capability for ASP.NET to auto-select a viewstate encryption algorithm on your behalf.
There is an extra option that developers can use in their code to make viewstate more secure: the ViewStateUserKey property. Although this property is not new in ASP.NET 2.0, many developers are unaware of its existence. When viewstate is being hashed you can add a per-user identifier to the information that is used when hashing viewstate. By default, when ASP.NET hashes the viewstate for the page, it includes extra information derived from the .aspx page as part of the stream of data that is being hashed. This mechanism ensures that the viewstate from one page can’t be posted to a different page (excepting the new cross-page posting feature in ASP.NET 2.0).
This default protection though won’t prevent a malicious user from hijacking the viewstate data shown in one user’s browser and then attempting to submit it in a separate browser. For example if a web application automatically trusts all of its postback data and doesn’t perform any additional security checks, it becomes possible for someone to steal the viewstate form variable and then replay it to trigger actions on the server that a user may normally not have rights to. You have the option of injecting your own user-specific information into the data stream that is being hashed by setting a value on the ViewStateUserKey property. Because the intent of the property is to prevent user A from posting user B’s viewstate back to the server, the logical choice for a ViewStateUserKey value is the value from
User.Identity.Name.
311