Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional ASP.NET Security - Jeff Ferguson

.pdf
Скачиваний:
28
Добавлен:
24.05.2014
Размер:
13.26 Mб
Скачать

Storing Secrets

The moment that secrets - such as a database connection string, username, or password - are stored somewhere on our system, they are vulnerable to attack. Storing secrets is a very hard thing to do, since there is no such thing as bullet-proof security. For every step we take to secure a secret, there will be some way to hack into it, although, of course, some methods are far more secure than others. Maintaining secrets can be a nightmare, so how could we store this information? Well, we have to make compromises.

One way in which we could store information safely, either within a file, or within the Registry, would be to use encryption. However, there are some difficulties associated with this. Firstly, storing any kind of sensitive information within the Registry does not fit well with ASP.NET, since ASP.NET works on the XCOPY deployment model. Also, another difficulty with using encryption is that the have to store the encryption key somewhere, and finding the appropriate place - which must be secure yet accessible - can be difficult. Storing encrypted data and the key in the same place, such as the Registry, or within a file, would be a little unwise. This would be like leaving the key on your door, so that anyone can open the door and get in.

In this brief chapter, we'll look at a few important points about storing sensitive data, and at the types of threats that we must anticipate. We'll look in turn at storing secrets in the following ways:

Q In ASP.NET Pages

Q In Code-Behind Files a In a .configFile

Q In a Protected .configFile

Q In Memory (in application-level variables)

Q Using Hashing Techniques Q

Using the Data Protection API

Initially, then, we'll look at an inappropriate way of storing secrets: in an ASP.NET page.

Storing Secrets in Pages

Many developers try to store secret information in ASP.NET pages. This information might include usernames, passwords, crypto public keys, crypto private keys, database connection string information, and so on. For example:

