When developing a web application one must be aware of a few common forms of attack.
A spoofed form is a form which looks legitimate, but is not the same form a user thinks it is. The easiest way for hackers to spoof forms is to copy the target form from a web site, and then have it executed it from a different location. The hacker takes the legitimate form, removes security restrictions previously imposed on the data, and then has the data sent to its intended destination when the form is submitted. An example is a form where a drop down box is changed to a text field on the spoofed form, allowing for potentially unrestricted data access in that field. A security problem could arise if the developer assumes the field which held the drop down box didn’t need to be properly filtered or escaped.
One way to check to see if a form is being submitted from an authorized location is to check the REFERER header in the PHP $_SERVER superglobal array (e.g. $_SERVER[‘REFERER’]). While this should be done, it is not completely reliable as the REFERER header is coming from the client’s browser. As with anything originating from a client’s machine it cannot be fully trusted.
Cross-Site Scripting (XSS)
Cross Site Request Forgeries (CSRF)
By using a forged request, CSRF exploits an application’s trust in a user. Even if a web application uses POST requests, the malicious user will try to send GET requests to the web application in the hopes that it will work. For example, the attacker might try:
<img src="http://victim.com/checkout.php?productid=9918719293" />
This will work only if the user is logged into the target web site (example.org) while on the other web site hosting the malicious code.
To counter this attack, a web application needs to be able to determine the legitimacy of each request. A good way to do this is by using the token method. When a new web application session is started, a random token is generated and placed in the $_SESSION superglobal array.
$session_start(); $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token;
This token is then placed in a hidden form field before being sent back to the client. When a new request comes in from the client the $_POST[‘token’] value is checked with the value in $_SESSION[‘token’]. If the two match then the request is legitimate.
In PHP, when a new web application session is started it sends a cookie to the client. By default this cookie (session) name is called PHPSESSID. This name can be changed either by changing the session.name setting in php.ini, or it can be changed programmatically via the session_name() function. A session fixation (aka session riding) attack fixes a particular web request to a specific session id by setting it in a GET header. For example:
By doing this the attacker may be able to gain access to a user’s account. The way to thwart such an attack is to change the session id by calling the session_regenerate_id() function every time a user’s privilege level changes.
What if after session_regenerate_id() is called the attacker discovers the new session id and attempts to gain access through this new id? This is called session hijacking. One way to thwart such an attack is to record the client’s User-Agent request header. Save it in a session variable (e.g. $_SESSION[‘user_agent’] and check it on subsequent requests to see if it has changed. If it changes terminate the session with an error message and send it back to the client.
SQL injection occurs when client input has not been properly filtered and escaped. Such attacks will try to inject SQL code into normal form fields in the hopes of getting the web site to return useful information such as user names and passwords. For example, given a user name and password form:
<form> Login: <input type="text" name="login" /><br/> Password: <input type="text" name="password" /><br/> </form>
A typical attack might be to put in the login (or password) field something like this for a site using MS SQL Server:
login' OR 1=1--
For non MS SQL sites, this might work:
' OR 'a'='a
Its also possible for an attacker to download the login form, and try to insert this code into some hidden fields. Another approach would be for an attacker might try to put this into the target website’s url, for example:
http://victim.com/index.asp?id=login' OR 1=1--
The idea is for the attacker to get logged in without a valid login or password by changing the SQL used to access the user database from this:
SELECT * FROM users WHERE login = 'login' AND password = 'password';
SELECT * FROM users WHERE login = 'login' OR 1=1-- AND password = 'password';
In MS SQL the ‘—’ notation tells MS SQL to ignore the rest of the line. The conditional ‘1=1’ will always evaluate to TRUE, and TRUE OR FALSE will evaluate to TRUE. Therefore the SQL SELECT will return the user table if this code is allowed to execute.
The solution to this potential attack is simple: never trust ANY user input. Always escape and filter all user input and SQL output to eliminate this possibility from occurring. Input can be filtered by custom filters or simple ones such as ctype_alnum() or it can be filtered with the database specific functions such as mysql_real_escape_string() or sqlite_escape_string(). Escaping and filtering user input makes it safe to use in SQL statements and queries.
Remote Code Injection
PHP has the ability to dynamically include PHP source using the include or require directives. For example, given web application source code which contains the following line would be a remote code injection security risk:
…could lead to an attacker typing this into his browser address bar:
This would cause the code at the remote site to be injected into the web server application, rendering the entire application at the mercy of the attacker. Note the %3F at the end of the string would have the effect of treating the remainder of the include “/comment.php” to be treated as a variable to the remote site’s pwnage.php script thus rendering the local script useless.
There are two server settings that can be set in php.ini which can turn off this functionality: allow_url_fopen and allow_url_include
- allow_url_fopen - enables the URL-aware fopen wrappers that enable accessing URL object like files
- allow_url_include — allows allows the use of URL-aware fopen wrappers such as: include, include_once, require, require_once – Note that for this setting to be on allow_url_fopen must also be on.
Command injection is the ability of an attacker to issue operating system commands via the PHP functions system(), exec(), passthru(), and the backtick operator `. Care must be used when writing web application code with these commands. If they must be used, be sure to escape their arguments via the escapeshellarg() or escapeshellcmd() commands. Basically client input should be avoided when constructing the commands or the arguments to these commands.