I spend a good deal of time reading about PHP security over at the PHP Developer’s Network forums. In one of the many discussions I have had over there, I recall one in particular that really opened my eyes to how easy it can be to overlook a very serious security issue. As you may know, there is a predefined array of server-related variables in PHP, aptly named $_SERVER. For years I used a certain element “PHP_SELF” within this array in instances where I needed to output what page I was currently working within. One of the most common of such instances is when you need to tell a form to post back to itself (a very common practice in PHP).
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>"> <!-- form contents --> </form>
How could this possibly be a security issue? I mean this is an untainted value set by PHP itself, right?
Wrong!
Unfortunately, this is not the case at all. The PHP_SELF element can in fact be altered by the user to include any kind of malicious XSS code he/she desires. They will be limited only by their imaginations, and the amount of data that is actually allowed though the url (I believe this depends upon the browser). Don’t feel too down on yourself if you weren’t aware. Using this element in an irresponsible manner has been a staple in online PHP tutorials for as long as I can remember.
Where’s the beef?
So where is this supposed user-polluted input? If you take a moment to examine the entire $_SERVER array (hint: phpinfo()), you’ll notice another element called PATH_INFO. See, it is perfectly valid to tack on extra information after the page name in the URL. For example, the following is a valid URL in PHP.
http://www.example.com/index.php/news/category/javascript
In this case, PATH_INFO would be populated with the data appended to the end of the URL. PHP_SELF will also contain this data.. If you were to examine your $_SERVER array given this URL, you’d see something like the following:
[PATH_INFO] => /news/category/javascript
[PHP_SELF] => /php/phpselftest.php/news/category/javascript
Now let’s revisit our form example from earlier. Using $_SERVER['PHP_SELF'] directly within the form action, you can see how this may present an XSS vulnerability. I realize how paranoid I have sounded about XSS in the last several blog posts, but I assure you it’s for good reason. If you are unfamiliar with XSS, I encourage you to read up on it. Wikipedia’s article on the subject is a good starting point.
Let’s say the user enters http://www.example.com/form.php/%22%3E%3Cscript%3Ealert(‘xss attack’)%3C/script%3E%3Cbr%20class=%22irrelevant
Now let’s take a look at what effect this has on our form output:
<form method="post" action="http://www.example.com/form.php/"><script>alert('xss attack')</script><br class="irrelevant"> <!-- form contents --> </form>
This is not good. While I can assure you that the simple javascript I injected is harmless, I can’t guarantee the same for your users. It would be a somewhat trivial task for an attacker to write a javascript capable of stealing sensitive data and tricking an unsuspected user into executing it by way of a link such as the one above.
Don’t worry, there are many solutions
Among the many solutions, I prefer the simplest. In many cases, you can get away with just using a pound sign instead of explicitly naming the page.
<form method="post" action="#"> <!-- form contents --> </form>
However, should you require the use of the <base> HTML element, this solution will generally fail miserably. The browser will no longer assume it should post back to the current page, and instead post to whatever you have assigned to your base tag’s href attribute.
In this case, you can either sanitize the value of PHP_SELF, or you can get the basename() of either the magic constant, __FILE__ (available for php v4.0.2 or later) or the basename() of $_SERVER['SCRIPT_FILENAME'].
Tormn Says:
July 22nd, 2008 at 4:53 am
As a hacker, I also would like to tell you how important XSS attacks are. If XSS is available on a website, you will most likely get access to other peoples accounts. There are ways you can stop this from happening, but most people do not stop XSS.
Matthew Byrne: the blog » Form breaks Wordpress Says:
January 26th, 2009 at 11:26 am
[...] scrabbling around looking for answers I did come across a good article about security vulnerability when using the PHP_Self command, it’s well worth a read. Tags: form, php, security, Wordpress [...]
Kristof Says:
February 24th, 2009 at 11:41 am
Turn MultiViews off and your problem is solved.
Radman Says:
March 1st, 2009 at 2:07 pm
Every time i come here I am not dissapointed, nice post
Daniel Hahler Says:
May 29th, 2009 at 7:39 am
Good post. You can just use "" (empty string) for the form action though (instead of "#").
Or just use htmlspecialchars() to output PHP_SELF (but leaving it away makes more sense anyway).
Frank Says:
May 29th, 2009 at 2:21 pm
Stumbled onto this from reddit, thanks that is an awesome heads up on a fairly common practice!
kapil Says:
June 16th, 2009 at 2:19 am
I used action="" and it solved what I needed. Is this ok to do or does this also has any security issue?
Jarrett M. Barnett Says:
July 7th, 2009 at 11:59 am
This post is actually fairly old. From my understanding leaving the action blank (action=”") is both not proper and still open to XSS attacks.
If your using PHP5 (and maybe later versions of PHP4), you should look into htmlspecialchars() – http://us2.php.net/manual/en/function.htmlspecialchars.php
I personally purify everything.
halı yıkama Says:
July 29th, 2009 at 4:46 am
$_SERVER['PHP_SELF'] can not be trusted, but there are safe alternatives – The Q great article thank you.