Improve web application security with jQuery Mobile

Learn how to secure your mobile applications

Many web developers consider security a low priority. Security is frequently relegated to the end of the software development life cycle, as little more than an afterthought. Sometimes, software security is neglected entirely, resulting in applications rife with common vulnerabilities. Because such bugs might manifest only under conditions present during an attack, they can be hard to detect prior to such events without knowledge of how the exploitation process works. Using a web application built with jQuery Mobile, PHP, and MySQL, this tutorial shows how many types of vulnerabilities occur along with common methods of exploitation and, most importantly, their respective countermeasures.

John Leitch is an independent application security consultant living in Grand Rapids, Michigan. Working primarily with web applications, he specializes in fuzz testing, dynamic analysis, and code review. Always on the hunt for bugs, he frequently releases vulnerability advisories.

Before you start

This tutorial is for jQuery Mobile developers interested in securing their
applications. It assumes that the reader has basic knowledge related to web
application development using PHP, MySQL, JavaScript, XHTML, and CSS. Also, this
tutorial is in no way comprehensive; it is intended as an introduction to web
application security. For further reading on the issues covered here, plus
other relevant topics, check Resources.

About this tutorial

Frequently used acronyms

API: Application program interface

CSRF or XSRF: Cross-site request forgery

CSS: Cascading Stylesheets

HTML: Hypertext Markup Language

HTTP: Hypertext Transfer Protocol

OS: Operating system

SQL: Structured Query Language

URL: Uniform Resource Locator

W3C: World Wide Web Consortium

XHTML: Extensible Hypertext Markup Language

XML: Extensible Markup Language

XSS: Cross-site scripting

With the rise of smart phones and similar devices, web application security has been
broadened to include mobile applications. Because of the constraints imposed by the
interfaces of many such devices, developers sometimes work with the flawed assumption
that client-side input validation is sufficient for protection against attacks.
However, requests sent by mobile applications can be manipulated in the same way as
traditional web applications. Because of this vulnerability, the client cannot be
trusted. With sensitive data sometimes stored on devices and the servers that they
use, the protection of users from black-hat hackers is critical. This tutorial shows how several types of vulnerabilities occur and some of the countermeasures that can be put in place to mitigate attackers trying to exploit them. The following types of vulnerabilities are covered:

Cross-site scripting

Cross-site request forgery

Broken access control

SQL injection

File inclusion

OS command injection

Scripting language injection

Arbitrary file creation

All vulnerabilities and countermeasures are demonstrated using a sample application
built with jQuery Mobile, PHP, and MySQL. (See Download
for a .zip file with the sample code.)

Prerequisites

You will need the following tools to complete this tutorial:

Web server— You can use any web server with PHP support. Many of the exploits throughout this tutorial are Windows specific, but they can be adapted for other operating systems. Suggested web servers are Apache or the IBM HTTPServer.

PHP— Because some attacks described do not work against the latest version, PHP 5.3.1 was used. Such incompatibilities are noted throughout the tutorial.

MySQL— This tutorial uses MySQL, an open source database. Version 5.1.41 was used for this tutorial, but other versions should work fine.

Web debugging proxy— Because a way of manipulating HTTP requests is necessary, a web debugging proxy is very helpful. Throughout this tutorial, Fiddler v2.3.2.4 is used, but any other web debugger proxy that allows for modification of requests works.

jQuery Mobile— The front end of the sample application built in this tutorial uses jQuery Mobile 1.0 Alpha 3.

Building an insecure application

You begin this tutorial by creating an insecure example application called the
Contrived Mobile Application (CMA), which serves as a testing ground for the different
types of attacks covered in the following sections. To achieve this test, CMA has two
core pieces of functionality:

A user profile system for customization

A calculator for performing basic arithmetic

Each element of the application introduces security holes that attackers can use for their own ends. With each vulnerability covered, CMA is patched appropriately in an attempt to thwart future hackers.

The schema

As a user-centric application, CMA has a simple schema consisting of one table (see Listing 1).

Listing 2. Improper handling of input passed to require_once

First, a conditional statement checks to see if a language cookie is present. If it is, the PHP extension is appended to the cookie value and the string is passed to the require_once function to load the appropriate script.

Authentication and authorization

Next you add some rudimentary protection. A login form (see Figure 1) is created along with authentication and authorization logic, and upon successfully logging in the session value, CurrentUser is set to the username of the authenticated user. Authorization logic is implemented by checking this session value.

