Chapter 1. Writing Secure Code

In This Chapter

  • Designing for security

  • Building secure Windows and Web applications

  • Digging into System.Security

Security is a big topic. Ignoring for a moment all the buzzwords surrounding security, I'm sure you realize that you need to protect your application from being used by people who shouldn't use it. You also need to prevent your application from being used for things it shouldn't be used for.

At the beginning of the electronic age, security was usually performed by obfuscation. If you had an application that you didn't want people peeking at, you just hid it, and no one would know where to find it. Thus, it would be secure. (Remember War Games, the movie in which the military assumed that no one would find the phone number needed to connect to its mainframes — but Matthew Broderick's character did?)

That obviously doesn't cut it anymore; now you need to consider security as an integral requirement of every system that you write. Your application might not contain sensitive data, but can it be used to get to other information on the machine? Can it be used to gain access to a network that it shouldn't? The answers to these questions matter.

The two main parts to security are authentication and authorization. Authentication is the process of making sure a user is authentic — that the user is who he claims to be. The most common method of authentication is to require the use of a username and password, though other ways exist, such as thumbprint scans. Authorization is the act of ensuring that a user has the authority to do what he asks to do. File permissions are a good example of this — users can't delete system-only files, for instance.

The silent partner of security makes sure that your system can't be fooled into believing a user is authentic or authorized. Because of this requirement, there is more to security than inserting username and password text boxes in your program. In this chapter, I tell you what tools are available in the .NET Framework to help you make sure that your applications are secure.

Designing Secure Software

Security takes a fair amount of work to accurately design. If you break the process into pieces, you find that it's a lot easier to accomplish. The Patterns and Practices team (a group of software architects at Microsoft who devise programming best practices) have created a systematic approach to designing secure programs that I think you will find straightforward, so I describe it in the following sections.

Determining what to protect

Different applications have different artifacts that need protection, but all applications share something that needs protection. If you have a database in your application, that is the most important item to protect. If your application is a server-based application, the server should rate fairly high when you're determining what to protect.

Even if your program is just a little single-user application, the software should do no wrong — an outsider shouldn't be able to use the application to break into the user's computer.

Documenting the components of the program

If you think this section's title sounds similar to part of the design process, you're right. A lot of threat modeling is just understanding how the application works and describing it well.

First, describe what the application does. This description becomes a functional overview. If you follow the commonly accepted Software Development Life Cycle (SDLC), and then the use cases, requirements, or user stories document (depending on your personal methodology) should give you a good starting point.

Next, describe how the application accomplishes all those tasks at the highest level. A Software Architecture Overview (SAO) diagram is a useful way to do it. This diagram shows which machines and services do what in your software.

Tip

If you happen to be using Visual Studio Team System, building a diagram in the Enterprise Architect version is the ultimate SAO diagram and is a good model.

Sometimes the SAO is a simple diagram — if you have a standalone Windows Forms program like a game, that's all there is! A standalone program has no network connection, and no communication between software parts. Therefore, the software architecture diagram contains only one instance.

Decomposing components into functions

After you create a document that describes what the software is doing and how, you need to break out the individual functional pieces of the software. If you have set up your software in a component fashion, the classes and methods show the functional decomposition. It's simpler than it sounds.

The end result of breaking the software into individual pieces is having a decent matrix of which components need to be protected, which parts of the software interact with each component, which parts of the network and hardware system interact with each component, and which functions of the software do what with each component.

Identifying potential threats in functions

After you create the list of components that you need to protect, you tackle the tough part: Put two and two together. Identifying threats is the process that gets the security consultants the big bucks, and it's almost entirely a factor of experience.

For instance, if your application connects to a database, you have to imagine that the connection could be intercepted by a third party. If you use a file to store sensitive information, the file could, theoretically, be compromised.

To create a threat model, you need to categorize the potential threats to your software. An easy way to remember the different categories of threats is as the acronym STRIDE:

  • Spoofing identity: Users pretend that they are someone who they are not.

  • Tampering with data or files: Users edit something that shouldn't be edited.

  • Repudiation of action: Users have the opportunity to say they didn't do something that they actually did do.

  • Information disclosure: Users see something that shouldn't be seen.

  • Denial of service: Users prevent legitimate users from accessing the system.

  • Elevation of privilege: Users get access to something that they shouldn't have access to.

