ASP.NET 2.0 Security Inspection Questions - Sensitive Data

From Guidance Share

Jump to: navigation, search

- J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Jason Taylor, Rudolph Araujo


Contents

Sensitive Data Vulnerabilities and Implications

Vulnerability

Implications

Storing secrets when you do not need to

This drastically increases the security risk. Do not store secrets unnecessarily.

Storing secrets in code

If the code is on the server, an attacker may be able to download it. Secrets are visible in binary assemblies.

Storing secrets in clear text

Anyone who can log on to the server can see secret data.

Passing sensitive data in clear text over networks

Eavesdroppers can monitor the network to reveal and tamper with the data.


If the code you are reviewing uses sensitive data, such as connection strings and account credentials, you should make sure that the code protects the data and ensures that it remains private and unaltered. Table 10 shows a set of common sensitive data vulnerabilities and their implications.

The following questions help you to identify vulnerable areas:

  • Does the code store secrets?
  • Is sensitive data stored in predictable locations?
  • Does the code store sensitive data in view state?
  • Does the code pass sensitive data across Web pages?


Does the code store secrets?

If an assembly stores secrets, review the design to make sure that it is absolutely necessary to store the secret. If the code must store a secret, review the following questions to make sure that it does so as securely as possible:

  • Does the application store secrets in code?

Are there secrets or critical intellectual property embedded in the code? Managed code is easy to decompile. It is possible to recover code from the final executable that is very similar to the original code. Any sensitive intellectual property or hard coded secrets can be stolen with ease. An obfuscator can make this type of theft more difficult, but cannot entirely prevent it. Another common problem is to use hidden form fields thinking this information will not be visible to the user.

The following is an example of bad code containing hard-coded account credentials:

IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
string userName = "joe", domainName = "acmecorp", password="p@Ssw0rd";
const int LOGON32_PROVIDER_DEFAULT = 0;
// This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
const int SecurityImpersonation = 2;
tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, 
                            domainName, 
                            password, 
                            LOGON32_LOGON_INTERACTIVE, 
                            LOGON32_PROVIDER_DEFAULT,
                            ref tokenHandle);
 
  • How does the code encrypt secrets?

Verify that the code uses DPAPI to encrypt connection strings and credentials. Do not store secrets in the Local Security Authority (LSA), because the account used to access the LSA requires extended privileges. For information on using DPAPI, see "How To: Create a DPAPI Library" in the "How To" section of Microsoft patterns & practices Volume I, Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication or How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI.


  • Does the code store secrets in the registry?

If the code stores secrets in HKEY_LOCAL_MACHINE, verify that the secrets are first encrypted and then secured with a restricted ACL. An ACL is not required if the code stores secrets in HKEY_CURRENT_USER because this registry key is automatically restricted to processes running under the associated user account.


  • Does the code eliminate secrets from memory?

Look for failure to clear secrets from memory after use. Because the common language runtime (CLR) manages memory for you, this is actually harder to do in managed code than it used to be in native code. To make sure that secrets are adequately cleared, verify that the following steps have been taken:

  • Strings should not be used to store secrets; they cannot be changed or effectively cleared. Instead the code should use a byte array or a CLR 2.0 SecureString.
  • Whatever type the code uses, it should call the Clear method as soon as it is finished with the data.
  • If the secret is paged to disk, it can persist for long periods of time and be difficult to completely clear. Make sure that GCHandle.Alloc and GCHandleType.Pinned are used to keep the managed objects from being paged to disk.


Is sensitive data stored in predictable locations?

Sensitive data should be stored and transmitted in encrypted form; anything less invites theft. For example, a common error is to store database server passwords in the ASP.NET Web.config file, as shown in the following example.

<connectionStrings>
 <add name="MySQLServer" 
      connectionString="Initial Catalog=finance;data 
      source=localhost;username='Bob' password='pwd';"
      providerName="System.Data.SqlClient"/>
</connectionStrings>
 

Instead, the connection strings should be encrypted with the Aspnet_regiis utility. The command syntax is:

aspnet_regiis -pe "connectionStrings" -app "/MachineDPAPI" -prov "DataProtectionConfigurationProvider"

The Web.config file after encryption should be similar to the following.

<!—web.config after encrypting the connection strings section -->

...

<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
 <EncryptedData>
   <CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAexuIJ/8oFE+sGTs7jBKZdgQAAAACAAAAAAADZgAAqAAAABAAAAAKms84dyaCPAeaSC1dIMIBAAAAAASAAACgAAAAEAAAAKaVI6aAOFdqhdc6w1Er3HMwAAAAcZ00MZOz1dI7kYRvkMIn/

BmfrvoHNUwz6H9rcxJ6Ow41E3hwHLbh79IUWiiNp0VqFAAAAF2sXCdb3fcKkgnagkHkILqteTXh</CipherValue>

   </CipherData>
 </EncryptedData>
</connectionStrings>

... Similarly, the code should not store forms authentication credentials in the Web.config file, as illustrated in the following example.

<authentication mode="Forms">
  <forms name="App" loginUrl="/login.aspx">
     <credentials passwordFormat = "Clear" 
        <user name="UserName1" password="Password1"/>
        <user name="UserName2" password="Password2"/>
        <user name="UserName3" password="Password3"/>
     </credentials>
  </forms>
</authentication>
 

Instead, use an external store with well-controlled access, such as Active Directory or a SQL Server database.


Does the code store sensitive data in view state?

The code should not store sensitive data in view state. It should use server-side storage instead. If the application must store sensitive data in view state, make sure that view state is tamper proof and encrypted. Verify the following settings: enableViewStateMac="true" (the default setting) and viewStateEncryptionMode="Auto". Also verify that the code invokes Page.RegisterRequiresViewStateEncryption when encryption is required.

Do not use code similar to the following example.

<pages buffer="true" enableSessionState="true"
      enableViewState="true" enableViewStateMac="false"
      autoEventWireup="true" validateRequest="true"/>
 

Instead, use code similar to the following.

<pages buffer="true" enableSessionState="true"
      enableViewState="true" enableViewStateMac="true"
      autoEventWireup="true" validateRequest="true"
      viewStateEncryptionMode="Auto" />


Does the code pass sensitive data across Web pages?

The code should not use a hidden object to store sensitive data, and then use view state to pass it across pages. The following bad code illustrates this practice.

...
protected System.Web.UI.WebControls.TextBox sensitiveDataField;
clientSessionId.Visible = false;
...
if (userIsAuthorized)
{
 sensitiveDataField.Text = sensitiveData.ToString();
}
...
 

Instead, use server-side state management to pass sensitive data, as shown in the following example.

<sessionState mode="InProc" ... cookieless="true" />
...
if (userIsAuthorized)
{
 Session[sensitiveDataId] = sensitiveData.ToString();
}
...
Personal tools