Home > Articles > Security > General Security and Privacy

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Specific Security Mistakes

Beyond the general danger of revealing application logic to potential attackers, there are specific mistakes that programmers make when writing client-side code that can open their applications to attack.

Improper Authorization

Let's return to MyLocalWeatherForecast.com. MyLocalWeatherForecast.com has an administration page, where site administrators can check usage statistics. The site requires administrative authorization rights in order to access this page. Site users and other prying eyes are, hence, prevented from viewing the sensitive content.

Because the site already used Ajax to retrieve the weather forecast data, the programmers continued this model and used Ajax to retrieve the administrative data: They added client-side JavaScript code that pulls the usage statistics from the server, as shown in Figure 6-7.

Figure 6-7

Figure 6-7 Intended usage of the Ajax administration functionality

Unfortunately, while the developers at MyLocalWeatherForecast.com were diligent about restricting access to the administration page (admin.php), they neglected to restrict access to the server API that provides the actual data to that page. While an attacker would be blocked from accessing admin.php, there is nothing to prevent him from calling the GetUsageStatistics function directly. This technique is illustrated in Figure 6-8.

Figure 6-8

Figure 6-8 Hacking the administration functionality by directly accessing the client-side JavaScript function

There is no reason for the hacker to try to gain access to admin.php. He can dispense with the usual, tedious authorization bypass attacks like hijacking a legitimate user's session or guessing a username and password through brute force. Instead, he can simply ask the server for the administrative data without having to go to the administrative page, just as Eve did in her attack on HighTechVacations.net in Chapter 2. The programmers at MyLocalWeatherForecast.com never intended the GetUsageStatistics function to be called from any page other than admin.php. They might not have even realized that it could be called from any other page. Nevertheless, their application has been hacked and they are to blame.

Some of the worst cases of improperly authorized API methods come from sites that were once standard Web applications but were later converted to Ajax-enabled applications. You must take care when Ajaxifying applications in order to avoid accidentally exposing sensitive or trusted server-side functionality. In one real-world example of this, the developers of a Web framework made all their user management functionality available through Ajax calls. Just like our fictional developers at MyLocalWeatherForecast.com, they neglected to add authorization to the server code. As a result, any attacker could easily add new users to the system, remove existing users, or change users' passwords at will.

Overly Granular Server API

The lack of proper authorization in the previous section is really just a specific case of a much broader and more dangerous problem: the overly granular server API. This problem occurs when programmers expose a server API and assume that the only consumers of that API will be the pages of their applications and that those pages will always use that API in exactly the way that the programmers intended. The truth is, an attacker can easily manipulate the intended control flow of any client-side script code. Let's revisit the online music store example from Chapter 1, "Introduction to Ajax Security."

function purchaseSong(username, password, songId) {

  // first authenticate the user
  var authenticated = checkCredentials(username, password);
  if (authenticated == false) {
    alert('The username or password is incorrect.');
    return;
  }

  // get the price of the song
  var songPrice = getSongPrice(songId);

  // make sure the user has enough money in his account
  if (getAccountBalance(username) < songPrice) {
    alert('You do not have enough money in your account.');
    return;
  }

  // debit the user's account
  debitAccount(username, songPrice);

  // start downloading the song to the client machine
  downloadSong(songId);
}

The intended flow of this code is straightforward. First the application checks the user's username and password, then it retrieves the price of the selected song and makes sure the user has enough money in his account to purchase it. Next, it debits the user's account for the appropriate amount, and finally it allows the song to download to the user's computer. All of this works fine for a legitimate user. But let's think like our hacker Eve would and attach a JavaScript debugger to the page to see what kind of havoc we can wreak.

We will start with the debugger Firebug for Firefox. Firebug will display the raw HTML, DOM object values, and any currently loaded script source code for the current page. It will also allow the user to place breakpoints on lines of script, as we do in Figure 6-9.

Figure 6-9

Figure 6-9 Attaching a breakpoint to JavaScript with Firebug

You can see that a breakpoint has been hit just before the call to the checkCredentials function. Let's step over this line, allow the client to call checkCredentials, and examine the return value (see Figure 6-10).

Figure 6-10

Figure 6-10 Examining the return value from checkCredentials

Unfortunately, the username and password we provided do not appear to be valid. The value of the authenticated variable as returned from checkCredentials is false, and if we allow execution of this code to proceed as-is, the page will alert us that the credentials are invalid and then exit the purchaseSong function. However, as a hacker, this does us absolutely no good. Before we proceed, let's use Firebug to alter the value of authenticated from false to true, as we have done in Figure 6-11.