All these threats must be documented in an outline under the functions that expose the threat. This strategy not only gives you a good, discrete list of threats but also focuses your security hardening on those parts of the application that pose the greatest security risk.

Rating the risk

The final step in the process is to rate the risks. Microsoft uses the DREAD model to assess risk to its applications. The acronym DREAD represents five key attributes used to measure each vulnerability:

  • Damage potential: The dollar cost to the company for a breach

  • Reproducibility: Special conditions to the breach that might make it harder or easier to find

  • Exploitability: A measure of how far into a corporate system a hacker can get

  • Affected users: The number of users who are affected and who they are

  • Discoverability: The ease with which you can find the potential breach

You can research the DREAD model at msdn.microsoft.com/security, or position your threat model to consider those attributes. The key is to determine which threats are most likely to cause problems and then mitigate them.

Building Secure Windows Applications

The framework lives in a tightly controlled sandbox when running on a client computer. Because of the realities of this sandbox, the configuration of security policy for your application becomes important.

The first place you need to look for security in writing Windows applications is in the world of authentication and authorization. Authentication confirms the identity of a user, and authorization determines what she can and can't do within an application.

When you are threat modeling, you can easily consider all of the possible authentication and authorization threats using the STRIDE acronym. (See the earlier section "Identifying potential threats in functions," for more about STRIDE.)

Authentication using Windows login

To be straightforward, the best way for an application to authorize a user is to make use of the Windows login. A host of arguments take place about this strategy and others, but the key is simplicity: Simple things are more secure.

For much of the software developed with Visual Studio, the application will be used in an office by users who have different roles in the company; for example, some users might be in the Sales or Accounting department. In many environments, the most privileged users are managers or administrators — yet another set of roles. In most offices, each employee has her own user account, and each user is assigned to the Windows groups that are appropriate for the roles she plays in the company.

Note

Using Windows security works only if the Windows environment is set up correctly. You can't effectively build a secure application in a workspace with a bunch of Windows XP machines on which everyone logs on as the administrator, because you can't tell who is in what role.

Building a Windows application to take advantage of Windows security is straightforward. The goal is to check to see who is logged on (authentication) and then check that user's role (authorization).

The following steps show you how to create an application that protects the menu system for each user by showing and hiding buttons:

  1. Start a new Windows Forms Application project by choosing File

    Authentication using Windows login

    This would work for Windows Presentation Foundation (WPF) too, but it's easier to show in Windows Forms. (Book V is all about WPF.) I named my project Windows Security.

  2. Add three buttons to your form — one for Sales Menu, one for Accounting Menu, and one for Manager's Menu.

    My example is shown in Figure 1-1.

    The Windows Security application sample.

    Figure 1-1. The Windows Security application sample.

  3. Set every button's visible properties to False so that they aren't shown on the form by default.

  4. Double-click the form to reach the Form1_Load event handler.

  5. Above the Namespace statement, import the System.Security.Principal namespace this way:

    using System.Security.Principal;
  6. In the Form1_Load event handler, instantiate a new Identity object that represents the current user with the GetCurrent method of the WindowsIdentity object by adding this bit of code:

    WindowsIdentity myIdentity = WindowsIdentity.
       GetCurrent();
  7. Get a reference to this identity with the WindowsPrincipal class:

    WindowsPrincipal myPrincipal = new
       WindowsPrincipal(myIdentity);
  8. Finally, also in the Form1_Load subroutine, code a little If/Then statement to determine which button to show. The code is shown in Listing 1-1.

Example 1-1. The Windows Security Application Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Principal;

namespace WindowsSecurity
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            WindowsIdentity myIdentity = WindowsIdentity.GetCurrent();
            WindowsPrincipal myPrincipal = new WindowsPrincipal(myIdentity);
            if(myPrincipal.IsInRole("Accounting"))
            {
                AccountingButton.Visible=true;
            }
            else if (myPrincipal.IsInRole("Sales"))
            {
SalesButton.Visible = true;
            }
            else if (myPrincipal.IsInRole("Management"))
            {
                ManagerButton.Visible = true;
            }
        }
    }
}

