Detecting use of register globals Detecting use of register globals php php

Detecting use of register globals


A small script I just hacked together to detect simple undefined variables. You'll need PHP-Parser for this:

<?phperror_reporting(E_ALL);$dir = './foo';require_once './lib/bootstrap.php';class Scope {    protected $stack;    protected $pos;    public function __construct() {        $this->stack = array();        $this->pos = -1;    }    public function addVar($name) {        $this->stack[$this->pos][$name] = true;    }    public function hasVar($name) {        return isset($this->stack[$this->pos][$name]);    }    public function pushScope() {        $this->stack[++$this->pos] = array();    }    public function popScope() {        --$this->pos;    }}class UndefinedVariableVisitor extends PHPParser_NodeVisitorAbstract {    protected $scope;    protected $parser;    protected $traverser;    public function __construct(Scope $scope, PHPParser_Parser $parser, PHPParser_NodeTraverser $traverser) {        $this->scope = $scope;        $this->parser = $parser;        $this->traverser = $traverser;    }    public function enterNode(PHPParser_Node $node) {        if (($node instanceof PHPParser_Node_Expr_Assign || $node instanceof PHPParser_Node_Expr_AssignRef)            && $node->var instanceof PHPParser_Node_Expr_Variable            && is_string($node->var->name)        ) {            $this->scope->addVar($node->var->name);        } elseif ($node instanceof PHPParser_Node_Stmt_Global || $node instanceof PHPParser_Node_Stmt_Static) {            foreach ($node->vars as $var) {                if (is_string($var->name)) {                    $this->scope->addVar($var->name);                }            }        } elseif ($node instanceof PHPParser_Node_Expr_Variable && is_string($node->name)) {            if (!$this->scope->hasVar($node->name)) {                echo 'Undefined variable $' . $node->name . ' on line ' . $node->getLine() . "\n";            }        } elseif ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) {            $this->scope->pushScope();            // params are always available            foreach ($node->params as $param) {                $this->scope->addVar($param->name);            }            // methods always have $this            if ($node instanceof PHPParser_Node_Stmt_ClassMethod) {                $this->scope->addVar('this');            }        } elseif ($node instanceof PHPParser_Node_Expr_Include && $node->expr instanceof PHPParser_Node_Scalar_String) {            $file = $node->expr->value;            $code = file_get_contents($file);            $stmts = $this->parser->parse($code);            // for includes within the file            $cwd = getcwd();            chdir(dirname($file));            $this->traverser->traverse($stmts);            chdir($cwd);        }    }    public function leaveNode(PHPParser_Node $node) {        if ($node instanceof PHPParser_Node_Stmt_Function || $node instanceof PHPParser_Node_Stmt_ClassMethod) {            $this->scope->popScope();        }    }}$parser = new PHPParser_Parser(new PHPParser_Lexer());$scope = new Scope;$traverser = new PHPParser_NodeTraverser;$traverser->addVisitor(new UndefinedVariableVisitor($scope, $parser, $traverser));foreach (new RecursiveIteratorIterator(             new RecursiveDirectoryIterator($dir),             RecursiveIteratorIterator::LEAVES_ONLY)         as $file) {    if (!preg_match('/\.php$/', $file)) continue;    echo 'Checking ' . $file . ':', "\n";    $code = file_get_contents($file);    $stmts = $parser->parse($code);    // for includes within the file    $cwd = getcwd();    chdir(dirname($file));    $scope->pushScope();    $traverser->traverse($stmts);    $scope->popScope();    chdir($cwd);    echo "\n";}

It's just a very basic implementation and I did not test it extensively, but it should work for scripts that don't go wild with $GLOBALS and $$varVars. It does basic include resolution.


This should work (from one of the php manual comments):

if (ini_get('register_globals')) {    foreach ($GLOBALS as $int_temp_name => $int_temp_value) {        if (!in_array($int_temp_name, array (                'GLOBALS',                '_FILES',                '_REQUEST',                '_COOKIE',                '_SERVER',                '_ENV',                '_SESSION',                ini_get('session.name'),                'int_temp_name',                'int_temp_value'            ))) {            unset ($GLOBALS[$int_temp_name]);        }    }}


Tweaked the code above with the latest parser: used it to make 5.4 register_globals to 7.2

    <?php/* * pgParser.php is a "Abstract syntax tree" progam based on the Nikic PHP-Parser  https://github.com/nikic/PHP-Parser * Tweaked for use to lose register global variables. */use PhpParser\Node;use PhpParser\NodeTraverser;use PhpParser\NodeVisitorAbstract;use PhpParser\Parser;error_reporting(E_ALL);/* * $dir contains a base dir to use when no PARAM directory eq "php ./pgParser.php /home/hrobben/php/ > results.txt" */$dir = '/home/hrobben/php/';/* * $nonecheck : variables not to check.... * e.g ['GLOBALS','_COOKIE', 'properties'] -> $GLOBALS, $_COOKIE, $properties >>>>> will not been checked. */$nonecheck = ['_COOKIE', '_SERVER', '_SESSION'];/* * if those names part or directory not include... * this can be usefull when certain users (developers) have own directories * empty = [] no directories will be excluded * e.g. ['george', 'henry']  all directories /tree/git/base/src/henry/  and /template/george/blog/  will be excluded. */$nonesubdir = ['henry', 'ckeditor'];/* * $excludeMatch can be filled with text in directory plus file to exclude. * e.q.  _MA. will exclude the file index_MA.php3 to scan. * e.g.  _MA will exclude the file /git/src/test_MAIN/index.php3 * be carefull. * Not for include files, only base files. */$excludeMatch = ['_HR.', '_ed.'];/* * you can give a base directory to begin scanning. */if (count($argv) > 0) {    $dir = $argv[1];}require_once './vendor/autoload.php';class Scope{    protected $stack;    protected $pos;    public function __construct()    {        $this->stack = array();        $this->pos = -1;    }    public function addVar($name)    {        $this->stack[$this->pos][$name] = true;    }    public function hasVar($name): bool    {        return isset($this->stack[$this->pos][$name]);    }    public function pushScope()    {        $this->stack[++$this->pos] = array();    }    public function popScope()    {        --$this->pos;    }    public function getPos()    {        return $this->pos;    }}class UndefinedVariableVisitor extends NodeVisitorAbstract{    protected $scope;    protected $parser;    protected $traverser;    protected $nonecheck;    protected $isInclude = false;    public function __construct(Scope $scope, Parser $parser, NodeTraverser $traverser, array $nonecheck)    {        $this->scope = $scope;        $this->parser = $parser;        $this->traverser = $traverser;        $this->nonecheck = $nonecheck;    }    public function enterNode(Node $node)    {        $includePath = ['/home/hrobben/php/include/class/'];        $nonesubdir = ['jp', 'jp117']; // for the include exclusion directories.        if (($node instanceof PhpParser\Node\Expr\Assign || $node instanceof PhpParser\Node\Expr\AssignRef)            && $node->var instanceof PhpParser\Node\Expr\Variable            && is_string($node->var->name)        ) {            // if preg_replace uses the same var name as second argument, its not setting a var, it must also be on the first level.            if ($node->expr->name->parts[0] == 'preg_replace' && $node->expr->args[2]->value->name == $node->var->name && $this->scope->getPos() == 0) {                //echo 'found $'.$node->expr->args[2]->value->name.' level '.$this->scope->getPos()."\n";            } else {                //echo 'add var $' . $node->var->name . "\n";                $this->scope->addVar($node->var->name);            }        } elseif ($node instanceof PhpParser\Node\Stmt\Global_ || $node instanceof PhpParser\Node\Stmt\Static_) {            foreach ($node->vars as $var) {                if (is_string($var->name)) {                    $this->scope->addVar($var->name);                    //echo 'add global or static var $' . $var->name . "\n";                }            }        } elseif ($node instanceof PhpParser\Node\Stmt\Foreach_) {            $this->scope->addVar($node->valueVar->name);            if ($node->keyVar) {                $this->scope->addVar($node->keyVar->name);            }        } elseif ($node instanceof PhpParser\Node\Stmt\StaticVar) {            $this->scope->addVar($node->var->name);        } elseif ($node instanceof PhpParser\Node\Expr\FuncCall && ($node->name->parts[0] == 'preg_match')) {            foreach ($node->args as $args) {                if ($node->args[2]) {                    $this->scope->addVar($node->args[2]->value->name);                }            }        } elseif ($node instanceof PhpParser\Node\Expr\List_) {            foreach ($node->items as $item) {                $this->scope->addVar($item->value->name);            }        } elseif ($node instanceof PhpParser\Node\Expr\Closure) {            foreach ($node->params as $param) {                $this->scope->addVar($param->var->name);            }        } elseif ($node instanceof PhpParser\Node\Expr\Variable && is_string($node->name) && !$node instanceof PhpParser\Node\Const_) {            if (!$this->scope->hasVar($node->name) && !in_array($node->name, $this->nonecheck)) {                echo 'Undefined variable $' . $node->name . ' on line ' . $node->getLine() . "\n";            }        } elseif ($node instanceof PhpParser\Node\Stmt\Function_ || $node instanceof PhpParser\Node\Stmt\ClassMethod) {            $this->scope->pushScope();            // params are always available            foreach ($node->params as $param) {                //echo 'param name : '. $param->var->name. "\n";                $this->scope->addVar($param->var->name);            }            // methods always have $this            if ($node instanceof PhpParser\Node\Stmt\ClassMethod) {                $this->scope->addVar('this');            }        } elseif ($node instanceof PhpParser\Node\Expr\Include_ && $node->expr instanceof PhpParser\Node\Scalar\String_) {            $file = $node->expr->value;            $code = file_get_contents($file);            if (strlen($code) < 5) {                foreach ($includePath as $path) {                    if (strlen($code) < 5) {                        $code = file_get_contents($path . $file);                    }                }            }            $match_string = implode("\/|", $nonesubdir);            if (preg_match("/$match_string\//", $file)) {                $code = '';                echo 'file skipped: ' . $file . "\n";            }            $stmts = $this->parser->parse($code);            // for includes within the file            $cwd = getcwd();            chdir(dirname($file));            $this->isInclude = true;            $this->traverser->traverse($stmts);            $this->isInclude = false;            chdir($cwd);            if (strlen($code) < 5) {                echo 'Include <- ' . $file . ' not found or empty.' . "\n";            } else {                echo 'Include <- ' . $file . "\n";            }        }    }    public function leaveNode(Node $node)    {        if ($node instanceof PhpParser\Node\Stmt\Function_ || $node instanceof PhpParser\Node\Stmt\ClassMethod) {            $this->scope->popScope();        }    }}$lexer = new PhpParser\Lexer();$parser = (new PhpParser\ParserFactory)->create(    PhpParser\ParserFactory::PREFER_PHP7,    $lexer);$scope = new Scope;$traverser = new NodeTraverser();$traverser->addVisitor(new UndefinedVariableVisitor($scope, $parser, $traverser, $nonecheck));foreach (new RecursiveIteratorIterator(             new RecursiveDirectoryIterator($dir),             RecursiveIteratorIterator::LEAVES_ONLY)         as $file) {    if (!preg_match('/\.php$|\.html$|\.php3$/', $file)) continue;    $match_string = implode("\/|\/", $nonesubdir);    if (preg_match("/\/$match_string\//", $file)) continue;    $match_string = implode("|", $excludeMatch);    if (preg_match("/$match_string/", $file)) continue;    echo 'Checking ' . $file . ':', "\n";    $code = file_get_contents($file);    $stmts = $parser->parse($code);    // print_r($stmts);    // for includes within the file    $cwd = getcwd();    chdir(dirname($file));    $scope->pushScope();    $traverser->traverse($stmts);    //print_r($stmts);    $scope->popScope();    chdir($cwd);    echo "\n";}

Used this to make a big many script files holding code to go without global registered variables. We gone use it with PHP7.2