2012-08-07 4 views
9

静的コード分析を行い、register_globalsイニシアチブへの依存を検出する方法はありますか?ファイルを手作業で調べ、初期化されていない変数やそれに依存している可能性のある変数を探すのは比較的簡単ですが、何百ものスクリプトでこれを行う必要があるので、私は自動化されたソリューションを探しています。レジスタグローバルの使用の検出

私の最後の手段は、ディレクティブをオフにして厳密なエラー報告とQAを長時間実行してから、エラーログが検出するインスタンスを修正することですが、これは100ケースの%、そして確かに自動化されたソリューションが存在する場合はリソースの有効利用ではありません。

+0

可変変数と 'extract()'のような関数を考慮する必要があるので難しいでしょう –

+1

私は「いいえ」と考えています。 @MikeBが正しく指摘しているように、 'extract()'や変数変数(さらに '$ GLOBALS')のようなものは、コードを実際には実行しないコード解析のための巨大なスパナです。はい、バッチでスクリプトを実行して、設定されていない変数の使用に関する苦情を探すことができますが、コードはちょうど壊れている可能性があります(この場合はまだ修正が必要です)。残念ながら、ここでは手作業によるアプローチがおそらく唯一の選択肢です。お楽しみください... – DaveRandom

+0

PHP 5.4.0から 'register_globals'が削除されましたので注意してください。 – Florent

答えて

13

私は単純な未定義の変数を検出するために一緒にハッキング小さなスクリプト。

<?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"; 
} 

それはちょうど非常に基本的な実装だと、私は広範囲にそれをテストしていないが、それは$GLOBALS$$varVarsと野生行っていないスクリプトのために働く必要があります:あなたはこのためPHP-Parserが必要になります。基本的なインクルード解決を行います。

+5

Apache環境の 'if(!preg_match( '/ \ php $ /'、$ file) ; 'ばかげた証拠ではありません。 ApacheはPHPスクリプトとして '/ \。php(\。| $)/'と一致するものを実行します(Windowsでは 'i'修飾子を使用します)。これはあまり知られていないApacheの不思議さで、PHPで動くサイトの世界では数多くのセキュリティホールを占めています。これは、おそらく*私はそれが(特に 'PHPParser')あなたが私が理解していないものをupvotingしているように感じることなく1つを与えるためにやっていることを十分に知っていると言うことはできません+1のように見えます。 – DaveRandom

0

これは(PHPマニュアルコメントのいずれかから)動作するはずです:

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]); 
     } 
    } 
} 
+0

これは静的にグローバルの使用を検出しますか? –

+1

はい、そうでなければ* register_globalsをオフにすることができます。しかし、OPは、スクリプトがオフになっているかどうかを模倣するのではなく、オンにするかどうかをプログラムで検出できるかどうかを尋ねています。さらに、さまざまな状況でオンになるかもしれない多くの変数を捕まえることはできません。 '$ argv'はどうでしょうか? '$ argc'はどうでしょうか? '$ HTTP_RAW_POST_DATA'はどうでしょうか? – DaveRandom

+0

はい、あなたは2が両方とも正しいかどうかチェックしていますが、それが使われているのかどうかはチェックされていません。 –

関連する問題