Listing 4. Insecure authorization logic

User search

No user-centric application is complete without the ability to search for other users
within the system. The user search feature accepts keywords and returns matches as an
unordered list (Figure 2). Listing 5 shows the search logic.

Listing 5 performs a variety of functions. First, it outputs the user-submitted query at the top of the page to remind users what they searched for. Following that, the conditions of a SELECT a statement are dynamically built from the user-supplied keywords. The SQL statement is passed to mysql_query, and the script loops through the results, echoing pertinent data as HTML list items.

Figure 2. The results of a user search

Calculator

Because users might need to solve basic arithmetic problems on the fly, CMA offers a
calculator feature. Calculator consists of three inputs: x, y, and operation. Listing 6 shows the code for solving arithmetic problems and displaying the results.

This piece of code first checks the GET data to determine what operation the user selected. Next, it builds the arithmetic problem as a string. It then outputs the complete problem so the user can see it, before interpreting the dynamically built string as PHP.

The code in Listing 7 first builds and executes an UPDATE
statement for the UserAccount table. If the user uploaded an image, it is processed with a mock image compression utility and then moved into the images directory.

Figure 4 shows the user preferences interface with First Name, Last
Name, New Password, Repeat New Password, Profile Picture fields.

Figure 4. The user preferences interface showing the John Doe account

Having gone over relevant CMA functionality, it is time to look more closely at its
implementation. The next section covers the vulnerabilities present, how someone can
exploit them, and what you can do to prevent that exploitation.

Cross-site scripting (XSS)

A website is vulnerable to XSS when a hacker can inject client-side scripts to attack other users. There are two types of XSS: reflected and persistent. It is a common misconception that XSS is nothing more than a nuisance. In some instances of reflected XSS the threat is minor, but in many cases it leaves users vulnerable to account compromise or worse.

Reflected XSS

Reflected XSS occurs when request data is rendered unencoded and unfiltered in the response. With the help of social engineering, an attacker can trick a user into visiting a page that creates such a request, allowing the attacker to execute JavaScript in the context of the targeted user. What can be done with this varies depending on the nature of the hole, but XSS is generally exploited to hijack sessions, steal credentials, or perform otherwise unauthorized actions.

Persistent

Usually a greater threat than the reflected type, an XSS vulnerability is considered persistent when the server saves the user-submitted request data. Because the malicious data is persisted within the application, the social engineering aspect of the attack is made simpler or even removed altogether depending on the exploit.

Exploitation

CMA is loaded with XSS vulnerabilities; the user search alone has both the reflected and persistent types. Exploitation of the reflected type is shown here: http://localhost/CMA/insecure/search.php?query=%3Cscript%3Ealert
(document.cookie)%3C/script%3E

The effects of the reflected XSS attack are visible immediately on navigating to the link, unless client-side countermeasures are in place:Query: <script>alert(document.cookie)</script>

Anti-XSS features might be built into a browser, such as Microsoft® Internet
Explorer® 8, or installed as a plug-in, such as noXSS for Firefox. For your
purposes, client-side filters should not be considered as you cannot rely on users to
have them installed, and in many cases they protect only against the reflected type.
In some instances, client-side filtering actually is counter-productive, introducing universal XSS (UXSS) vulnerabilities. One example of this was in Internet Explorer 8 before Microsoft patched the hole.

To see persistent XSS in action, insert the script tags displayed here into the First Name or Last Name field of the User Preferences form, then search for the user: <script>alert(document.cookie)</script>

The JavaScript is executed when the user is shown in search results.

Prevent cross-site scripting

Stopping XSS attacks is usually a matter of applying the right encoding to user input
in the server response. For the user search reflected XSS example, applying HTML
entity encoding should be sufficient to prevent malicious actions. You can take this step with the PHP API by using the htmlentities function: Query: <?php echo htmlentities($query); ?>.

Testing the attack against the updated code now yields a different result: Query: <script>alert(document.cookie)</script>

The less-than and greater-than characters are now HTML entity encoded, preventing the attacker from injecting markup. The persistent vulnerability is fixed in the same way (see Listing 8) with the htmlentities function.

Listing 8. A CMA modification to prevent persistent XSS

When user-submitted data is injected into an HTML attribute value, take care to ensure that string delimiters used to enclose the value are stripped or encoded within the value itself. Otherwise, attribute injection can take place: <a href='http://www.mywebsite.com/'>My Website</a>

