Slides from my Lesser Known Security Problems in PHP Applications Talk at ZendCon

September 18th, 2008 | by Stefan Esser |

Here are the slides of my ZendCon talk about Lesser Known Security Problems in PHP Applications.

(PDF) Lesser Known Security Problems in PHP Applications

  1. 21 Responses to “Slides from my Lesser Known Security Problems in PHP Applications Talk at ZendCon”

  2. By Jussi on Sep 18, 2008 | Reply

    Great summary, thx for sharing the slides.

  3. By Jard on Sep 19, 2008 | Reply

    The MySQL packet maximum option is max_allowed_packet and not max_packet_size is it not, or am I going mad?

  4. By Stefan Esser on Sep 19, 2008 | Reply

    @Jard: yes you are right, I was copying the wrong spelling from one set of slides to the next. max_allowed_packet is actually the correct setting. max_packet_size is some name internally used in the mysql source code.

  5. By Hodicska Gergely on Sep 19, 2008 | Reply

    Thank you Stefan for the slides, it was helpful even if I cant take part of the conference. I like that you always step over the common xss/csrf/sql injection topic.

    May I have some question?

    ZipArchive vulnerability: can I defense against by listing first the file names and check if there is some suspicious strings in them?

    “mysql_real_escape_string() not safe when SET NAMES is used”: I tried to google on this, but I didn’t find any info, can you clarify this with a few words, or point to a link where this is discussed?

    “REQUEST_URI encoding depends on client”: I experienced that PHP does not automatically urldecode REQUEST_URI. But it does other URL like entries in the $_SERVER array. Is this a planed behavior?


  6. By Max on Sep 19, 2008 | Reply

    Thanks for the collection.

  7. By Jakub Vrána on Sep 19, 2008 | Reply

    Thanks for sharing the slides, there’s a lot of useful information.

    I’ve added following warning to the PHP manual: “Usage of SERVER and ENV variables is checked during the compile time so using them through e.g. variable variables will not cause their initialization.”

    mysql_real_escape_string() is not safe when SET NAMES is used in the case that database uses different character set than connection? So SET CHARACTER SET is safe, right?

  8. By Stefan Esser on Sep 19, 2008 | Reply


    SET NAMES is usually used to switch the encoding from what is default to what the application needs.
    This is done in a way that mysql_real_escape_string doesn’t know about this. This means if you switch to some multi byte encoding that allows backslash as 2nd 3rd 4th… byte you run into trouble, because mysql_real_escape_string doesn’t escape correctly. UTF-8 is safe…

    Safe way to change encoding is mysql_set_charset, but that is only available in new PHP versions


    As far as I know the urldecoding is not done by PHP but already by the webserver. PHP just forwards what APACHE/CGI/… tells it. (Caution I haven’t checked the code, there also might be some decoding in PHP) Best practice is to not assume anything ;)


    I haven’t looked much into protecting against the ZIP Archive thing, because I consider it something that must be fixed within PHP. There is obviously a patch it just isn’t commited yet.
    The problem with ZIP files is that every filename is 2 times in there. So I am not 100% sure if listen the files will traverse one table and unpacking will traverse the other table. If that is the case you can obviously bypass your protection by manipulating only one of the filenames.

  9. By dedlfix on Sep 20, 2008 | Reply

    SET CHARACTER SET vs. SET NAMES vs. mysql_set_charset()

    Both SET CHARACTER SET and SET NAMES only change some session settings on the server side. As Stefan said they do not affect the client API settings, neither SET CHARACTER SET nor SET NAMES. So mysql_set_charset() is always the best way, but: You won’t have any trouble using SET CHARACTER SET or SET NAMES instead of mysql_set_charset() if both the default connection encoding and the encoding you set are either any of the ISO-8859 family or UTF-8 or mixed. The characters affected by mysql_real_escape_string() have the same byte values in these encodings and they are unambiguous.


    If you don’t know the difference between SET CHARACTER SET and SET NAMES you always want to use SET NAMES (if you can’t use mysql_set_charset()).

    The database setting (as in CREATE DATABASE foo CHARACTER SET bar) is just a default value for new tables without an explicit character set value. (And a table’s character set value is only a default value for new fields. Both database’s and table’s values or changing of the values do not affect existing field’s data.) If your database encoding value is latin1 and your field’s encodings are utf8 and you use SET CHARACTER SET utf8 instead of SET NAMES utf8 you will lose your non-latin1-characters while manipulating data.

    An SQL statement encoded in character_set_client is translated to character_set_connection after receiving. SET CHARACTER SET will set character_set_connection to the database’s value, not the value you said. SET NAMES sets both character_set_client and character_set_connection to the same value. (The third value character_set_results doesn’t matter in this case, it’s for the result’s encoding.)

  10. By Hodicska Gergely on Sep 20, 2008 | Reply

    @SET NAMES: oh I see, thx.
    @ZIP_ARCHIVE: ok, I will try to test this one, I am just not sure if it is easy to create a corrupted archive.

    @REQUEST_URI: my experience (I didn’t look into the PHP’s source code this part) is, that I have to urldecode in my routing class which is based on REQUEST_URI:
    “REDIRECT_QUERY_STRING”=>string(15) “param=bar%5B%5D”
    “REDIRECT_URL”=>string(6) “/foo[]”
    “QUERY_STRING”=>string(15) “param=bar%5B%5D”
    “REQUEST_URI”=>string(26) “/foo%5B%5D?param=bar%5B%5D”
    “argv”=>array(1) {[0]=>string(15) “param=bar%5B%5D”}

  11. By anonymous on Sep 23, 2008 | Reply

    as always, great work Stefan

  12. By kuza55 on Sep 23, 2008 | Reply

    I actually looked at some PHP code recently and saw some slightly suspect code blocks that looked vulnerable via cross-application attacks:

    // Include config.php file (contain all main variables)
    include (”../config.php”);
    // Include language
    include (”../languages/$g_lang”);


    if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
    die(”This file cannot be executed directly”);

    Which would both be exploitable if there was another file on the server which included .php files, but you couldn’t exploit as a traditional LFI due to some protection such as magic_quotes which escapes null bytes.

  13. By kuza55 on Sep 23, 2008 | Reply

    argh, last comment got mangled (angle brackets getting stripped) the second snippet was meant to be:

    if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
    die(”This file cannot be executed directly”);

    … vulnerable code here …

  14. By Casper on Sep 26, 2008 | Reply


    on page 5 in the pdf you mention something about the is_numeric() function. Could you explain whats bad with that?

    Thanks in advance.



  15. By Stefan Esser on Sep 27, 2008 | Reply


    The stuff on page 5 has nothing todo with is_numeric(). The problem here is that first REQUEST is validated and then it is rebuild.

    It is however rebuild in a different order than PHP would build it in default configuration.

    Therefore the is_numeric check can be bypassed. By storing a valid number in f.e. the cookie and sending the attack through POST.

  16. By hoyajigi on Sep 29, 2008 | Reply

    Wow. Thanx

    I heard your lecture at PHPFEST2008 in Korea.

  17. By Lukas on Nov 9, 2008 | Reply

    Hmm interesting thing about SET NAMES, I thought the sole reason why they added the connection as a parameter to mysql_real_escape_string() was to be able to handle this …

  18. By Mark on Nov 12, 2008 | Reply

    Very interesting and useful! Do you know if anyone video-ed the presentation?

  19. By Bong Bong on Dec 7, 2008 | Reply

    Thanks for the interesting read.
    Will suhosin’s session data encryption prevent the spoofed session data in /tmp attack?

  1. 3 Trackback(s)

  2. Nov 9, 2008: <?paul
  3. Jan 17, 2010: mysql_real_escape_string and SET NAMES « hakre on wordpress
  4. Mar 24, 2010: UTF-8, MySQL, kódování a PHP funkce pro práci s řetězci: strlen, substr aj. | Zdeněk Večeřa

Post a Comment