The Dangers of PHP's $$

A PHP question I particularly like to ask candidates at a job interview is to explain a bit of code that includes the $$ syntax for variable variables. It’s great if the candidate is already familiar with this feature of PHP; but what’s more important to me is that once the candidate understands how this syntax works that they can describe potential issues with using it.

A good example question would be something like this: what does the following code output?

$field = 'email';
$email = 'alice@example.com';
$$field = 'bob@example.com';
echo $field;
echo $email;

The correct answer is email and then bob@example.com. The variable $$field is not a typo (a few interviewees have asked if that will throw an error), it’s PHP’s variable variable notation. Possibly an easier way of understanding that third line of code is by rewriting it as:-

${$field} = 'bob@example.com';

When using the double string syntax ($$) we’re dynamically setting the variable name. In our example code we’re defining the variable name as the value of $field, so 'bob@example.com' is being assigned to the $email variable.

This ability to dynamically set a variable’s name in PHP can help us write very flexible code, but the important thing to remember when doing this is that it can also lead to insecurities.

The Risks of $$

Just think about it. What if the value of $field was coming from some external source, for example some user input.

$field = $_GET['field'];
$$field = 'chris@example.com';

$_GET['field'] could contain anything and yet we are blindly using it here as a variable. This could potentially be used to override any variable that preceeds this in the code.

Does this mean we shouldn’t use variable variables? No, but we do need to use them with caution. It’s therefore a good idea for us to whitelist the variables we’re going to allow assignment to when using $$. For example, let’s say we want to assign some post request data to a set of variables.

$fields = [
    'name',
    'email',
    'phone',
    'company',
];
foreach ($fields as $field) {
    $$field = $_POST[$field] ?? null;
}

Here we define a whitelist of fields that we will allow allocation to; then iterate through them using the variable variable approach to assign the post data. As a result we are in control of what variable names can be assigned to within the code.

Readability

Another important thing to remember when using variable variables is maintaining the readability of our code. If we look back at our original example we had the following line.

$$field = 'bob@example.com';

In isolation it is not clear what variable is actually getting set here. Therefore, it is a good idea to ensure that the definition of what the variable part of the variable is (in other words $field in this example) is kept as close as possible to its use case. If $field was set many lines above the line of code using it as a variable variable it would potentially be confusing to read; it also risks the value of $field being unintentionally modified in between it being defined and used for this purpose when someone comes along and updates the code at some later date.

A Real World Example

Let’s look at a real world example of PHP’s variable variables being dangerously used. The following is a modified version of a sample of code that I have previously encountered.

$params = explode('&', $_SERVER['QUERY_STRING']);
foreach ($params as $param) {
    list($key, $value) = explode('=', $param);
    $$key = $value;
}

This bit of code is extracting parameters from the query string then directly setting the associated variables using $$key = $value. Hopefully you can quickly spot the risks here. Any user could alter the query string for the page’s URL and add additional parameters that could potentially change variables used by the script.

To prevent the code being hacked this needs rewriting. To start with we can use parse_str to convert the query string into an array, then filter this against a whitelist of allowed fields.

parse_str($_SERVER['QUERY_STRING'], $queryParams);
$filteredParams = array_filter($queryParams, function($key) {
    return in_array($key, ['section', 'action', 'id']);
}, ARRAY_FILTER_USE_KEY);

Finally, we can use our filtered array, $filteredParams, to assign to variable variables.

foreach ($filteredParams as $key => $value) {
    $$key = $value;
}

With this modification a hacker can no longer attempt to set variables in our script that we don’t want to be modified from the query string. This is much better.

Final Thoughts

Whether you ever intend on using $$ or not it is good to be aware of what it does and how it can potentially lead to insecurities in your code. As I mentioned at the beginning $$ is not a syntax error, but it can easily added to code as a typo, so knowing how it functions is valuable for spotting potential mistakes.

Always remember to responsibly use PHP’s variable variables; never blindly use them without maintaining some form of control over which variables can be set

Related Content

Published on