Listing 9. Attribute injection, made possible by the lack of single quote encoding

As an added layer of security, enabling the HttpOnly flag of
the Set-Cookie response header prevents client-side scripts from accessing the
protected cookie. You cannot rely on this feature, however, as some browsers do not entirely support it.

The next section covers another prevalent vulnerability that can be used to launch client-side attacks against other users of a system.

Cross-site request forgery (CSRF or XSRF)

CSRF occurs when an attacker tricks users into performing actions within their security
context. If no security measures are in place, hackers can do this regardless of
whether the form method is GET or POST. Between the two, CSRF attacks that use the GET method are the biggest threat because the request can be forged
using only a URL, which an attacker can use as the source of an image. If the attack
has the ability to arbitrarily set the source of an image within the system, hackers can leverage it to launch an on-site request forgery (OSRF) attack.

Exploitation

Almost every action in CMA can be recreated as a CSRF attack. Listing 10 is an example of a GET-based attack that changes the targeted user's password to new_password.

The outcome of viewing the rendered HTML in Listing 11 is the creation of a request that is identical to that of a legitimate user updating user preferences, but all form values are controlled by the attacker.

Prevent cross-site request forgery

You can prevent CSRF in a couple of common ways. The easiest way to implement is to check the referrer in the HTTP request (see Listing 12); if the request is not from a trusted source, it should be rejected. The more granular the referrer checking, the better the security.

Listing 12. A basic referrer check implementation

This approach, though, is not foolproof. A more secure countermeasure is the employment of a security token. With each protected form, the server includes a long, sufficiently random token value. Each token value is tracked server-side to ensure that it is used only once and expires after a predetermined amount of time. On form submission, if the value is absent, invalid, or expired, the request is rejected on the grounds that it is most likely forged. Without the ability to guess the token value, an attacker is not able to craft an attack. If this security mechanism is applied to every page in the application, it also serves to prevent reflected XSS.

In the upcoming sections, you'll look at several types of server-side vulnerabilities.

Broken access control

Access control issues are often overlooked because, in most cases, access control cannot easily be tested using automated utilities. An application has broken access control when unauthenticated or unauthorized users can access resources to which they should be denied permission. This issue frequently happens when developers try to protect resources intended for authorized users by hiding URLs from unprivileged users. The assumption that this alone protects a resource is flawed; an attacker can still discover the URL through other means, such as inference. Also, users who lose privileges might still be able to access unauthorized resources with URLs that they have saved.

Exploitation

CMA has two access control-related vulnerabilities: authentication bypass and privilege escalation. The authentication bug stems from the failure to terminate execution on finding the user to be unauthenticated. Trying to perform a forbidden action (see Listing 13) in a browser results in seemingly expected behavior: The browser is redirected to the login page.

While the server responded with a 302 status code, the body of the responses contains
everything that an authenticated user receives. Further, the password change was successful, and the targeted account has been compromised.

Aside from the authentication bypass vulnerability, CMA contains another access control
issue: privilege escalation. Whenever a user updates his or her profile, the Id of the user's account, stored in a hidden field of the form as shown here, is submitted along with the form: <input type="hidden" name="userid" id="userid" value="2" />

When the form is posted back to the server, no validation is performed to confirm that
the submitted Id is that of the actual current user before
the value is used to load the record from the database. A malicious user can use browser-based web development tools to change the value of the hidden field before submitting the form or alter the request using a web debugging proxy to specify an arbitrary Id, allowing the malicious user to make changes to user accounts other than their own.

Prevent broken access control

To prevent authentication bypass, ensure that the execution of your protected
application logic does not take place if an authentication or authorization check
fails. With PHP, it is important to remember that setting the Location field of the
response using the header function does not terminate execution. Listing 15 shows authorization code that properly exits.

Listing 15. Improved authorization code

If the user is determined to be unauthorized, the exit function is called after the Location header is set. This step prevents the remainder of the script from being executed.

To eliminate privilege escalation, ensure that proper authorization is performed for
every privileged action. Avoid storing data client-side when it can be stored
server-side. The user ID in Listing 15 is a good example of data
that can be stored in the session. Part of the fix is shown here: $update .= "WHERE Id = $_SESSION[userid]";

Next is SQL injection, a well-known vulnerability with a wide range of potential consequences.

SQL injection

Despite growing awareness, SQL injection still remains a problem. Consequences of a successful SQL injection vary depending on the vulnerability. Some of the threats that can be introduced are these:

