Lockdown Pt 1– Assessing and Securing Legacy Client-side Applications

Tim Kulp | June 29, 2011

 

Welcome to Lockdown. In this three part series, we will examine how to build security into a legacy application without rewriting the entire system. This three article series will examine the following four topics:

  1. Assessing our Application: We begin by introducing our sample project Time Logger. This PHP application will be secured using various client side and server side techniques through the series. We will also walk through the Risk Assessment process so we know what we need to secure in our application.
  2. Low Hanging Fruit: Input Validation and Data Sanitization will help you monitor the data flowing through your application. These two techniques are simple modifications to any existing application.
  3. Secure your Storage: Using client side and server side encryption techniques we will secure our sensitive data in the application. We will use our Risk Assessment to determine what needs to be encrypted and how.
  4. Stamp of Approval: Finally we will examine how to test our security controls. We will discuss how to use some tools to validate our defenses as well as how to conduct a security code review with your development peers.

Our sample application: Time Logger

The fictitious legacy application that we are going to be securing is a billable hour tracking system used by the XYZ Contracting Company. The system allows contractors working at various client sites to log in and record their billable hours. While the contractors log their hours, the accounting department can log in and see how much they should pay each contractor based on the contractor’s billable hours.

You can download the sample project from http://seccode.blogspot.com/p/projects_28.html.The project is a simple PHP web application with a MySQL database.

Our Goal

By the end of this series you will be able to implement some key practices to improve the security of any existing application.  While we will be focusing mostly on client side (i.e. JavaScript) security, the concepts translate directly to server code, desktop applications, RIA, any software. Each concept we introduce will have a supporting exercise so that you can take the idea and easily convert it to code.

Topic 1: Risk Management and Assessment

Security = Risk Management; this statement is critical in understanding how to build a secure system. Risk Management is the repeating process of identifying how threats, vulnerabilities, impact and probability of attack will be handled to minimize loss. Using Risk Management will allow your organization to make business oriented decisions about what to protect and how. The first step in building a Risk Management program is to perform a Risk Assessment. Before we touch a single line of code, we need to build our Risk Assessment using the following activities:

  • Identify threats and how likely they are to happen: Define threats and probability of attack.
  • Identify and rank your assets and operations: Define what we want to protect (be specific).
  • Estimate potential impact if the threat is realized: What will happen if the threat occurs?
  • Identify how you will address the risk: Using Acceptance, Mitigation or Transference.
  • Document your findings: Your current findings will be the starting point for future assessments.

Why do we need to do this?

As developers we want to fix, build and be problem solvers. Risk Assessments tell us what problems we need to fix and what priority they need to be fixed in. A threat with a high probability and high impact will need to be addressed as soon as possible while a high probability and low impact threat might fall towards the end of the to-do list. Address all the security issues that you find but make sure the high impact, high probability ones are at the front of your work list.

Risk Assessment is complete, now what?

Risk Management takes the output of Risk Assessments and examines:

  • How has the risk been addressed?
  • What is the importance of this risk?

We repeat this process at frequent intervals to ensure that all risks are addressed throughout the Systems Development Lifecycle (SDLC). At the start of each Risk Management cycle you should run through a Risk Assessment starting from where you left off (remember the Document your Findings step). Examine any new risk that has arisen and track the risks that you knew about to see if you have indeed implemented controls to address the risk.

Many organizations use formalized Risk Management Frameworks (RMF). While these are excellent for large development shops, they can be paralyzing for small teams. If you want a formal RMF you will be able to find many excellent examples online but the keys are Risk Assessment, identify how the risk is being addressed and track the importance of the risk as the system is developed.

Exercise #1: Building a Risk Assessment for Time Logger

We are going to walk through building a Risk Assessment for Time Logger. This exercise can get as deep as you would like to take it but for the sake of this article, we are going to just do a basic assessment. You can download the following Risk Matrix as a template for entering your Risk Assessment information.

Step 1: Identify threats and probability. This step is easy with the OWASP Top Ten project. The OWASP Top Ten provides a list of the top ten most critical web application security flaws, their impacts and their likelihood of occurrence. The Top Ten list also examines how widespread the attack is online which can be used as a factor when determining probability of attack. Using this list we can start with ten threats to assess the risk in our application. For the purposes of this article, we will examine four of these threats: SQL Injection, Cross Site Scripting (XSS), Cross Site Request Forgery (CSRF) and Insecure Direct Object References.

