ASP.NET Security: 8 Ways to Avoid Attack
Building
ASP.NET Web applications has never been easier. Visual Studio.NET hides so many
technical details behind the scenes that developers need only concentrate on
the core business logic. However, hackers are on the lookout for any
opportunity to hack into your application. Which means the pressure is on you
to keep defense top of mind. If you don't, you'll quickly find your ASP.NET
apps vulnerable to attack.
I've compiled a list
of the eight most vital defenses, complete with the information you need to arm
yourself against them.
Tip 1—Cross-site
Scripting
Cross-site Scripting (XSS) is one of the most common attacks on Web applications today. Put simply, XSS happens when a hacker injects a script into your Web application (normally through user inputs) and your application accepts it without checking. When the data (containing the script) gets saved into your Web application, subsequent users may be affected as the script may inadvertently get loaded onto their Web browsers.
Cross-site Scripting (XSS) is one of the most common attacks on Web applications today. Put simply, XSS happens when a hacker injects a script into your Web application (normally through user inputs) and your application accepts it without checking. When the data (containing the script) gets saved into your Web application, subsequent users may be affected as the script may inadvertently get loaded onto their Web browsers.
To better understand
XSS, consider an example in which your Web application asks for a user’s name
via a text box (see Figure 1). When the
user clicks on the button, his name will be displayed in the label control.
However, instead of
entering his name, the user might enter a line of script, such as “<script>alert("Hello
there!");</script>” (see Figure 2).
Figure 2. Not a String. Without proper precautions, a user can enter a script in a text box instead of string.
In this case, when
the button is clicked, the script will be written to the Label control and the
Web browser will execute the script rather than display it (see Figure 3).
Figure 3. Not the Plan. The script from Figure 2 executes on the
client.
My example is benign,
but if a malicious script is injected and gets stored into your application, it
may then be sent to other users to cause more severe damage. For example, one
type of script could read and display the current cookie values or redirect the
user to another Web site.
Fortunately, ASP.NET
1.1 ships with built-in request protection to detect inputs that contain
scripts. Figure 4 shows what
would happen in ASP.NET 1.1 if a user tried to enter scripting code in an input
control.
Figure 4. Throwing an Error. ASP.NET's built-in request
validation kicks into action and warns you that a script has tried to execute.
Despite the built-in
defense by ASP.NET, there were reports
of loopholes. By inserting a null character () into the <Script> element, it will
bypass detection:
<SCRIPT>alert("Hello
there!");</SCRIPT>
While
this vulnerability has been hot-fixed, it is nevertheless important that you
employ added precaution. One good method is to use the Server.HtmlEncode() method to encode all
user inputs into HTML-encode strings:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
lblMessage.Text = Server.HtmlEncode(txtName.Text)
End
Sub
Figure 5. Workaround. You can manually turn off page validation
if built-in script protection is disabled.
If
the user injects a script, the HTML-encoded input will then look like this:
<script>alert("Hello
there!");</SCRIPT>
When
this HTML-encoded string is displayed in a browser, it will be displayed as a
string, and not executed as a client-side script.
If you need to turn
off the built-in script protection in ASP.NET 1.1 for some reason, you can set
the validateRequest attribute in your page to false (see Figure 5)
Tip
2 —SQL Injection
SQL Injection is another well-known exploit that hackers love. But surprisingly there are still a lot of people who don't seem to care about this problem. An example will help illustrate its importance: Suppose I have a simple login form with fields for user name and password.
SQL Injection is another well-known exploit that hackers love. But surprisingly there are still a lot of people who don't seem to care about this problem. An example will help illustrate its importance: Suppose I have a simple login form with fields for user name and password.
This is a very common
function in Web applications. Most people (especially beginning database
programmers) will write the functionality as shown:
Private Sub btnLogin_Click(ByVal sender As
System.Object, _
ByVal e As System.EventArgs) _
Handles btnLogin.Click
SqlConnection1.Open()
Dim
str As String = "SELECT * FROM Users WHERE UserID='" _
& txtName.Text & "' AND Password='" & _
txtPassword.Text & "'"
Dim
comm As New SqlCommand(str, SqlConnection1)
Dim
reader As SqlDataReader = comm.ExecuteReader()
If
Not reader.HasRows Then _
Response.Write("Login failed. Please try again")
While reader.Read()
Response.Write("Hello " & reader("UserName"))
End
While
End Sub
Figure 6. Not a Password. This form can allow manipulation your
database using SQL keywords.
In
this code, the developer simply gets whatever data the user has entered and
uses them to formulate the SQL string. At a minimum the developer should also
do an input validation; a good choice is to check the length of user-entered
data to be sure it is not overly long.
But there's a far
worse danger with such sloppy code. There are certain things that hackers can
enter in the password field to query your database. For example, the 'password'
shown in Figure 6 will display
all user names in the database. This is definitely not something you want to
support.
Another exploit the
hacker can try is known as the SQL Union attack. The following text,
entered as a string in either the user name or password text box will execute
as shown in Figure 7, giving the
hacker plenty of information about your server.
xyz' union select @@servername,
@@servicename, @@version --
Figure 7. Secrets Unearthed. The user has used the log in form
to successfully uncover information about SQL Server.
A
much safer way to formulate your SQL string is to use the Parameters object in
the SqlCommand object. The advantage to using this approach is that ADO.NET
doesn't do the substitution; it passes the parameters to SQL Server where the
substitution and validation occurs.
The following shows
the updated login method:
Private Sub btnLogin_Click(ByVal sender As
System.Object, _
ByVal e As System.EventArgs)
_
Handles btnsecureLogin.Click
SqlConnection1.Open()
Dim
str As String = "SELECT * FROM Users WHERE " & _
"UserID=@userID AND Password=@password"
Dim
comm As New SqlCommand(str, SqlConnection1)
comm.Parameters.Add("@userID", txtName.Text)
comm.Parameters.Add("@password", txtPassword.Text)
Dim
reader As SqlDataReader = comm.ExecuteReader()
If
Not reader.HasRows Then _
Response.Write("Login failed. Please try again")
While reader.Read()
Response.Write("Hello " & reader("UserName"))
End
While
End Sub
Tip 3—Validate your
User Inputs
Validate your user inputs religiously. The rule of thumb here is to assume the worst about your end users. They are bound to enter inputs that are totally unexpected. Be sure to check for illegal characters and limit the amount of data they can enter. ASP.NET ships with a couple of validation controls: make full use of them, both at the client side and server side.
Validate your user inputs religiously. The rule of thumb here is to assume the worst about your end users. They are bound to enter inputs that are totally unexpected. Be sure to check for illegal characters and limit the amount of data they can enter. ASP.NET ships with a couple of validation controls: make full use of them, both at the client side and server side.
Tip
4—Use Hashing to Store your Passwords
I have seen a number of cases where developers simply store users' passwords in plain text. This is a dangerous thing to do; if your SQL Server is compromised, you run the risk of exposing all the passwords. (There are those who argue that if your database server is compromised, it doesn’t matter how you save your passwords—they are no longer secure).
I have seen a number of cases where developers simply store users' passwords in plain text. This is a dangerous thing to do; if your SQL Server is compromised, you run the risk of exposing all the passwords. (There are those who argue that if your database server is compromised, it doesn’t matter how you save your passwords—they are no longer secure).
A much better way to
store passwords in your database is to use hashing. Hashing is a one-way
process of mapping data (plain text) of any length to a unique fixed-length
byte sequence. This fixed-length byte sequence is called a hash.
Statistically, two different pieces of data would not generate the same hash.
And a hash cannot be used to reverse-generate the plain text. In the case of
saving passwords in the database, saving the hash value of each password is
preferred over the saving the plain password. When a user logs in, the hash
value of the password is computed and then compared to the hash value stored in
the database. In this case, even if the database server is compromised, the
hackers have no way of knowing the users’ real passwords (though he could still
alter the hash value of a user’s password to one he generated himself and gain
illegal access).
The following
function shows how to use the SHA1 hash algorithm implementation in the .NET
Framework:
Public Function ComputeHashValue(ByVal data() As Byte) As Byte()
Dim hashAlg As SHA1 = SHA1.Create
Dim hashvalue() As Byte = hashAlg.ComputeHash(data)
Return hashvalue
End
Function
You
could derive the hash value of a password like this:
Dim
hashValue() As Byte
hashValue =
ComputeHashValue(Encoding.ASCII.GetBytes(txtPassword.Text))
The
hash value could then be stored in place of the user’s password.
Tip 5—Encrypt
Sensitive Data
ASP.NET Web developers know that it is sometimes useful to store information such as database connection strings in the Web.config file rather than hardcode them in the application. Doing so allows the database server to be changed without modifying and recompiling the application. However, storing sensitive information such as the connection string (which may contain user information and password) in plain text format in Web.config file is not a very good idea, as Web.config is an XML document stored as a text file and thus easily accessed.
ASP.NET Web developers know that it is sometimes useful to store information such as database connection strings in the Web.config file rather than hardcode them in the application. Doing so allows the database server to be changed without modifying and recompiling the application. However, storing sensitive information such as the connection string (which may contain user information and password) in plain text format in Web.config file is not a very good idea, as Web.config is an XML document stored as a text file and thus easily accessed.
So, a safer way would
be to encrypt the sensitive information and store the ciphertext into the Web.config file. There are two
types of encryption algorithms that you can use:
- Symmetric
- Asymmetric
Symmetric
algorithms encrypt and decrypt information using a common key. It is a simple
and efficient way of encrypting/decrypting information. However the use of a
common key makes it less secure if more than one party needs to know the key.
Asymmetric algorithms
encrypt and decrypt information using a pair of keys. This pair of keys is made
up of a private and a public key. Data encrypted using the public key can only
be decrypted using the private key and vice versa. Asymmetric algorithms are
much more complex and are computationally expensive. However, it is also much
more secure than symmetric algorithms.
Listing 1 shows the use of the Rijndael
symmetric encryption algorithm implementation in .NET. Listing 2 shows the RSA asymmetric encryption
algorithm implementation in .NET.
The functions shown
in Listings 1 and 2 will allow you encrypt the sensitive data in
your Web application, especially configuration and XML files. Listing 3 shows the supporting function used by
the functions in Listings 1 and 2. The supporting function converts a string to
a byte array. For example, it converts a string "1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16" to a byte array of
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}.
For asymmetric
encryption, you need to first create the pair of private and public keys:
'===========For Asymmetric use=============
Dim publicKey, privateKey As String
Dim RSA As New RSACryptoServiceProvider()
publicKey = RSA.ToXmlString(False)
' get public key
privateKey = RSA.ToXmlString(True)
' get private key
'===========Asymmetric Encryption=============
Dim cipherText as String = AsymmetricEncryption _
(txtAsyPlainText.Text,
publicKey)
'===========Asymmetric Decryption=============
Dim plainText as String = AsymmetricDecryption _
(txtAsyCipherText.Text,
privateKey)
For
symmetric encryption, you need a 16-byte key and Initialization Vector (IV):
'===========Symmetric=============
Dim Key, IV as String
Key="1234567890123456"
IV ="1234567890123456"
Dim cipherText As String = SymmetricEncryption _
(txtSyPlainText.Text, Key,
IV)
'===========Symmetric=============
Dim plainText as String = SymmetricDecryption _
(txtSyCipherText.Text, Key,
IV)
Because
SOAP messages are sent in plain text, Web services could also benefit from
encryption. Instead of using SSL to protect the entire communication path
(which is overkill), you could use encryption to protect sensitive information
such as credit card numbers from prying eyes.
Tip
6—Store Secure Information in the Registry
Besides encrypting data manually, you might also want to use the registry to store sensitive information. For example, you might configure your Web server to log in to a remote database server using Windows authentication. And so you might configure your Web application to use impersonation, specifying the username and password:
Besides encrypting data manually, you might also want to use the registry to store sensitive information. For example, you might configure your Web server to log in to a remote database server using Windows authentication. And so you might configure your Web application to use impersonation, specifying the username and password:
<identity impersonate="true"
userName="someuser"
password="tosecret"
/>
Figure 8. Using the Registry Editor. Navigate to the registry
directory shown to make your changes.
However, storing the
username and password in Web.config in plain text is not
a good idea. A better idea is to use the registry to store the username and
password.
In the following
series of steps, I will show you how to store your database connection string
in Web.config and then use the ASPNET_SETREG.exe utility provided by
Microsoft to store the username and password in the registry.
1. Download the
aspnet_setreg.exe utility from http://support.microsoft.com/default.aspx?scid=kb;en-us;Q329290.
2. Create a new user
account in your Windows machine. I have called it ASPNETUSER with a password of
“secret.”
3. Add the
<appSettings> element into your Web.config file. This setting
saves the database connection string into Web.config:
<configuration>
<appSettings>
<add key="Distributor" value="workstation
id=F16;packet size=4096;integrated security=SSPI;data
source=F16;persist security info=True;initial
catalog=Distributor" />
</appSettings>
4.
In your code, you can retrieve the connection string defined in your Web.config file using:
Dim
connStr As String = _
ConfigurationSettings.AppSettings("Distributor")
Dim
Conn As New SqlConnection(connStr)
5.
Next, use the aspnet_setreg.exe utility to add the
username and password of the user account that your ASP.NET application will
impersonate, into the registry:
C:\>aspnet_setreg
-k:Software\ASPNetApp\Identity -u:ASPNETUSER -p:secret
6.
When you do that, Windows will print a long message to the screen. The text of
the message is shown below. In particular, look for two lines denoted in bold.
You will need to save the two lines in a text file.
Please edit your configuration to contain the
following:
userName="registry:HKLM\Software\ASPNetApp\Identity\ASPNET_SETREG,userName"
password="registry:HKLM\Software\ASPNetApp\Identity\ASPNET_SETREG,password"
The DACL on the registry key grants Full
Control to System, Administrators, and Creator Owner.
If you have encrypted credentials for the
<identity/> configuration section, or a connection string for the
<sessionState/> configuration section,
ensure that the process identity has Read access to the
registry key. Furthermore, if you have
configured IIS to access content on a UNC share, the account used to
access the share will need Read access to the
registry key.
Regedt32.exe may be used to view/modify
registry key permissions.
You may rename the registry subkey and
registry value in order to prevent discovery.
7.
Locate the Machine.config file at: C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\CONFIG and modify the
<identity> element in Machine.config file to:
<identity impersonate="true"
userName="registry:HKLM\Software\ASPNetApp\Identity\ASPNET_SETREG,userName"
password="registry:HKLM\Software\ASPNetApp\Identity\ASPNET_SETREG,password"
/>
Figure 9. Read Permission. Change the settings to give ASPNET
the permission to read the key.
You
can override the settings in Machine.config by modifying the Web.config file in your
application and specifying another user identity to impersonate.
8. Launch the
registry editor and navigate to My
Computer/HKEY_LOCAL_MACHINE/SOFTWARE/ASPNetApp/Identity/ASPNET_SETREG (use regedt32), as
shown in Figure 8.
9. Right-click on the
ASPNET_SETREG registry key and select Permissions. Add the
user account ASPNET and set it to Read permission (see Figure 9).
10. Give the user
account ASPNETUSER FULL CONTROL access rights to the
“Temporary ASP.NET Files” folder located in C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322.
11. That’s it! Your
application will now run under the impersonation of ASPNETUSER. And the credentials
of the user can be securely retrieved from the registry.
Tip
7—Do Some Housekeeping before You Deploy Your Web Application
Tracing ASP.NET Web applications is made easy by using the <trace> element in the Web.config file as well as the page directive. However, when you are ready to deploy the Web application, be sure to disable tracing at both the page level as well as the application level, which you can do with this code:
Tracing ASP.NET Web applications is made easy by using the <trace> element in the Web.config file as well as the page directive. However, when you are ready to deploy the Web application, be sure to disable tracing at both the page level as well as the application level, which you can do with this code:
<trace enabled="false"
requestLimit="10" pageOutput="false"
traceMode="SortByTime" localOnly="true" />
Also,
turn off debug mode in Web.config file:
<compilation
defaultLanguage="vb" debug="false" />
In
the <customErrors> element in Web.config, remember to set the
mode attribute to RemoteOnly:
<customErrors mode="RemoteOnly"
/>
The
mode attribute has three possible values:
- "On" always display custom
(friendly) messages
- "Off" always display detailed
ASP.NET error information.
- "RemoteOnly" displays custom
(friendly) messages only to users not running on the local Web server.
This setting is recommended for security purposes, so that you do not
display application detail information to remote clients.
Last,
but not least, remove the Solution and Project files from your deployment
server; they are not mapped to the ISAPI filter and hence it is very easy for
hackers to guess their name and access their content directly from the Web
browser.
Tip 8—Use Sessions,
but Not Cookie-less Sessions
If there is a need to persist sensitive information about a user, use Session objects. Session objects in ASP.NET use cookies to store the Session ID on the cookie, which gets passed to-and-fro between the client and the server. The Session objects containing sensitive information are stored on the server side. Hence, the only information exposed is the Session ID, and not the sensitive information.
If there is a need to persist sensitive information about a user, use Session objects. Session objects in ASP.NET use cookies to store the Session ID on the cookie, which gets passed to-and-fro between the client and the server. The Session objects containing sensitive information are stored on the server side. Hence, the only information exposed is the Session ID, and not the sensitive information.
ASP.NET supports
cookie-less sessions, which might seem tempting since many users turn cookies
off in their browsers. But don't go down that road: Using cookie-less sessions
subjects you to session hijacking, where a hacker can simply use the URL that
you are accessing and assume the browsing. The bottom line is, always avoid
cookie-less sessions.
0 comments:
Post a Comment