Etienne Kneuss

Insane

I'm currently working on a scala compiler plugin that performs interprocedural, compositional effect and pointer analysis on arbitrary scala programs!

Feel free to check out Insane on github!

Phantm

In short, Phantm is a type checker for PHP applications, but it does also much more!

PHP

I sporadically contributes to the core of PHP for quite a while now, see my patch repository.

Introduction

This little guide will cover some of the basic mistakes often seen on IRC. It should enlight people about some basic guidelines required to provide a clean and portable code.

G1: Quote your array indexes

Beginners often forget to quote their indexes when filling an array. Like any other string, you need to quote them. Even if PHP is kind enough to guess what your mistake was, it won't always result in what you expected.

<?php
$array[key1] = 'foo'; // BAD example
$array['key1'] = 'foo'; // Good example
?>

Of course, the first example is perfectly correct if key1 is a defined constant.

G2: Don't quote single variables

I always wanted to know why the beginners use to quote a single variable. Anyway, there is often no point in quoting single variables. You should in fact avoid doing so.

<?php
$name = 'John':
echo "$name"; // The quotes are useless
?>

G3: Always use long open tags

For portability reasons, you should always use long open tags () in your script. Other tags can be removed by setting the configuration directive short_open_tag to 0. So, to have your script as portable as possible, you should take the habit to use to enclose your PHP code.

G4: Do not rely on register_globals

register_globals is a magic feature that will inject your script with variables. For example, if the user request your php page using http://yoursite/page.php?age=18 the following code will work if register_globals is enabled:

<?php
echo 'Hi, you must be '.intval($age).' years old';
?>

However, this setting could expose a script to security problems and, for portability reasons again, your script should not rely on it. You should instead use superglobal arrays ($_GET, $_POST, $_REQUEST, $_COOKIE) to access such content:

G5: Do not rely on magic_quotes_gpc

The magic_quotes_gpc configuration directive was introduced as a security protection against SQL injection. This setting is thus most likely on. However, your script could be used on a server that doesn't have it enabled, and would thus be exposed to major security problems. First, you need to reverse the effects on such settings, here is a nice way doing it:

<?php
function clean (&$item, $key) {
    $item = stripslashes($item);
}
if (get_magic_quotes_gpc()) {
    array_walk_recursive($_GET,     'clean');
    array_walk_recursive($_POST,    'clean');
    array_walk_recursive($_REQUEST, 'clean');
    array_walk_recursive($_COOKIE,  'clean');
}
?>

Note that this code won't affect magic_quotes_gpc's effect on keys as it's often not a problem. You should then use a proper way of escaping/filtering on ALL your tainted data used in a SQL query. Here is some propositions: Use the function intval() on variables that are meant to be integers (id, age, year, ...) Use the correct escaping function on text values, for example mysql_real_escape_string() MySQL based queries. This should save you of lot of troubles.

G6: Enable error reporting

One of the most important thing is to completely enable error_reporting in the development environment by setting error_reporting to E_ALL (including E_STRICT would be even better). You will then be able to detect your mistakes, like an unquoted array index, an undefined variable, etc... so you can fix them and not just hide them!

G7: Indent your code properly

Code readability is quite important, especially if you need to track a bug. The first step to have a readable code is to indent your code properly. A standard is to use 4 spaces instead of a tabulation.

<?php
// Unreadable code
if ($var == true) {
if ($var2 == false) {
switch($var3) {
        case 3:
echo 3;
    break;
default:
    echo 5;
}
echo 'foo';
    }
    echo 'bar';
        }
?>

The readability of this code can be amazingly improved by indenting your code correctly. You'll also decrease the odds to come across a parse error caused by a bracket mismatch:

<?php
// This is much better
if ($var == true) {
    if ($var2 == false) {
        switch($var3) {
            case 3:
                echo 3;
                break;
            default:
                echo 5;
        }
        echo 'foo';
    }
    echo 'bar';
}
?>

Introduction