Step 2: Identify and rank assets and operations. An asset is anything the business places a value on in the application. It could be anything from points of data in the database, files on the server or even a process that only privileged users should be permitted to access. In Time Logger, the business has identified the following assets to protect:

  • Username and Password information
  • Hours of the contractors
  • Login process
  • Hour logging process

After reviewing the application, the development team recommends another item for the asset list: the Database Configuration File. The file is currently just a text file and can be accessed by anyone online but contains all the necessary information to connect to the database. Together the business and development teams rank the assets in order of importance to the application.

Step 3: Identify impact. The OWASP Top Ten gives us technical and business impact for the threats we have identified. We need to take those impacts and illustrate how they will affect the assets identified in step two. When writing impact statements remember that these are for business people, not technical staff. Here is an example of developer to developer impact statement:

“If we don’t fix this it will allow malicious JavaScript commands to be interpreted as commands and not data by the rendering engine in IE”

If you said that to a business person, they would probably think you are really smart but not understand your “techno-babble”. Business must understand the impact of the threat or you might not receive approval (aka project funding) to address it. Here is the same statement for a business person:

“This threat can lead to an attacker using our application to attack our users and damage our corporate image.”

This statement is concise and hits on how the threat will impact users as well as a qualitative impact on the business. Impacts can be quantitative (cost money) or qualitative (something not easily measurable like customer satisfaction). Try to incorporate a quantitative or qualitative impact into each of your statements.

For Time Logger, here are some sample impact statements:

  • SQL Injection threat can lead to attackers deleting our hours accounting information which will lead to our contractors not being paid and leaving the company.
  • XSS threat would allow an attacker to load malware to our users computers leading to frustrated contractors and infected accounting computers.
  • Direct access to the database configuration file would allow attackers to log in to our database directly and steal our contractor’s information costing the company a PR nightmare.

Step 4: Identify how you will address the risk. There are three strategies you can use to address risk, accept the risk, try to mitigate the risk or transfer the risk to a third party. In this step, we identify how we will address the risk in our application. You do not need to go into detail, remember we are still planning how we will secure the system. Here are some sample statements for Time Logger:

“We will mitigate the SQL Injection threat by using parameterized queries, stored procedures and input validation.”

“We will mitigate XSS by using Input Validation and an input sanitization code library.”

These statements will be used by the development teams as guidance on how to address these threats. Statements on how to address risk can be more technical because they are for a technical audience. The business might be interested in how you will address risk but usually they are more interested in the fact that you will be addressing the risk rather than the detailed implementation.

Step 5: Document your Findings. If you have been keeping notes along the way then you have almost completed this step. Remember, this documentation will be your starting point for future assessments so take some time to go back and make sure everything is clear, concise and legible. A formal report is not necessary but will impress your business oriented peers.

On your own

Build out more details, more threats and more impacts for the Time Logger application. You can review the code for inspiration but do not limit your threats to what is obvious in the code. Use the OWASP Top Ten as a baseline of what could be there and how you would suggest mitigating each threat.

Topic 2: Low Hanging Fruit

We have our Risk Assessment in hand and will be reviewing it throughout the project with our Risk Management process. We have identified what we need to secure and some general ideas on how we are going to do that. Now we are ready to start coding. If you have not already, go grab the sample project now from http://seccode.blogspot.com/p/projects_28.html.

In this project, for whatever reason, we cannot rewrite the entire application. Instead, we need to make major impacts with minimal impact to the code base. Input Validation and Data Sanitization are excellent low cost, high impact tactics for securing your application. Both techniques can be implemented at the application level and have their benefits trickle down into other layers of the application. As stated before, we are focusing on client side security so we want to get the most bang for our buck, but if you can secure all the layers of the application do it. Always be as thorough as possible in your security implementations and lockdown every bit of code you can get your fingers on.

Input Validation

Input Validation is the process of checking data coming in to your application to make sure it conforms to an expected format, value or data type. Using Input Validation you can check that when you expect a number to come back from the user, it is a number and not a string, Boolean, object or anything else.

One gotcha that catches many developers is the assumption that malicious input would only come from a user. Adopt the following mantra: “All input is evil!” All is the operative word. This includes data from users, databases, file systems, server variables, header information, etc… Anything can be used as an attack vector against your application so be prepared to check everything. Yes, this will increase overhead in your application but users will appreciate the added protection and security of their data. With the mindset of “All input is evil”, make sure that any input validation that you add to the client side, you include on the server side. Attackers do not always use browsers or JavaScript so make sure your application has checks after the data leaves the browser.

