Introduction
ASP.NET is a very potential technology for developing web applications. Now a day many applications are being developed in ASP.NET. For any web application Security is a very important feature. Especially those web applications which contain some sensitive user information or any financial data, security is a crucial aspect for them. Some times extra security features provided in the web application may degrade the overall performance of the web application. So one should properly choose security features so as the application is reliable as well as performance is also not very much affected. This article considers the security features in ASP.NET 2.0. But these are applicable to higher versions also.
We can divide the whole Security feature analysis to following major categories:
- Authentication
- Configuration
- Data Access Security
- Code Access Security
- Exception Handling
- Communication Security
Authentication
Authentication means to check whether the user is the registered user or the trusted user. There are two types of Authentication provided by ASP.NET 2.0
- Forms Authentication
- Windows Authentication
One should use windows authentication, wherever it is possible. Because Windows Authentication uses the active directory or similar structure to store the user name and passwords, and we need not to create password policies and password encryption to store them in database. ASP.NET uses identity object to represent the current user’s identity. This object internally implements the System.Security.Principal.IIdentity interface to represent the authenticated user. We can access the authenticated user through HttpContext.Principal.IIdentity regardless of authentication method, whether it is Forms authentication or Windows authentication.
Forms Authentication
In forms authentication, user name and password combination are stored in database or configuration file. The user provides the credentials and that are validated against these stored credentials. ASP.NET 2.0 provides MembershipProvider abstract class. The membership feature has built-in providers for user stores including SQL Server, Active Directory, and Active Directory Application Mode (ADAM).
We should consider following points before implementing the forms Authentication:
- We should use ASP.NET built in class MemberShip provider rather than implementing our own custom logic for validating credentials.
- In the authentication user credentials are passed across the network, so we should use the SSL to send the credentials across the network.
- We should try to reduce the session time out, if we can not use Secure Socket Layer.
- Passwords should not be directly stored in configuration files. If it is directly stored anywhere then the password should be properly encrypted and logic for encryption and decryption should be handled.
- Strong password policies should be enforced so as to get more security.
- We should never persists Authentication cookies, because they are stored in user’s profile and can be stolen by some attacker.
Windows Authentication
We should use Windows Authentication where it is possible. Because by windows authentication we are having the benefits of Active directory, enforceable account and password policy and one centralized storage of credentials. For making Secure Windows authentication we can consider the followings:
- Password length and complexity should be set.
- There should be some password expiration. So that after a particular period of time password keeps changing.
Where possible, you should use Windows authentication because this enables you to use an existing identity store such as your corporate Active Directory, it enables you to enforce strong password policies, you do not need to build custom identity store management tools and passwords are not transmitted over the network.
Configuration
There are lots of configuration settings for any ASP.NET web application. These settings are very important for any web application. There is connection string, the most important part for database connectivity. Some times developers directly give database credentials in plane in web.config file. That is a bad practice, if you are giving the credentials in connection string then we should encrypt the connection string with the help of following command:
aspnet_regiis -pe "connectionStrings" -app "/<Name of the application>"
And now if you want to decrypt the connection string back, you can run the following command:
aspnet_regiis -pd "connectionStrings" -app "/<Name of the application>"
The modified connection string will look like this:
<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>R7cyuRk+SXJoimz7wlOpJr/YLeADGnwJVcmElHbrG/B5dDTE4C9rzSmm
TsbJ9Xcl2oDQt1qYma9L7pzQsQQYqLrkajqJ4i6ZQH1cmiot8ja7Vh+yItes7TRU1AoXN9
T0mbX5H1Axm0O3X/285/MdXXTUlPkDMAZXmzNVeEJHSCE=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>
d2++QtjcVwIkJLsye+dNJbCveORxeWiVSJIbcQQqAFofhay1wMci8FFlbQWttiRYFcvxrmVfNS
xoZV8GjfPtppiodhOzQZ+0/QIFiU9Cifqh/T/7JyFkFSn13bTKjbYmHObKAzZ+Eg6gCXBxsVEr
zH9GRphlsz5ru1BytFYxo/lUGRvZfpLHLYWRuFyLXnxNoAGfL1mpQM7M46x5YWRMsNsNEKTo/
PU9/Jvnh/lT+GlcgCs2JRpyzSfKE7zSJH+TpIRtd86PwQ5HG3Pd2frYdYw0rmlmlI9D
</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
Data Access Security
By the Data access Security we mean to say that unauthorized illegal access to database should not be provided. Some hackers may inject the harmful code through forms that can access the database illegally. So every data should be properly validated before submitting it to back end.
For checking such securities we should consider following points:
- All data should be validated for length, input and format before doing any processing.
- We should use proper regular expression class for validating the data for formatting at back end.
- Dynamic query generation on the basis of data provided by the user should be avoided, means whatever parameters user is providing should not be directly involved in a query generation.
- Use the stored procedures wherever possible, rather than just batch of queries.
- If any error occurs then do the proper roll back, so database will never be in an in consistent state.
- Use the views with restricted privileges rather than using the table names in Database stored procedures and direct SQL queries.
- Create a window account that contains the least permissions on each object and use that account for accessing the database.
- Even database should not directly contain any passwords. If passwords are there then they must be in encrypted form.
- Try to use windows authentication for database access also, because it provides some benefits, like storing of credentials at centralized and safe place and also password policies and all that are already implemented by the active directory.
- Proper exception handling must be there for each and every data accessing function. Best practice is we should create the log file for logging the exceptions. I am giving an example code here used to create the log files.
using System;
using System.IO;
using System.Text;
namespace DataAccessLayer
{
public class CreateLogFiles
{
private static int intCount;
private static String strError;
private string sLogFormat;
private string sErrorTime;
public CreateLogFiles()
{
sLogFormat = DateTime.Now.ToShortDateString().ToString() + " " +
DateTime.Now.ToLongTimeString().ToString() + " ==> ";
string sYear = DateTime.Now.Year.ToString();
string sMonth = DateTime.Now.Month.ToString();
string sDay = DateTime.Now.Day.ToString();
sErrorTime = sYear + sMonth + sDay;
}
public void ErrorLog(string sPathName, string sErrMsg)
{
if (sPathName == "")
{
sPathName = "<Path>\\ErrorLog";
}
sErrMsg = DateTime.Now.ToString() + " ==> " + sErrMsg;
try
{
StreamWriter sw = new StreamWriter(sPathName + sErrorTime + ".txt", true);
lock (sw)
{
sw.WriteLine(sErrMsg);
sw.Flush();
sw.Close();
}
}
catch (Exception)
{
}
}
public void CreateErrorString(String msg)
{
if (intCount < 21)
{
intCount++;
strError += DateTime.Now.ToString() + "==> " + msg + "\r\n";
}
else
{
CreateLogFiles obj = new CreateLogFiles();
obj.ErrorLog("<Path>\\ErrorLog", strError);
strError = "";
intCount = 0;
}
}
}
}
The above code contains two methods “public void ErrorLog(string sPathName, string sErrMsg)”, and “public void CreateErrorString(String msg)” . The first method should be used when there is low user load or number of occurrences of error is less and the second one is used when higher number of concurrent users is there.
Code Access Security
By code Access Security we mean to say restrict the access to system resources which our code may do, and the type of privileged operations that our code may do. These restrictions are independent of user who calls the code. For using the code access security we should consider the following guidelines:
- If our application only uses managed code then we can use the various trust levels to limit the exposure to security attacks. For that we can add the following line in our web.config file.
<trust level="Full|High|Medium|Low|Minimal" />
- We should a trust level that does not exceed our application’s requirements. We can use the Permission calculator tool (Permcal.exe) provided by ASP.NET to calculate the required permissions for any code.
- We should use Medium Trust in shared networks.
- We can use health monitoring and auditing events of ASP.NET to check some sensitive code accessibility by various users. What it does, simply log the events whenever they access some resource or something restricted.
Internally these events log the errors and warnings of web Application in Event Viewer.
- To use code access security we should follow following steps in the application:
First we should identify the permissions that our application requires. We can do that by manually analyzing the permissions or use Permcal.exe tool. To see the required permissions for an assembly we should run following command from Visual Studio Command prompt:
Permcal –Show <assembly>
The output of above command is like this:
<assembly />
<namespace name="ClassLibrary1" />
<type name="Class1" />
<method sig="instance void test()" />
<method sig="instance void .ctor()">
<demand />
<permissionset class="System.Security.PermissionSet" version="1" />
<ipermission class="System.Security.Permissions.RegistryPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" read="true" />
<ipermission class="System.Security.Permissions.FileIOPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" unrestricted="true" />
</permissionset />
</demand />
<sandbox />
<permissionset class="System.Security.PermissionSet" version="1" />
<ipermission class="System.Security.Permissions.RegistryPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" read="true" />
<ipermission class="System.Security.Permissions.FileIOPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" unrestricted="true" />
</permissionset />
</sandbox />
</method>
</type />
</namespace />
</assembly />
Examine the permissions listed in the <demand> element. After that try to evaluate the trust level that our application may require on the basis of following table .
Trust Level |
Key Capabilities and Restrictions |
Full |
No restrictions imposed by code access security. |
High |
No unmanaged code. No enterprise services. Can access Microsoft SQL Server and other OLE DB data sources. Can send e-mail by using SMTP servers. Very limited reflection permissions. No ability to invoke code by using reflection. A broad set of other framework features are available. Applications have full access to the file system and to sockets. |
Medium |
Permissions are limited to what the application can access within the directory structure of the application. No file access is permitted outside of the application's virtual directory hierarchy. Can access SQL Server. Can send e-mail by using SMTP servers. Limited rights to certain common environment variables. No reflection permissions whatsoever. No sockets permission. To access Web resources, you must explicitly add endpoint URLs—either in the originUrl attribute of the <trust> element or inside the policy file. |
Low |
Intended to model the concept of a read-only application with no network connectivity. Read only access for file I/O within the application's virtual directory structure. |
Minimal |
Execute only. No ability to change the IPrincipal on a thread or on the HttpContext. |
Configure the ASP.NET application for that particular trust level. Use the following node in web,config.
...
<system.web>
...
<trust level="Medium" />
...
</system.web>
...
Exception Handling
Exception handling is also an important part of the security. ASP.NET provides us the functionality to handle all the unhandled exceptions at once. We can add an Global Application Error Handler in global.asax, and that will handle all the exceptions that are occurring in our application. I am writing an sample example for Global Error Handler, this should be added in the file global.asax.
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError().GetBaseException();
CreateLogFiles Err = new CreateLogFiles();
try
{
HttpException httpException = (HttpException)ex;
httpCode = httpException.GetHttpCode();
}
catch
{
}
finally
{
Err.ErrorLog("<path>\\ErrorLog", DateTime.Now.ToString() +
" User : " + name[1].ToString() + " ==>" + Server.GetLastError().TargetSite.ToString()
+ " Error Status code : " + httpCode.ToString() + ex.Message);
switch (httpCode)
{
case 500: Response.Redirect("../../../ErrorPages/InternalServerError.htm"); break;
case 403: Response.Redirect("../../../ErrorPages/NoAccess.htm"); break;
case 404: Response.Redirect("../../../ErrorPages/PageNotFound.htm"); break;
default: Response.Redirect("../../../ErrorPages/ErrorStatus.htm"); break;
}
}
}
CreateLogFiles is the same class as we added at the starting for logging the errors. Apart from that we can write separate exception handling codes for each function, or wherever it is required. We can add custom error node to web.config to redirect the users to some error page rather than displaying the error.
Communication Security
While using the sensitive data we should consider the following, we should use the SSL or IPSec. And also we should consider optimizing those pages which uses the SSL. For pages where you must use SSL, We should consider the followings:
- We should make the page size as small as possible.
- We should avoid using images or video contents on such pages.
Summary
This article helps the developer quickly review the security features for the ASP.NET web application. Developer can quickly implement security features for the web application, rather than going through the whole big books for Security features of ASP.NET.