To successfully run this code, you must have an environment that has Accounting, Sales, and Management NT user groups.

In some cases, you don't need this kind of role diversification. Sometimes you just need to know whether the user is in a standard role, which System.Security provides for. Using the WindowsBuiltInRole enumerator, you describe actions that should take place when, for example, the administrator is logged on:

if (myPrincipal.IsInRole(WindowsBuiltInRole.Administrator))
      {
          //Do something
      }

Encrypting information

Encryption is, at its core, an insanely sophisticated process. Five namespaces are devoted to different algorithms. Because encryption is so complex, I don't get into the details in this book.

Nonetheless, it is important that you understand one cryptographic element for a key element of security — encrypting files. When you work with a file in a Windows Forms application, you risk someone loading it up in a text editor and looking at it, unless you have encrypted the program.

The common encryption scheme Data Encryption Standard (DES) is implemented in .NET. It isn't the strongest encryption in these days of 64-bit desktop machines, but it's strong enough to encrypt the data files for a Windows application. You can find the methods to encrypt for DES in the DESCryptoServiceProvider in the System.Security.Cryptography namespace.

Deployment security

If you deploy your application using ClickOnce, you need to define the access to the PC that the application will request. ClickOnce is a Web server–based deployment strategy that allows users to run Windows Forms applications from a Web browser by using the WindowsSecurity tab in the My Project configuration file, shown in Figure 1-2.

The WindowsSecurity tab of the My Project configuration file.

Figure 1-2. The WindowsSecurity tab of the My Project configuration file.

Getting to the My Project configuration file is straightforward:

  1. From an open project, go the Solution Explorer by pressing Ctrl+Alt+L.

  2. Double-click the My Project file.

  3. Click the WindowsSecurity tab.

Here, you can define the features that your application uses, so that the user installing it receives a warning at installation rather than a security error when running the application.

Building Secure Web Forms Applications

Web Forms applications are disconnected, loosely coupled programs that expose a server to potential attacks through the exposed ports used by the applications. By loosely coupled, I mean they have a transact-and-wait relationship with the server.

Because of this coupling, building for security becomes more important than ever with a Web Forms application. A side effect is that your application can become less functional.

When building Web-based applications, you spend less time worrying about authentication (especially if your application is made publicly available) and more time worrying about crackers. Because you are making a server — usually something you keep private — available to the public, your programs are subject to a whole new set of security rules.

The key to protecting a public server is honesty. You have to be honest with yourself about the weaknesses of the system. Don't think, "Well, a cracker could figure out the password by doing XYZ, but no one would ever do that." Trust me, someone will figure it out.

The two main types of attacks to be concerned about for a Web Forms application are SQL Injection attacks and script exploits.

SQL Injection attacks

A SQL Injection attack happens when a hacker enters a line of SQL code into an input field used to query a database in a form on a Web page (such as the username and password text boxes in a login form). Malicious SQL code causes the database to act in an unexpected way or to allow the hacker to gain access to, alter, or damage the database.

Understanding SQL Injection

The best way to understand how a hacker uses a SQL Injection is to see an example. For instance, a Web page has code that accepts a Product ID from the user in a text box and returns product details based on the Product ID the user entered. The code on the server might look like this:

//Get productId from user
string ProductId = TextBox1.Text;
//Get information from the database
string SelectString = "SELECT * FROM Items WHERE ProductId =
   '" + ProductId + "';";
SqlCommand cmd = new SqlCommand(SelectString, conn);
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
//Process results
myReader.Close();
conn.Close();

Normally, a user enters the appropriate information into the text box. But a cracker attempting a SQL Injection attack would enter the following string into textBox1:

"FOOBAR';DELETE FROM Items;--"

The SQL code that would be run by your code would look like this:

SELECT * FROM Items WHERE ProductID = 'FOOBAR';DELETE FROM
   Items;--'