Figure 6-11

Figure 6-11 The attacker has modified the value of the authenticated variable from false to true.

By editing the value of the variable, we have modified the intended flow of the application. If we were to let the code continue execution at this point, it would assume (incorrectly) that we have a valid username and password, and proceed to retrieve the price of the selected song. However, while we have the black hat on, why should we stop at just bypassing authentication? We can use this exact same technique to modify the returned value of the song price, from $.99 to $.01 or free. Or, we could cut out the middleman and just use the Console window in Firebug to call the downloadSong function directly.

In this example, all of the required steps of the transaction—checking the user's credentials, ensuring that she had enough money in her account, debiting the account, and downloading the song—should have been encapsulated as one single public function. Instead of exposing all of these steps as individual methods in the server API, the programmers should have written a single purchaseSong method that would execute on the server and enforce the individual steps to be called in the correct order with the correct parameter values. The exposure of overly-granular server APIs is one of the most critical security issues facing Ajax applications today. It bears repeating: Never assume that client-side code will be executed the way you intend—or even that it will be executed at all.

Session State Stored in JavaScript

The issue of inappropriately storing session state on the client is nothing new. One of the most infamous security vulnerabilities of all time is the client-side pricing vulnerability. Client-side pricing vulnerabilities occur when applications store item prices in a client-side state mechanism, such as a hidden form field or a cookie, rather than in server-side state. The problem with client-side state mechanisms is that they rely on the user to return the state to the server without tampering with it. Of course, trusting a user to hold data as tantalizing as item prices without tampering with it is like trusting a five-year-old to hold an ice cream cone without tasting it. When users are capable of deciding how much they want to pay for items, you can be certain that free is going to be a popular choice.

While this issue is not new to Ajax, Ajax does add a new attack vector: state stored in client-side JavaScript variables. Remember the code from the online music store:

  // get the price of the song
  var songPrice = getSongPrice(songId);

  // make sure the user has enough money in his account
  if (getAccountBalance(username) < songPrice) {
    alert('You do not have enough money in your account.');
    return;
  }

  // debit the user's account
  debitAccount(username, songPrice);

By storing the song price in a client-side JavaScript variable, the application invites attackers to modify the value and pay whatever they like for their music. We touched on this concept earlier, in the context of making the server API too granular and allowing an attacker to manipulate the intended control flow. However, the problem of storing session state on the client is separate from the problem of having an API that is too granular.

For example, suppose that the server exposes an AddItem function to add an item to the shopping cart and a second function, CheckOut, to check out. This is a well-defined API in terms of granularity, but if the application relies on the client-side code to keep a running total of the shopping cart price, and that running total is passed to the CheckOut function, then the application is vulnerable to a client-side pricing attack.

Sensitive Data Revealed to Users

Programmers often hard code string values into their applications. This practice is usually frowned upon due to localization issues—for example, it is harder to translate an application into Spanish or Japanese if there are English words and sentences hard coded throughout the source code. However, depending on the string values, there could be security implications as well. If the programmer has hard coded a database connection string or authentication credentials into the application, then anyone with access to the source code now has credentials to the corresponding database or secure area of the application.

Programmers also frequently misuse sensitive strings by processing discount codes on the client. Let's say that the music store in our previous example wanted to reward its best customers by offering them a 50-percent-off discount. The music store emails these customers a special code that they can enter on the order form to receive the discount. In order to improve response time and save processing power on the Web server, the programmers implemented the discount logic in the client-side code rather than the server-side code.

    <script type="text/javascript">

    function processDiscountCode(discountCode) {
        if (discountCode == "HALF-OFF-MUSIC") {
            // redirect request to the secret discount order page
            window.location = "SecretDiscountOrderForm.html";
        }
    }
    </script>

The programmers must not have been expecting anyone to view the page source of the order form, because if they had, they would have realized that their "secret" discount code is plainly visible for anyone to find. Now everyone can have their music for half price.

In some cases, the sensitive string doesn't even have to be a string. Some numeric values should be kept just as secret as connection strings or login credentials. Most e-commerce Web sites would not want a user to know the profit the company is making on each item in the catalog. Most companies would not want their employees' salaries published in the employee directory on the company intranet.