Input Validation Techniques

There are two strategies that you can implement when validating input: White Listing and Black Listing. I think of these strategies as Permit (White List) and Deny (Black List). Let’s examine these strategies in more detail.

Black Listing is the process of stating what should be excluded. For example, imagine that you are at a night club and the doorman is checking names of everyone who comes in. He is letting everyone in except for the people on his list. If you are on the list you cannot come in to the club. Black Listing input works the same way. Any input is allowed in unless it matches characters found on the black list. This technique focuses on exclusion of known values and can fail when an unknown malicious value sneaks through.

White Listing is the process of stating what is allowed, denying everything else. Using our doorman example again, now he does not let anyone in to the club unless your name is on the list. If you are on the list, you can go in to the club otherwise you stand outside. White Listing focuses on declaring a select number of values that are permitted as input and denying all else. The strength of White Listing is only known good values are permitted which keeps unknown values (malicious or otherwise) out of your application. Setting up White Lists can be difficult and time consuming as you have to discover all permitted values to function properly.

Exercise #2: White Listing Input for Time Logger

We are going to implement a White List for the login input in Time Logger’s default.php page. Here is the current HTML of the default.php page:

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />

<title>Time Logger 1.0: Track your time in your contracts</title>

</head>



<body>

    <form action="default.php" method="post">

        <h1>Time Logger</h1>

        <fieldset>

            <legend>Please Login</legend>

            <ol>

                <li>

                    <label for="txtUN">Username: </label><input type="text" id="txtUN" name="txtUN"/>

                </li>

                <li>

                    <label for="txtPW">Password: </label><input type="text" id="txtPW" name="txtPW"/>

                </li>

                <li>

                    <input type="submit" value="Login" id="btnLogin" name="btnLogin" />

                </li>

            </ol>

        </fieldset>

    </form>

</body>



</html>

As you can see the log in control is fairly standard: text boxes for username, password and a button to login. In its current setup, anyone can enter anything into the fields and it will be processed as a username, password combination. This can open the application up to SQL Injection attacks.

XYZ Corporation has a standard username format and password policy that applies to all their applications. In Time Logger, all of the users are restricted to a username between 3 and 7 characters long, all lower case with numbers at the end if the name is duplicated (i.e. the second jsmith could be jsmith1). Passwords in Time Logger must be 6 to 12 characters long and can be comprised of approved special characters. By implementing validation controls to ensure the username and password match these conventions, we can reduce our exposure to SQL Injection (Reduce, not totally negate. To do this we need to update how PHP is handling our SQL statements).

To implement this validation we first need to setup the onsubmit event for the form tag:

<form action="default.php" method="post" onsubmit="return validate()">

The JavaScript method “validate()” will be used to examine the textboxes and ensure their values match the formats we are expecting. If the formats match, the method will return true and pass the page to the processing PHP. We need to let the user know when their entry has failed and to do that we need to add another html tag just inside the fieldset before the ol.

<fieldset>

            <legend>Please Login</legend>

            <div id="msg" style="display:none;"></div>

            <ol>

Now, let’s add the validate function and walk through the code:

<script language="javascript" type="text/javascript">

    function validate(){

        resetMsg();

    

        //validate the username

        var tainted_un = $("#txtUN").val();

        var unValidationString = "^[a-z]{3,7}|[a-z]{3,6}[1-9]$";

        var reUNValidator = new RegExp(unValidationString);

        var un = reUNValidator.exec(tainted_un);

                

        //validate the password conforms to password policy

        var tainted_pw = $("#txtPW").val();

        var pwValidationString = "^[a-zA-Z0-9!]{6,12}$";

        var rePWValidator = new RegExp(pwValidationString);

        var pw = rePWValidator.exec(tainted_pw);

        

        

        if(un != null && pw != null) {

            return true;    //if un and pw are both not null, then both matched

        }

        else {

            //if un OR pw did not match a valid format, then show an error message

            showMsg("Your username or password did not match a valid format.");

            return false;

        }

        

    }

    

    function resetMsg(){

        $("#msg").html("");

        $("#msg").hide();

    }

    

    function showMsg(str){

        $("#msg").html(str);

        $("#msg").show();

    }

    

    $(document).ready(function(){

        <?php

            echo "showMsg('$msg');";

        ?>

    });

</script>

