Seven principles for preventing vulnerabilities in PHP programming

7. Use Test Programs

It is essential to use automated tests alongside manual testing. To test an individual feature, it is often sufficient to use a self-written test program that is familiar with a lot of input values and the corresponding output values and compare the test results with the real results.

However, if the program you are testing is a bit more elaborate, it is no longer effective to write your own test programs. Fortunately, PHP has many test systems, such as PHPUnit [1], SimpleTest [2], or TestPlan [3].

PHPUnit and SimpleTest both serve to test the individual functions and instructions of the program – for example, whether variable values are valid or whether functions and function blocks return correct values. Thus, both PHPUnit and SimpleTest are suitable only for PHP environments. The manual for PHPUnit [4] provides a very good introduction to automated testing – knowledge that is easily transferable to SimpleTest, which has sparse documentation. Both are functionally and conceptually comparable.

TestPlan, on the other hand, checks to see whether the program returns expected values with specific user input; that is, it tests the response to certain inputs. For this purpose, TestPlan has its own scripting language that is optimized for performing basic interactions on web pages, such as entering data in forms or clicking on links. The programmer can then search the output for key words or character strings. This way, programmers can easily check to see whether the application works from a user perspective. If you select the input values skillfully, you can also discover critical situations (see the box titled "TestPlan in Practice").

TestPlan in Practice

Installation of TestPlan is relatively simple and explained well on the homepage [3], except for how to set the TESTPLAN_HOME environment variable correctly; for this article, it would be:

export TESTPLAN_HOME=~/testplan-1-0-r6/

On the author's Fedora test computer, it was not necessary to set JAVA_HOME.

The first simple test in Listing 1 checks the number of links on the author's homepage, thereby demonstrating multiple items from TestPlan's scripting language: It supports loops, can count, extracts things from the output, and even generates output itself. Thus line 5 selects the links in the %Response% output with parameter a, which corresponds to the HTML tag for a link. The href parameter in line 6 points to the URL returned by line 7.

Listing 1

Count Links

01 default %Cmds.Site% http://www.eggendorfer.info/
02 GotoURL %Cmds.Site%
03
04 set %Count% 1
05 foreach %Link% in %Response://a%
06   set %URL% as selectIn %Link% @href
07   Notice %Count% Link: %Link% %URL%
08   set %Count% as binOp %Count% + 1
09 end

Listing 2 contains the simple counting test from Listing 1, but it is more complex: Here, TestPlan logs on to Facebook for the author and verifies whether HTTPS is enabled. This check is performed in two stages: First, the Check function ensures that the desired text is found on the page. If that is successful, the test script continues running. It then reads the message, removes the HTML from it, and uses a regular expression to check whether enabled is present. (Frequent tests in the form of Listing 2 will, however, cause Facebook to become suspicious, and it will require a CAPTCHA for login.)

Listing 2

Test Facebook

01 default %Cmds.Site% https://www.facebook.com
02 GotoURL %Cmds.Site%
03
04 SubmitForm with
05   %Form% id:login_form
06   %Params:email% someone@somewhere.com
07   %Params:pass% somesecurepassword
08   %Submit% key:enter
09 end
10
11 set %Count% 1
12 foreach %Link% in %Response://a%
13   set %URL% as selectIn %Link% @href
14   set %Count% as binOp %Count% + 1
15 end
16
17 Notice %Count% Links on Facebook homepage.
18 GotoURL https://www.facebook.com/settings?tab=security
19
20 Notice Check if HTTPS is enabled.
21 Check //span[contains(text(),'Secure browsing is currently')]
22 set %all% %Response://span[contains(text(),'Secure browsing is currently')]%
23 if strMatches %all% ^(Secure browsing is currently).* (enabled)\.$
24 Pass HTTPS enabled
25 else
26   Notice HTTPS disabled
27   Notice Try to enable HTTPS.
28   Notice Subsequently start test again.
29   GotoURL https://www.facebook.com/settings?tab=security&section=browsing&view
30   set %id% %Response://form[@action='/ajax/settings/security/browsing.php']/@id%
31   set %fb_dtsg% %Response://form[@action='/ajax/settings/security/browsing.php']/input[@name='fb_dtsg']/@value%
32   SubmitForm with
33     %Form% id:%id%
34     %Params:secure_browsing% 1
35     %Params:fb_dtsg% %fb_dtsg%
36     %Submit% value:Save changes
37   end
38   Notice Test HTTPS again
39   Fail HTTPS
40 end

If enabled is present, the script reports this test as having been passed (see Figure 3); otherwise, the test will automatically try to enable HTTPS itself (Figure 4, line 6-00). Because Facebook assigns a form a new, seemingly random ID on each new access, you'll have to employ a small trick. With the XPath expression in line 30, it is possible to read the ID. The result is deposited in the local variable %id%. This is also the case for the contents of a hidden input field in line 31. With these values, you can address the desired form on the page (line 33), filled out correctly and posted to Facebook.

If you now want to test automatically whether Facebook has really enabled HTTPS – which is necessary if the test is to continue, you must now write a small test to see whether Facebook has logged the user out again. Depending on the result, the script must log on again if necessary and then run the test again. The TestPlan language includes the possibility of calling up external test modules, so it is sufficient to implement the HTTPS test once – your own test script will then call up the module in several places. This approach simplifies the maintenance of the test.

Fundamentally, TestPlan's own language is powerful and easy to learn, but it is poorly documented. A bit of initial experimentation is therefore necessary. TestPlan is worth using because of its flexibility – and not just for your own web applications, but for small (and of course, legal) attacks as well.

In all cases, it must be clear in the developer's mind under which conditions the program runs correctly, where the limits are, and which inputs it will not tolerate. This knowledge is transferred into the tests, which reduces the likelihood of error.

Is All This Worthwhile?

Unfortunately, systematic testing of web applications is still not very widespread. Cost and time pressures are the most common reasons for the lack of focus on testing. That is strange, because as soon as attackers take over your website, nobody thinks about time and budgets anymore. Even those who do not want to include loss of revenue and reputation in the equation should keep in mind that, once suitable test processes are established, the overhead for maintaining those processes is quite manageable. The knowledge gained from systematic testing helps prevent the same mistakes from happening again, and the quality of future work improves.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News