It is dangerous to hard code sensitive information even into server-side code, but in client-side code it is absolutely fatal. With just five seconds worth of effort, even the most unskilled n00b hacker can capture enough information to gain unauthorized access to sensitive areas and resources of your application. The ease with which this vulnerability can be exploited really highlights it as a critical danger. It is possible to extract hard coded values from desktop applications using disassembly tools like IDA Pro or .NET Reflector, or by attaching a debugger and stepping through the compiled code. This approach requires at least a modest level of time and ability, and, again, it only works for desktop applications. There is no guaranteed way to be able to extract data from server-side Web application code; this is usually only possible through some other configuration error, such as an overly detailed error message or a publicly accessible backup file. With client-side JavaScript, though, all the attacker needs to do is click the View Source option in his Web browser. From a hacker's point of view, this is as easy as it gets.

Comments and Documentation Included in Client-Side Code

The dangers of using code comments in client code have already been discussed briefly in Chapter 5, but it is worth mentioning them again here, in the context of code transparency. Any code comments or documentation added to client-side code will be accessible by the end user, just like the rest of the source code. When a programmer explains the logic of a particularly complicated function in source documentation, she is not only making it easier for her colleagues to understand, but also her attackers.

In general, you should minimize any practice that increases code transparency. On the other hand, it is important for programmers to document their code so that other people can maintain and extend it. The best solution is to allow (or force?) programmers to document their code appropriately during development, but not to deploy this code. Instead, the developers should make a copy with the documentation comments stripped out. This comment-less version of the code should be deployed to the production Web server. This approach is similar to the best practice concerning debug code. It is unreasonable and unproductive to prohibit programmers from creating debug versions of their applications, but these versions should never be deployed to a production environment. Instead, a mirrored version of the application, minus the debug information, is created for deployment. This is the perfect approach to follow for client-side code documentation as well.

This approach does require vigilance from the developers. They must remember to never directly modify the production code, and to always create the comment-less copy before deploying the application. This may seem like a fragile process that is prone to human error. To a certain extent that is true, but we are caught between the rock of security vulnerabilities (documented code being visible to attackers) and the hard place of unmaintainable code (no documentation whatsoever). A good way to mitigate this risk is to write a tool (or purchase one from a third party) that automatically strips out code comments. Run this tool as part of your deployment process so that stripping comments out of production code is not forgotten.

Data Transformation Performed on the Client

Virtually every Web application has to handle the issue of transforming raw data into HTML. Any data retrieved from a database, XML document, binary file—or any other storage location—must be formatted into HTML before it can be displayed to a user. In traditional Web applications, this transformation is performed on the server, along with all the other HTML that needs to be generated. However, Ajax applications are often designed in such a way that this data transformation is performed on the client instead of the server.

In some Ajax applications, the responses received from the partial update requests contain HTML ready to be inserted into the page DOM, and the client is not required to perform any data processing. Applications that use the ASP.NET AJAX UpdatePanel control work this way. In the majority of cases, though, the responses from the partial updates contain raw data in XML or JSON format that needs to be transformed into HTML before being inserted into the page DOM. There are many good reasons to design an Ajax application to work in this manner. Data transformation is computationally expensive. If we could get the client to do some of the heavy lifting of the application logic, we could improve the overall performance and scalability of the application by reducing the stress on the server. The downside to this approach is that performing data transformation on the client can greatly increase the impact of any code injection vulnerabilities such as SQL Injection and XPath Injection.

Code injection attacks can be very tedious to perform. SQL Injection attacks, in particular, are notoriously frustrating. One of the goals of a typical SQL Injection attack is to break out of the table referenced by the query and retrieve data from other tables. For example, assume that a SQL query executed on the server is as follows:

SELECT * FROM [Customer] WHERE CustomerId = <user input>

An attacker will try to inject her own SQL into this query in order to select data from tables other than the Customer table, such as the OrderHistory table or the CreditCard table. The usual method used to accomplish this is to inject a UNION SELECT clause into the query statement (the injected code is shown in italics):

SELECT * FROM [Customer] WHERE CustomerId = x;
UNION SELECT * FROM [CreditCard]

The problem with this is that the results of UNION SELECT clauses must have exactly the same number and type of columns as the results of the original SELECT statement. The command shown in the example above will fail unless the Customer and CreditCard tables have identical data schemas. UNION SELECT SQL Injection attacks also rely heavily on verbose error messages being returned from the server. If the application developers have taken the proper precautions to prevent this, then the attacker is forced to attempt blind SQL Injection attacks (covered in depth in Chapter 3), which are even more tedious than UNION SELECTs.