Data disclosure

Modification of existing data

Insertion of new data

Arbitrary file system access

Arbitrary network access

System compromise

Exploitation

Every query in CMA is vulnerable to SQL injection, so you'll work with several input vectors. By injecting a condition and commenting out the rest of the query through the username field, authentication can be bypassed. Listing 16 shows the query as it was intended.

Listing 16. The select statement when John and Password1 are submitted as user credentials

Listing 17. The select statement when 'or 1=1;# and an empty password are submitted as credentials

Because 1 is always equal to 1 and the password checking condition is commented out by
the number sign character (#), the query in Listing 17 returns the count of all the records in the UserAccount table. If the count is not zero, the return value of the Authenticate function evaluates to true, granting the attacker access.

The user search functionality is vulnerable in a way that can be exploited to extract arbitrary data, among other things. Listing 18 shows the search query as it was intended to work.

Listing 18. The user search query under normal conditions

SELECT FirstName, LastName FROM UserAccount
WHERE FirstName LIKE '%John%' OR LastName LIKE '%John%';

By utilizing the UNION operator, attackers can append an entirely new query to retrieve data of their choosing: 'and 1=0 UNION SELECT Username, Password FROM UserAccount;#

Listing 19 shows the dynamically generated query after submission of the attack string.

The attack yields the user name and password digest for every user in the database (see Figure 5).

Figure 5. The result of successful injection using the UNION operator

Prevent SQL injection

To prevent SQL injection, you must properly escape and validate all user-submitted
input. Most web development APIs come with functions to achieve this. With PHP and
MySQL, use parameterized queries along with mysql_real_escape_string for string values to protect from many attacks (see Listing 20).

Listing 22. Safely creating a query using an untrusted integer

File inclusion

There are two types of file inclusion: remote and local. As the name implies, this type of vulnerability allows an attacker to arbitrarily include a file. Whether the result is disclosure of the contents of the file or execution as code depends on the nature of the exploit. With PHP, remote file inclusion is generally not possible if allow_url_fopen is disabled in the php.ini file.

Exploitation

The language cookie in CMA is vulnerable to local file inclusion and, if the server is configured to allow opening URLs, remote file inclusion. By passing a series of traversal sequences along with a folder and file outside of the webroot followed by a null byte to terminate the string, arbitrary files can be included. Listing 23 shows a malicious request that includes the win.ini file.

Listing 23. A malicious request that attempts to retrieve the win.ini file of the server

Note that null byte poisoning of paths no longer works as of PHP 5.3.4. In some
instances, though, it is not required, so do not consider this alone as a fix for file inclusion vulnerabilities.

Aside from disclosing arbitrary files, file inclusion can sometimes be used to trick the server into interpreting arbitrary file types (such as jpgs) as code.

Prevent file inclusion

If possible, avoid passing user input to any functions that read or include files. If this approach cannot be avoided, try taking a whitelist approach to validating data, as is done in Listing 25. If the number of valid values is too great for a whitelist, check for any traversal sequences or null bytes and reject (do not attempt to sanitize) the request. Ensure that the server appends the extension of the user-submitted filename.

OS command injection

As you might expect, OS command injection is a very serious threat. If user input is
passed to a function that executes operating system commands, take great care to ensure that the data is properly escaped.

Exploitation

The mock image compression of the user preferences functionality is vulnerable to OS
command injection. Commands can be injected by passing the pipe character (|) followed by a malicious command using the image compression data in the body of the request (see Listing 26).

The value passed to the system function is shown here: ping "C:\tools\xampp\tmp\php7533.tmp" 5|calc

Prevent OS command injection

Avoid passing user input to functions that execute OS commands. You usually can use safer API functions to achieve a similar outcome. If a safer approach cannot be taken and untrusted data must be used to create command-line arguments, ensure that the data is properly escaped. The PHP API provides a function for escaping dangerous characters called escapeshellcmd. Listing 27 shows the corrected user preference code.

Scripting language injection

A scripting language injection vulnerability is present when user input is interpreted as code. In many cases, this leads to compromise of the server as the attacker is able to execute code within the security context of the interpreter process.

Exploitation

Because user-submitted data is passed to the eval function, the calculator functionality of CMA is vulnerable to scripting language injection. Both the X and Y inputs can be used to execute arbitrary code, but because they are of the number type, client-side restrictions have to be bypassed. This bypass can be achieved by using a web debugging proxy:

Prevent scripting language injection

Avoid evaluating user input as code. Relevant functionality can usually be created by using safer API functions (this is the approach taken in Listing 28). If it cannot be avoided, apply strict validation (whitelist if possible), and reject any input that is deemed unsafe. Do not attempt to sanitize user input.

Arbitrary file creation

In many instances, the result of arbitrary file creation is similar to scripting
language injection; an attacker can create a file with the appropriate extension and
then access it to execute arbitrary code. The attacker can achieve this several
ways, and you need to use caution when you work with any functionality that can be leveraged to create files. In some situations, this functionality can be combined with other vulnerabilities, such as directory traversal, allowing the attacker to wreak more damage.

Exploitation

One way to create an arbitrary file is to use the profile picture feature of the user preferences to upload a PHP file rather than an image. A simple script can provide a remote shell:
<?php system($_GET["CMD"]); ?>.
After it is uploaded, an attacker can then access the script to run OS commands with ease:http://localhost/CMA/insecure/images/shell.php?CMD=calc

Depending on whether an appropriate SQL injection vulnerability is present and the configuration of the server, it might be possible to leverage SQL to create a new script. Depending on the SQL server permissions, it might be possible to utilize directory traversal to overwrite critical system files, effectively compromising the server: SELECT '<?php system($_GET["CMD"]); ?>' FROM dual INTO OUTFILE '../../htdocs/shell.php'

Listing 29. The shell creating query injected into CMA using the UNION operator

You can discover target paths for this type of attack through trial and error or by leveraging an information leakage vulnerability (not covered in this tutorial) that reveals the absolute path of the document root.

Prevent arbitrary file creation

If possible, perform whitelist validation on the extensions of any files that users
might create. This approach is the approach used to fix CMA, shown in Listing 30 and Listing 31. If a similar fix cannot be implemented, use blacklist validation to ensure that no malicious extensions are allowed. For Apache and PHP, this approach means rejecting several extensions, such as PHP, PHTML, and HTACCESS. If input is deemed malicious, reject it; do not attempt to sanitize any questionable data.

The IsNullPoisoned function checks the string for any null bytes and returns true if the position is not null while the IsValidImageExtension function checks to ensure that the filename is not null poisoned and that its extension is in the whitelist.

Listing 33. Corrected regular expression-based validation

Check the filename for null byte poisoning before you use preg_match to prevent the attacker from injecting the string-terminating character.

Ensure that all SQL injection vulnerabilities are patched to stop attackers from using database server functionality to manipulate the file system. If the application does not require such functionality, consider disabling the features using database privileges. If possible, run the database server on a separate server than the HTTP server.

For an extra layer of security, store user-uploaded files outside of the document root or forbid access to users by using web server features if direct access is unnecessary. If an attacker is able to bypass file extension filters, this approach makes accessing and executing the malicious script more difficult. Do not give the client control of the upload destination folder; otherwise, an attacker might use directory traversal (covered earlier in this tutorial) to store the file in an unprotected directory.

Summary

As already stated, this tutorial is in no way comprehensive. In fact, no such source
exists due to the ever-changing landscape of software security. The best protection
against continually evolving attackers is to stay current by regularly reading about
new security threats. For several excellent sources that examine in depth why
vulnerabilities occur and what can be done to prevent them, see Resources. Remember, just as a system cannot be declared bug free, it cannot be deemed completely secure.

Download

Resources

Learn

Working with jQuery (Michael Abernethy, developerWorks, September 2008): Get to know jQuery and learn to implement it in your own web application projects in this good introduction to the JavaScript framework.

Locking down your PHP applications (Thomas Myer, developerWorks, May 2006): Learn more about securing PHP applications and guard against the most common security threats: SQL injections, the manipulation of the GET and POST variables, buffer overflow attacks, cross-site scripting attacks, data manipulation inside the browser, and remote form posting.

Overcome security threats for Ajax applications (Sachiko Yoshihama, Frederik De Keukelaere, Michael Steiner, Naohiko Uramoto; developerWorks, June 2007): Learn more about client-side attacks and how to avoid some of the most common attacks.

Packet Storm: Explore an excellent source for exploits, advisories, and tools both old and new.

The Web Application Hacker's Handbook (Dafydd Stuttard and Marcus Pinto, Wiley, October 2007): Get a broader view of web application security in this practical guide to finding and exploiting security flaws.

The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.