The SQL Server executes some code you didn't expect; in this case, the code deleted everything in the Items table.

Preventing SQL Injection

Note

The easiest way to prevent SQL Injection is to never use string concatenation to generate SQL. Use a stored procedure and SQL parameters. You can read more about that in Chapter 2 of this minibook.

Script exploits

A script exploit is a security flaw that takes advantage of the JavaScript engine in a user's Web browser. Script exploits take advantage of one of the more common features of public Web Forms applications — enabling interaction among users. For instance, a Web Forms application may enable a user to post a comment that other users of the site can view, or it may allow a user to fill out an online profile.

Understanding script exploits

If a malicious user were to put some script code in his or her profile or comment, that hacker could take over the browser of the next user who comes to the site. Several outcomes are possible, and none of them are good.

For instance, the cookies collection is available to JavaScript when a user comes to your site. A malicious user would put some script code in his or her profile that could copy the cookie for your site to a remote server. This could give the malicious user access to the current user's session because the session identifier is stored as a cookie. The malicious user would then be able to spoof the current user's identity.

Preventing script exploits

Fortunately, ASP.NET prevents users from typing most script code into a form field and posting it to the server. Try it with a basic Web Forms project by following these steps (you see the error shown in Figure 1-3):

  1. Create a new Web Forms project.

  2. Add a text box and a button to the default page.

  3. Run the project.

  4. Type <script>msgbox()</script> into the text box.

  5. Click the button.

Tip

Additionally, you can use the Server.HTMLEncode method to encode anything that the Web Forms application sends to the screen — this will make script code appear in real text rather than in HTML.

Script exploits are blocked by default.

Figure 1-3. Script exploits are blocked by default.

Best practices for securing Web Forms applications

Aside from make sure that your Web Forms application will prevent SQL Injection attacks and script exploits, you should keep in mind some good practices for securing your Web applications.

The following list runs down some of the most important practices for securing your Web applications:

  • Keep your IIS box up to date.

  • Back up everything.

  • Avoid using a Querystring variable.

  • Don't leave HTML comments in place. Any user can view the HTML code and see your comments by choosing View

    Best practices for securing Web Forms applications
  • Don't depend on client-side validation for security — it can be faked.

  • Use strong passwords.

  • Don't assume what the user sent you came from your form and is safe. It is easy to fake a form post.

  • Make sure that error messages don't give the user any information about your application. E-mail yourself the error messages instead of displaying them to the user.

  • Use Secure Sockets Layer.

  • Don't store anything useful in a cookie.

  • Close all unused ports on your Web server.

  • Turn off SMTP on IIS unless you need it.

  • Run a virus checker if you allow uploads.

  • Don't run your application as Administrator.

  • Use temporary cookies, if possible, by setting the expiration date to a past date. The cookie will stay alive only for the length of the session.

  • Put a size limit on file uploads. You can do it in the Web.Config file:

    <configuration>
       <system.web>
            <httpRuntime maxRequestLength="4096" />
       </system.web>
    </configuration>
  • Remember that the ViewState of Web Forms is easily viewable.

Using System.Security

Although many of the security tools are built into the classes that use them, some classes defy description or classification. For that reason, System.Security is the holding pot for stuff that doesn't fit anywhere else.

The more common namespaces for System.Security are described in Table 1-1. I show how to use the Security.Principal namespace in the earlier section "Authentication using Windows login."

Table 1-1. Common Namespaces in System.Security

Namespace

Description

Common Classes

Security

Base classes for security

CodeAccessPermission, SecureString

AccessControl

Sophisticated control for authorization

AccessRule, AuditRule

Authorization

Enumerations that describe the security of an application

CipherAlgorithmType

Cryptography

Contains several namespaces that help with encryption

CryptoConfig, DESCryptoServiceProvider

Permissions

Controls access to resources

PrincipalPermission, SecurityPermission

Policy

Defends repudiation with classes for evidence

Evidence, Site, Url

Principal

Defines the object that represents the current user context

WindowsIdentity, WindowsPrincipal

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.12.192