检测寄存器全局变量的使用

FtD*_*Xw6 9 php code-analysis

PHP中有没有办法进行静态代码分析并检测对register_globals主动性的依赖?手动检查文件并查找尚未初始化的变量并从中推断可能依赖于它的变量相对简单,但我需要为数百个脚本执行此操作,因此我正在寻找自动化解决方案.

我的最后一招是设置开发环境,关闭指令并严格报告错误并让QA玩了很长时间,然后修复错误日志捕获的实例,但不能保证找到100%的情况如果存在自动化解决方案,当然也不能很好地利用资源.

Nik*_*kiC 13

我刚刚攻击一个小脚本来检测简单的未定义变量.你需要PHP-Parser:

<?php

error_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";
}
Run Code Online (Sandbox Code Playgroud)

这只是一个非常基本的实现,我没有进行广泛的测试,但它应该为脚本,不走野一起工作$GLOBALS$$varVars.它基本包括解决方案.

  • 只是一个FYI,在Apache环境中`if(!preg_match('/\.php $ /',$ file))继续;`不是万无一失的.Apache将执行任何匹配`/\.php(\.| $)/`作为PHP脚本的东西(在Windows上有一个`i`修饰符).这是一个鲜为人知的Apache奇怪之处,它解释了PHP驱动的网站世界中许多安全漏洞.这看起来像*可能*值得+1,虽然我不能说我知道它(特别是`PHPParser`)正在做什么给你一个而不觉得我正在赞美我不理解的东西. (5认同)