register_globals und Sicherheit

Ende 2001 brachte die PHP-Version 4.1 ein verbessertes Übergabeverfahren für Benutzerdaten aus Formularen bzw. Cookies, seit PHP 4.2 ist register_globals defaultmässig auf 'off' gesetzt. Die unterschiedlichen Konfígurationen sorgten in den Folgejahren für große Verwirrung, manche Applikationen wurden einfach nur mit register_globals-Emulationen versehen, um sicherzustellen, dass sie in verschiedenen Umgebungen lauffähig sind. Leider setzen auch nach mehr als vier Jahren noch einige Provider register_globals auf 'on' - und leider gibt es nachwievor auch register_globals-Emulationen in Applikationen.

Doch es lohnt sich umzusteigen, denn man kann sich so eine Menge Ärger sparen und potentielle Sicherheitslücken schließen.

Historie

Die Geschichte von register_globals ist recht wechselhaft. Gerade Rasmus Lerdorf, quasi der Erfinder von PHP, wandelte sich über die Jahre vom Globalisierungsfreund zum Globalisierungsgegner.

Während er in einem  Interview im Jahre 2002 noch ausführte:

I was personally not in favour of turning Register Globals off by default. It adds very little to the overall security of an application. If people do not check data coming from the user then with or without Register Globals enabled that application is going to be insecure.

änderte er im Laufe der Zeit scheinbar seine Meinung - in der Wishlist für PHP 6 aus dem Jahre 2005 wird nunmehr ausgeführt:

[...]1. Remove register_globals completely[...]

Auf php.internals werden auch die Gründe für den Meinungswandel ausgeführt:

[...]there isn't much point in deprecating things if we are never going to
remove anything[...]

[…]I don't think pushing register_globals and magic_quotes_gpc down to the application level making them the exception rather than the rule is much of a break.[…]

[…] The security problems often occur in the non-register_globals apps on such a server because the authors never even considered that they would be on. […]

Abhilfe

Falls der Provider systemweit register_globals=on gesetzt hat gibt es - je nach Server-Konfiguration - unter Umständen diverse Möglichkeiten, dies anzupassen. Oftmals bringt schon folgender Eintrag in einer .htaccess im root der Installation Abhilfe:

# set register_globals=off
php_flag register_globals off

alternativ kann auch eine php.ini im root der Installation helfen:

php_admin_flag register_globals Off

Ein Sonderfall scheint der Provider 1&1/Puretec zu sein - hier kann man (aus welchem Grund auch immer) unter PHP4 die register_globals nicht deaktivieren. Eine Umstellung auf PHP5 via .htaccess sollte dies Problem allerdings lösen:

AddType x-mapp-php5 .php

Auch beim Provider Strato ist es den Kunden nicht ohne weiteres vergönnt, eine sichere Konfiguration zu nutzen - hier hilft dem Vernehmen nach nur eine individuelle php.ini bzw. eine Umstellung auf PHP4.4(!) via .htaccess:

AddType application/x-httpd-php43 .php

Sollte das alles nicht zum Erfolg führen (Kontrolle jeweils über eine phpinfo.php): Provider kontaktieren!

Zusätzliche Möglichkeiten:

Darüberhinaus wurde für Entwickler in der Vergangenheit auch die Möglichkeit augezeigt, register_globals=off zu emulieren:

Zitatif (ini_get('register_globals')){
foreach($GLOBALS as $s_variable_name => $m_variable_value){
if (!in_array($s_variable_name, array('GLOBALS', 'argv',
'argc', '_FILES', '_COOKIE', '_POST', '_GET', '_SERVER',
'_ENV', '_SESSION', '_REQUEST',
's_variable_name', 'm_variable_value')
)){
unset($GLOBALS[$s_variable_name]);
}
}
unset($GLOBALS['s_variable_name']);
unset($GLOBALS['m_variable_value']);
}

siehe Blog von Andrei Necolau.

Zitat<?php
// Emulate register_globals off
function unregister_GLOBALS()
{
    if (!ini_get('register_globals')) {
        return;
    }

    // Might want to change this perhaps to a nicer error
    if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
        die('GLOBALS overwrite attempt detected');
    }

    // Variables that shouldn't be unset
    $noUnset = array('GLOBALS',  '_GET',
                     '_POST',    '_COOKIE',
                     '_REQUEST', '_SERVER',
                     '_ENV',     '_FILES');

    $input = array_merge($_GET,    $_POST,
                         $_COOKIE, $_SERVER,
                         $_ENV,    $_FILES,
                         isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());

    foreach ($input as $k => $v) {
        if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) {
            unset($GLOBALS[$k]);
        }
    }
}

unregister_GLOBALS();

?>

siehe PHP-FAQ.

Dies ist bei PHP als CGI-Extension oftmals die einzige - zugegebenermassen recht unelegante - Möglichkeit, die Direktive individuell zu setzen.


Netzwerke

Blogroll