When building a website, security is an important point which is, sadly, often forgotten. In this article, I'll talk about one security hole called XSS with magic_quotes_gpc. It consists, in website context, of injecting arbitrary html code into a trusted website. The attack is normally targeted towards the client-side. The exploit is used to steal client-side informations like cookies. PHP has tryed to protect novices users against this kind of injection holes by introducing a "magic" feature : magic_quotes_gpc. This feature will automatically escape single and double quotes in untrusted inherited data: GET, POST and COOKIE variables. Escaping quotes is a way to prevent injections, because the attaker can't leave a quoted string:

// get untrusted data (magic_quotes_gpc is on)

$variable = $_GET['variable'];

// use it in an output

echo '<img src="', $variable, '" />';

// if the quotes contained in $variable are escaped, it
//   makes it impossible to leave the src=".." . The
//   result would look like:
// <img src="foo\"bar" />

But, magic_quotes_gpc is not enough, there is still a way to steal passwords is many situations using a trick I've though about.

Idea

Let's take an example : an image gallery community.

  • The script that displays each images get the image url by GET variables.
  • No checks are made to test the validity of the url.
  • magic_quotes_gpc is on.

The code would be similar to the first example:

// get untrusted data (magic_quotes_gpc is on)
$image_url = $_GET['image_url'];

// display the image
echo 'The image you requested : <br />';
echo '<img src="', $image_url, '" alt="some nice image" width="400" height="400" />';

In this case, there is no way to inject an efficient javascript that could request and send the cookie, so we will use a trick.

Let's use the url of a php image requesting http authentication : it will pop up an authentication box requesting a re-login for example. The basic inexperienced user would fill it with his community login and try it. The PHP image will recieve the information and just have to store it.

Note that this kind of trick will work on every injections that will modify client requests to get the complete page: images, frames, css file, ...

Conclusion

As you can see, magic_quotes_gpc does not protect your website against malicious injections that can lead to login theft. There is no "magic" solution when talking security and PHP provides a lot of functions that make your tests easier so use them!

Catching mysql errors

When you work with mysql_* functions, they may return any kind of errors. Those errors won't be printed directly. To display them, you'll have to use the mysql_error() function.

The most common example of its use is :

// ...
$resource = mysql_query('SELECT ....') OR die(mysql_error());
// ...

Explanation

This is a boolean expression which can be written

($resource = mysql_query('SELECT ....')) OR (die(mysql_error()));

You've to know that an assignation will return the value assigned, so in this case ($resource = mysql_query('SELECT ....')) will return the value returned by mysql_query('SELECT ....').

After a quick look into the manual page about mysql_query(), you can see that there are only three things mysql_query can return:

  • when everything works: a resource or true
  • when an error occurs: false

Notice that a ressource even "empty" is evaluated as true in a boolean expression.

We will focus on the case when mysql_query() returns false (error occured) because we want to catch the errors.

An important thing to know is that in boolean expressions, the part after an "OR" won't be executed if the first part is evaluated as true.

false OR print 'hello world'; // 'hello world' is outputted

true OR print 'hello world'; // won't output anything

Now, you can see that the die(mysql_error()) printing the error will be executed only if the first part, the value returned by mysql_query(...), is evaluated as false which means an error occured.

That was an explanation for the short syntax, but notice that this syntax produces the same result and could be easier to understand:

// ...
$resource = mysql_query('SELECT ...');
if ($resource === false) {
    die(mysql_error());
}

ps: the "OR" boolean operator should be replaced by "||" and the "AND" by "&&" for a matter of precedence. I used the textual version to make it more clear.

while ($row = mysql_fetch_...)

First of all, a resource is NOT an array, so using functions like foreach() or count() on it is simply useless.

A resource is something that contains the selected data. The only way to look into it is using the mysql_* functions.

The most common example of fetching multiple rows from a resource is :

// ...
$resource = mysql_query('SELECT...') or die(mysql_error());

while ($row = mysql_fetch_assoc($resource)) {
    // ...
}
// ...

This code is often used but not always understood by novices.

Explanation