void Page_Load( Object Src,

EventArgs

E) {

 

SqlConnection dbConn = new

SqlConnection( " server=Sruthi ;uid=SecureAppUser;pwd= !23Chille32~@@@ ! ; database=Northwind; " ) ;

This is a very bad idea, since if someone is able to view the source of an ASP.NET page, they can read the secret information. If you can avoid the human readability factor from the security threat then you can store the info in the ASP.NET page.

Quite frequently, some developers will use two database connections in the code, one which will point to the development server, and the other to the production server.

void Page_Load( Object Src,

EventArgs E)

SqlConnection dbConn = new

//Dev Server

SqlConnection ( "server=SruthiDev;uid=SecureAppUser,-pwd= !23Chille32~ database=Northwind; " ) ;

//Prod Server

//SqlConnection( " server =Sruthi;uid=SecureAppUser ;pwd=!23Chille32~ database=Northwind; " ) ;

When the application goes to production, the development server name will be commented out, and the production server name un-commented. This is bad practice, since anyone who has access to the source of the application can know about the database connection information.

72

Storing Secrets

Storing Secrets in Code-Behind Files

Separating out your code into a code-behind class can mean that you can place information like connection strings in this code-behind class. If you are using Visual Studio .NET to build ASP.NET applications then this method of coding is the standard way of writing ASP.NET pages. Code-behind can be used as an uncompiled file, a technique that has the same effect as storing the information in the ASP.NET page, since both are human readable, and are thus vulnerable. Visual Studio .NET, however, compiles all code-behind into an assembly, making it harder to read the sensitive information, and you can also do this from the command-line environment. We'll look at the pros and cons of compiling secrets into an assembly in the next chapter.

Storing Secrets in a .config File

Another place where we could store sensitive information is in a web. config file. Many developers leave the database connection information in web.config file's appSettings section. The appSettings section in the web. config file is very flexible, and can be extended to store application-specific information.

<configuration>

<appSettings>

odd key="DBConnStr"

value="server=Sruthi;uid=SecureWebUser;pwd=!23URL32 ~@@@!;database=Northwind;" />

</appSettings>

</configuration>

The web. config file is a text file, and is kept in the shared web hosting environment, so this could cause some security risks. If someone has access to the physical files, they can access the database server name, database name, username, password, the method to connect to the database TCP/IP, and the port (usually 1433 for SQL Server).

Storing Secrets in a Protected .config File

ASP.NET applications depend on machine. config and web. config files. The machine . config file affects all the applications running on the particular version of the ASP.NET, meaning that each version of ASP.NET will have its own version of a machine. config file, and the changes made in that particular version will only affect the ASP.NET applications using that version of the framework.

Moreover, ASP.NET follows the hierarchical configuration model. For example, if you have folders beneath the main application folder, all the configuration information will be inherited from the parent folder and can be overridden by configuration information in sub-folders.

This option provides a way to protect the sub-folder's configuration file. For example, we could store our secret information in the web. config file stored in a sub-folder, and restrict the permissions to a few people that we trust. For example, suppose you have folders called Admin and Sales beneath your main application folder, and you want to restrict the access to the Admin folder to very few people: those included in the Windows OS groups GrpAdmins and GrpSales, for instance, we could restrict access, giving access only to members of these groups, in the following way:

<configuration>

 

 

<system.web>

 

 

</system.web>

 

 

<!— Configuration for the

"Admin"

subdirectory. -->

<location path="Admin"> <system.web>

 

<authorization>

 

 

<allow roles="GrpAdmins"

/> <deny

 

users="?" /> </authorization

 

</system.web> </location>

 

 

< ! — Configuration for the

"Sales"

subdirectory.

--> <location path="Sales"> <system.web>

<authorization>

 

<allow roles="GrpSales"

/> <deny users="?"

/> </authorization> </system.web> </location> </configuration>

We've configured permissions for the sub-folders using the <location> tag, and we've specified the sub-folder name in the path attribute. Each folder is configured to grant access to specific Windows OS groups, and deny access to anonymous users. We can then define the sub-folder-level secret information. For example, the following web. conf ig file stored in the Admin folder shows how the userlD, domain name, and password may be stored:

<configuration>

<!- Configuration information for the folder "Admin". --> <location path="Admin"> <appSettings>

odd key="userID"

value="Sruthi"

/> odd

key="domain"

value="Sruthi"

/> odd key= "password"

value=" ! 23URL32~@@@ ! " </appSettings> </location> </configuration>

ASP.NET also allows us to lock the configuration information in the web. conf ig file. For example, suppose we do not want session state information stored in the web. conf ig file to be changeable. vVe can lock the configuration information by setting the allowOverride attribute of the <location> to false. Here is an example of this:

<configuration>

<system.web>

74

Storing Secrets

</system.web>

<!— Configuration for the "Admin" subdirectory. --> <location path="Admin" allowOverride="false">

</location>

</configuration>

As you can see, we've specified that no information in the Admin sub folder can be changed. This will mean that the web. conf ig file stored in this directory is pretty much locked now. In spite of this, the web.conf ig files are still vulnerable, as they are human-readable. As I pointed out earlier, if you can avoid the human readability problem, then you should.

Storing Secrets in Memory

We can store sensitive information in memory, however, as we will see, this is not really a sensible option. For example, we could create few application-level variables in the Application_Start method.

void Application_Start(Object

sender, EventArgs E)

 

{ //Global

Variables

String

strUserlD

=

"";

String

strPassword="";

String

strConnString

=

""; }

 

 

 

When the application is starting for the first time, we can initialize the values from a secure place - maybe a remote data store - which is far more secure. The problem with this approach is that if the hacker has access the box in person, or remotely, they can attach the currently running process into a debugger, place few break points, and read the information contained in these variables. There go our secrets.

Another possibility would be that the hacker could access the PageFile. Sys file, which holds all the OS memory. Once they have the file with them, they can scan through it and find our secret information. This is a viable option if you can prevent this kind of attack. The only way to avoid this kind of attack is to secure access to your server from outside.

Storing Secrets Using Hashing Techniques

One of the ways to get around the encryption and decryption problem is to store hashed values rather than encrypted values. When we want to compare hashed strings, we can simply compare the hash, rather than the original clear text values.

Hashing (fingerprinting, or message digesting) is a cryptography algorithm that produces message digests. The hash calculation is implemented using a standard well-known formula (such as MD5 or SHS). These are difficult to reverse engineer or fake. That is, if someone finds out the hash total, it is difficult to reconstruct the message it was applied against, and it is difficult to manipulate the message in any way. You can review the Message-Digesting-5 (MD5) algorithm in Internet RFC 1321 (http://www.ietf.org/rfc). The message digests are usually 128 bits to 160 bits in length. For example, the MD5 hashing algorithm produces a 128-bit message digest, and a SHA3 (or Secure Hash Algorithm 3) algorithm produces 160-bit message digest.

The Forms Authentication module has a method called HashPasswordForStoringlnConf igFile in the FormsAuthentication class. This method can be used to generate hash values using either the MD5 or SHA3 algorithms. We could use this in the following way to utilize both the MD5 and SHA3 algorithms:

<%@ Import Namespace="System.Web.Security" %> <html>

<head>

<Title>Hashing</Title>

<script language="C#" runat=server>

void encryptStringtObject Src, EventArgs E)

{

SHA1.Text = FormsAuthentication.HashPasswordForStoringlnConfigFile(txtPassword.Text, "SHA1");

MDB.Text = FormsAuthentication.HashPasswordForStoringlnConfigFile(txtPassword.Text, "MD5"); } </script>

</head>

<body>

<form runat=server>

 

 

 

 

 

 

 

<pxb>0riginal

Clear Text

Password:

</bxbr> <asp:Textbox

 

id="txtPassword"

runat=server

 

/> <asp:Button runat="server"

text="Hash

String"

onClick="encryptString"

/></p>

 

<pxb>Hashed

 

Password

In

SHA1:

 

</b>

 

 

<asp:label

 

id="SHAl"

runat=server

/></p>

 

<pxb>Hashed

 

Password

In MD5:

</b>

 

 

<asp:label

 

id="MD5"

 

runat=server

 

 

/></p> </form> </body> </html>

We have a textbox that accepts a password, and it shows what the password will look like in the MD5 and SHA3 message digest formats. The following screen shows the message digest value for the password "! 332MyPassword#@ !". We will look at using hashing in far more detail when we look at Forms Authentication in Chapter 9.

Address ^J hKp //'loGalhovt/ch12/'hash_vb,aspx

Original Clear Text Password:

|!332MyPassword*@!

Hash String

Hashed Password InSHAI: D1626D8990C51F16574D4D08697679DCEEBE3082

Hashed Password In MD5: 5E3FBB30FBF4717F74A0037522FE5AB1

Local

'irfr

76

Storing Secrets

Alternatively, we could use the SHAlCryptoServiceProvider and MDSCryptoServiceProvider classes in the System. Security. Cryptography namespace to generate hash values.

So how do we use hashing in real-world applications? When the user signs up for membership at our site, we would store the password in a hashed format in the database. We might also store the clear text password in an isolated environment, where it is not reachable from the public network, so that we can send the password back to the user if they request it, by clicking the forgot password link, for instance (although this is rather frowned upon). When the user is trying to log in to our site, get the password from them, then hash it using the hash algorithm, and compare the hashed values.

Storing Secrets Using the Data Protection API

The Windows 2000 and XP operating systems include an added set of Crypto API's called Data Protection API's or DPAPI. The DPAPI exposes two functions called CryptProtectData and CryptUnprotectData, which will encrypt and decrypt the data respectively. DPAPI is very special when compared with other Crypto API's, as the encryption and decryption key is derived from the user's password. The other level of protection that is available from the DPAPI is that the data can only be decrypted from the same computer where it was encrypted, unless the user is configured with a roaming profile.

Unfortunately, we don't have direct access to the DPAPI from the .NET Framework. But we can build a COM wrapper for the DPAPI, and we should be able to access the DPAPI's from the .NET Framework using COM Interop.

For more information on this, read the "Storing Secrets in Windows 2000 and Windows XP " section of the book 'Writing Secure Code " (ISBN: 0-73561-588-8) by Michael Howard and David LeBlanc, from Microsoft Press.

Summary

In this chapter, we investigated some appropriate, and some inappropriate, ways of storing information. Obviously, whether or not a particular approach is suitable will depend on the sensitivity of the information. If, for instance, you wish to restrict access to some pages of a web application, but it would not really matter a great deal if someone else were to gain access to them, then you might decide that storing some passwords within a file on the web server would be appropriate, and that it mightn't be necessary to use hashing techniques or any other sophisticated measures in order to protect your restricted area. However, we would generally need to be far more careful about how to store information such as user names and passwords for our database.

We have seen that using hashing techniques can be very useful in storing secrets of various types, and have also seen that putting information in a protected . conf ig file can be a useful strategy in some circumstances.

In the next chapter, we'll investigate ways of securing database access.

77

Securing Database Access

Databases are the foundations of business systems, storing critical and sensitive information about an organization, which may be accessed, entered, or managed by a web application or an ERP application. The security of an organization's database is essential, since it can be vulnerable to attacks. Securing a database should have an even higher priority when it is exposed to public networks such as the Internet, and when it is accessed by an ASP.NET application. Database system vulnerabilities can be due to insecure password storage, un-configured or incorrectly configured databases, and unrecognized backdoors into the database. By assessing the vulnerabilities of the database, and addressing them with strict security policies, we can reduce security threats.

Database Accounts

Every database server comes with a super-user default account with or without a default password. For example:

Database Product

Account

Default Password

 

 

 

MS SQL Server

sa

(None)

Oracle

system

Manager

Oracle

sys

ChangeOnlnstall

 

 

 

These user accounts are very powerful, having privileges to perform any operation on the database. In my experience, I've seen many situations where the developer or the DBA hasn't changed the default password for the database, thus keeping the database wide open to the entire world. If the hacker knows what kind of database product you are using, the first thing they'll try is to use the default username and the password. Databases should always be protected with a very strong password, and the passwords should be changed frequently: this should be based on formal company policy.

When it comes to securing the databases, each database product has it own way of protecting itself. I'd strongly recommend that you read up on securing your chosen database server.

Another big mistake is to set up the web application to use the full privileged administrator account. Unfortunately, this is a common problem: developers either wittingly or unwittingly leaving open a serious security threat. One reason for using the administrator account is that it can make life easier for the developer, removing the need to configure and maintain permissions for the database objects. This can lead to many problems, for example, a hacker can send malicious SQL code into the server, which will be executed without any problems, since the application will be running with administrator privileges. We'll look at this problem further, later in the chapter. We can reduce the risk by following a general principle of running the application with an unprivileged user account, also known as the principle of least privilege.

The principle of least privilege account conforms to three basic rules:

Q Users should be granted the least privilege required to accomplish their tasks.

Q Applications should be granted the least privilege required to perform their functions. Q Systems should be granted the least privilege required to fulfill their role in a larger network.

Restricting Connections to the Database

The best way to protect the database is to place it in a demilitarized zone (DMZ), which ensures that the database is not accessible from the outside world. If you are establishing a TCP/IP connection with the database from the web application, make sure you only open that particular port, and that all other ports are closed. If you are running multiple instances of SQL Server on the same server, then SQL Server could be using a few more ports on the firewall. It is important to close these ports in the external firewall. The option to accept ad hoc SQL queries over IIS should also be disabled, since it poses lot of security risks.

Once the database is protected and database accounts are secured using strong password (read the section Enforcing use of Secure Passwords in the next chapter for guidelines to choose a strong password), the next step is to protect the database connection. Even though the database is secured, if you leave the database connection information in an insecure place, then all the security measures that you might have taken will have gone with the wind.

As we saw earlier, storing database connection details in either a web.config or a global .asax file leaves your connection information vulnerable. Both web.config and global. asax files are text files, and in the shared web hosting environment, this could cause some security risks. If someone has access to the physical files, they can see the database server name, database name, username, password, the method to connect to the database TCP/IP, and the port (usually 1433 for SQL Server).

80

Securing Database Access

Storing Secrets in a .NET Component

The best way to protect the database connection is to store it in a .NET component or serviced COM component. For example, the following C# component provides an encapsulated way to protect the database connection string from prying eyes.

using System; using System.Data;

using System.Data.SqlClient;

public class ProtectDbConnection

//PrivateDatabaseobjectforthisclassprivate sqlConnection sqlDBConn;

//Open the database connection public sqlConnection Connect))

sqlDBConn = new sqlConnection("server=Sruthi;uid=SecureWebUser;" + "pwd=!23URL32~@@@!;database=Northwind;");

sqlDBConn.Open(); return sqlDBConn;

//Close the database connection public void Disconnect() {

if (sqlDBConn != null) {

//If the connection is open then close it if (sqlDBConn.State == ConnectionState.Open) sqlDBConn.Close();

Even though the database connection is protected inside the .NET component, if someone has access to the component (the DLL file) then they can view the IL source using the ILDASM utility. There are even utilities available to reverse-engineer the IL code back into its source code. But this option is far better than leaving the connection string in a plain text file such as Conf ig. web or global.asax.

Once this component is compiled and deployed, we can establish a very secure connection to the database by accessing the component.

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.SqlClient" %>

<HTML>

<HEAD>

81

Соседние файлы в предмете Программирование