However, when the query results are transformed into HTML on the client instead of the server, neither of these slow, inefficient techniques is necessary. A simple appended SELECT clause is all that is required to extract all the data from the database. Consider our previous SQL query example:

SELECT * FROM [Customer] WHERE CustomerId = <user input>

If we pass a valid value like "gabriel" for the CustomerId, the server will return an XML fragment that would then be parsed and inserted into the page DOM.

<data>
  <customer>
    <customerid>gabriel</customerid>
    <lastname>Krahulik</lastname>
    <firstname>Mike</firstname>
    <phone>707-555-2745</phone>
  </customer>
</data>

Now, let's try to SQL inject the database to retrieve the CreditCard table data simply by injecting a SELECT clause (the injected code is shown in italics).

SELECT * FROM [Customer] WHERE CustomerId = x;
SELECT * FROM [CreditCard]

If the results of this query are directly serialized and returned to the client, it is likely that the results will contain the data from the injected SELECT clause.

<data>
  <creditcard>
    <lastname>Holkins</lastname>
    <firstname>Jerry</firstname>
    <ccnumber>1234567812345678</ccnumber>
    <expirationDate>09-07-2010</expirationDate>
  </creditcard>
  <creditcard>
  ...
</data>

At this point, the client-side logic that displays the returned data may fail because the data is not in the expected format. However, this is irrelevant because the attacker has already won. Even if the stolen data is not displayed in the page, it was included with the server's response, and any competent hacker will be using a local proxy or packet sniffing tool so that he can examine the raw contents of the HTTP messages being exchanged.

Using this simplified SQL Injection technique, an attacker can extract out the entire contents of the back end database with just a few simple requests. A hack that previously would require thousands of requests over a matter of hours or days might now take only a few seconds. This not only makes the hacker's job easier, it also improves his chances of success because there is less likelihood that he will be caught by an intrusion detection system. Making 20 requests to the system is much less suspicious than making 20,000 requests to the system.

This simplified code injection technique is by no means limited to use with SQL Injection. If the server code is using an XPath query to retrieve data from an XML document, it may be possible for an attacker to inject his own malicious XPath clause into the query. Consider the following XPath query:

/Customer[CustomerId = <user input>]

An attacker could XPath inject this query as follows (the injected code is shown in italics):

/Customer[CustomerId = x] | /*

The | character is the equivalent of a SQL JOIN statement in XPath, and the /* clause instructs the query to return all of the data in the root node of the XML document tree. The data returned from this query will be all customers with a customer ID of x (probably an empty list) combined with the complete document. With a single request, the attacker has stolen the complete contents of the back end XML.

While the injectable query code (whether SQL or XPath) is the main culprit in this vulnerability, the fact that the raw query results are being returned to the client is definitely a contributing factor. This design antipattern is typically only found in Ajax applications and occasionally in Web services. The reason for this is that Web applications (Ajax or otherwise) are rarely intended to display the results of arbitrary user queries.

Queries are usually meant to return a specific, predetermined set of data to be displayed or acted on. In our earlier example, the SQL query was intended to return the ID, first name, last name, and phone number of the given customer. In traditional Web applications, these values are typically retrieved by element or column name from the query result set and written into the page HTML. Any attempt to inject a simplified ;SELECT attack clause into a traditional Web application query may succeed; but because the raw results are never returned to the client and the server simply discards any unexpected values, there is no way for the attacker to exploit the vulnerability. This is illustrated in Figure 6-12.

Figure 6-12

Figure 6-12 A traditional Web application using server-side data transformation will not return the attacker's desired data.

Compare these results with the results of an injection attack against an Ajax application that performs client-side data transformation (as shown in Figure 6-13). You will see that it is much easier for an attacker to extract data from the Ajax application.

Figure 6-13

Figure 6-13 An Ajax application using client-side data transformation does return the attacker's desired data.

Common implementation examples of this antipattern include:

  • Use of the FOR XML clause in Microsoft SQL Server
  • Returning .NET System.Data.DataSet objects to the client
  • Addressing query result elements by numeric index rather than name
  • Returning raw XPath/XQuery results

The solution to this problem is to implement a query output validation routine. Just as we validate all input to the query to ensure that it matches a predetermined format, we should also validate all output from the query to ensure that only the desired data elements are being returned to the client.

It is important to note that the choice of XML as the message format is irrelevant to the vulnerability. Whether we choose XML, JSON, comma-separated values, or any other format to send data to the client, the vulnerability can still be exploited unless we validate both the incoming query parameters and the outgoing results.

  • + Share This
  • 🔖 Save To Your Account