You've to know that a ressource contains a data pointer. You can make the analogy:

  • ressource -> file with multiple lines (rows)
  • data pointer -> number of the line to read

This pointer will be moved by the mysql_fetch_* functions after getting the row.

$row1 = mysql_fetch_assoc($resource); // returns one row
$row2 = mysql_fetch_assoc($resource); // returns the next row

At the end of the ressource, when there is no more row after the pointer, the mysql_fetch_* functions will return false.

We have seen that an assignation returns the value assigned, and its known that the while loop will be stopped when the condition is evaluated false, so the while loop will stop at the end of the resource.

Introduction

When on, register_globals will inject your scripts with all sorts of variables, like variables coming from GET or POST methods, from sessions and cookies. This setting can make scripts insecure, using the fact that php doesn't require initialization of variables. There is an example here http://www.php.net/register_globals of that kind of leak. This tiny text will explain another way to get rid of a login security using leaks created by register_global = ON too, but from another point of view.

Theorical considerations

There are many arrays that are initialized in a bad way, without "array();" and just by assigning an index like this :

$sample['key1'] = 'value1';
$sample['key2'] = 'value2';

You probably already know that you can access to one char of a string using { } or [ ]

For more informations: (http://www.php.net/manual/language.types.string.php#language.types.string.substr)[PHP manual page about strings]

Since php4, using [ ] instead of { } to access any char is deprecated, but still available for compatibility reasons.

So:

$string = "abcdefgh";

echo $string{2}; // 'c'
echo $string{4}; // 'e'
echo $string{0};

Behaves the same as :

$string = "abcdefgh";

echo $string[2]; // 'c'
echo $string[4]; // 'e'
echo $string[0];

Now what happens if you use non-numerical index? PHP evaluate the index as 0:

$string = "abcdefgh";

echo $string['foo']; // 'a'
echo $string[0]; // 'a'

You must also notice that if you modify a char with more than one char: only the first char will be used.

$string = 'abcdefgh';

$string[0] = 'yzzzzz'; // 'ybcdefgh'
$string[0] = 'y'; // 'ybcdefgh'

The leak

Imagine that there is a script that use :

$admin['user'] = 'foo';
$admin['pass'] = 'bar';

if($admin['user'] == $_GET['username'] AND $admin['pass'] == $_GET['password']){
    /* Give r00t */
}

It could seem quite secure, but in fact, it isn't. The security leak is caused by the bad array initialization mixed with register_gobals.

Here is an example

What happens if you try to request the page with : page.php?admin=asdf

$admin // is set to 'asdf'
$admin['user'] ='foo'; // sets the first char of 'asdf' to 'f'
$admin['pass'] ='bar'; // sets the first char of 'fsdf' to 'b'
$admin['user'] == $_GET['username'] tests if 'b' == $_GET['username']
$admin['pass'] == $_GET['password'] tests if 'b' == $_GET['password']

So, to have access : simply use : page.php?admin=asdf&username=b&password=b

It means you only have to know the first character of the pass, and if you don't : try, there are not many possibilities.

Conclusion

As you can see, the security leaks caused by register_globals are not always obvious. A good habbit to take is to disable the register_globals feature, especially if you don't use it. Its also important to be sure the variables you're working with are defined the proper way.

New website available

Tue, 10 May 2005

Here we go ! I'm happy to present you my new website. You probably feel a bit disappointed and I can understand that: the new design is completely different. I'll probably take the time to create a skin looking like the old website.

This new website is still under construction but the main work is done. I've made this new design thinking about two problem that occurred in the last one : shortage of place and the excessive weight of the images. As you can see, this design uses few images and its width is not fixed.

About the main system used behind: I changed the way I linked the pages, I used output buffering to have more control on the output but also to have control on the meta information that became dynamic. I also use a skin system using templates and CSS. Notice that I plan a "My settings" section where you'll be able to configure your own view of this website.

A repository system is also implemented to handle my php scripts, publications, school files and images. I'll fill it when I find the time.

I hope you'll enjoy.