In the validate() function, the first thing we do is clear any existing messages in our msg div. When we pull the value in from the #txtUN textbox, we store the initial value in a variable called “tainted_un”. I suggest using the “tainted_” prefix to separate out values that have not been validated or sanitized yet. This standard will help developers remember not to output tainted_ values back to the user. (this trick is from Securing PHP Web Applications by Tricia and William Ballad  which is an excellent book for securing PHP applications) Now we create the regular expression to validate that our username matches the standard format. If you are not a regular expression whiz, do not worry. There are tons of resources online to help you write the regular expression you are looking for. Here is an excellent resource to start with: 8 Regular Expressions You Should Know [http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know]. This particular expression says that from the start of the provided string (in this case it will be tainted_un) ^ the string can contain any lower case letter [a-z] for at least 3 times up to 7 times {3,7} OR | the string can contain any lower case letter [a-z] three to six times {3,6} and have a number at the end [0-9]$. Next we instantiate the regular expression object and run the execute method against the tainted_un value storing the result in the un variable. If the tainted_un value matches the regular expression’s rules, then the matched string will return otherwise un will be null.

Repeat the same process for the #txtPW field and then check to see if both un and pw have values (denoting that both tainted_ variables matched the formatting rules of the regular expression). If they both have values, then you probably have a properly formatted username and password which can be checked against the database. If either value is null, then the format did not match our expression and a message is displayed to the user stating such.

Here is the completed code for the username and password validation:

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />

<title>Time Logger 1.0: Track your time in your contracts</title>

<script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>

<script language="javascript" type="text/javascript">

    function validate(){

        resetMsg();

    

        //validate the username

        var tainted_un = $("#txtUN").val();

        var unValidationString = "^[a-z]{3,7}|[a-z]{3,6}[1-9]$";

        var reUNValidator = new RegExp(unValidationString);

        var un = reUNValidator.exec(tainted_un);

                

        //validate the password conforms to password policy

        var tainted_pw = $("#txtPW").val();

        var pwValidationString = "^[a-zA-Z0-9!]{6,12}$";

        var rePWValidator = new RegExp(pwValidationString);

        var pw = rePWValidator.exec(tainted_pw);

        

        

        if(un != null && pw != null) {

            return true;    //if un and pw are both not null, then both matched

        }

        else {

            //if un OR pw did not match a valid format, then show an error message

            showMsg("Your username or password did not match a valid format.");

            return false;

        }

        

    }

    

    function resetMsg(){

        $("#msg").html("");

        $("#msg").hide();

    }

    

    function showMsg(str){

        $("#msg").html(str);

        $("#msg").show();

    }

    

    $(document).ready(function(){

        <?php

            echo "showMsg('$msg');";

        ?>

    });

</script>



</head>



<body>

    <form action="default.php" method="post" onsubmit="return validate()">

        <h1>Time Logger</h1>

        <fieldset>

            <legend>Please Login</legend>

            <div id="msg" style="display:none;"></div>

            <ol>

                <li>

                    <label for="txtUN">Username: </label><input type="text" id="txtUN" name="txtUN"/>

                </li>

                <li>

                    <label for="txtPW">Password: </label><input type="text" id="txtPW" name="txtPW"/>

                </li>

                <li>

                    <input type="submit" value="Login" id="btnLogin" name="btnLogin" />

                </li>

            </ol>

        </fieldset>

    </form>

</body>



</html>

On your own

Examine the other pages of the project for more opportunities to implement input validation. Also, do not forget to add the input validation for the PHP code on the server side. As a suggested next task, setup validation on the TimeManager.php page to ensure that Hours are numeric between 0 and 12 hours and that the date field is in a valid date format. Look at the Ajax call and how you can check the returned JSON values to make sure they are properly formatted for display (and not an XSS attack).

Quick Recap

In this article we examined determining our security goals using Risk Management and Risk Assessment. Using the Time Logger project, we built a Risk Assessment that we will use to drive how we plan to secure the application. We examined how Input Validation can be added to your application and implemented in two different strategic approaches (White and Black Listing). We updated Time Logger to implement a White Listing approach to input validation using Regular Expressions in JavaScript.

To be continued…

Next article we will continue with Low Hanging Fruit with Data Sanitization and finish with Data Storage Security. We will continue to secure the Time Logger application and move into a bit of server side code for the Data Storage Security topic.

 

About the Author

Tim Kulp leads the development team at FrontierMEDEX in Baltimore, Maryland. You can find Tim on his blog or the Twitter feed @seccode, where he talks code, security and the Baltimore foodie scene.

Find Tim on: