2012-08-07 2 views
9

정적 코드 분석을 수행하고 register_globals 이니셔티브에서 의존성을 감지하는 방법은 PHP에 있습니까? 파일을 수작업으로 검사하고 초기화되지 않은 변수를 찾고 그로부터 추측 할 수있는 변수를 찾는 것은 비교적 간단하지만 수백 개의 스크립트에서이 작업을 수행해야하므로 자동화 된 솔루션을 찾고 있습니다. .레지스터 전역 사용 감지

마지막 수단은 지시어를 끄고 엄격한 오류보고 및 QA를 오랫동안 반복하도록 설정 한 다음 오류 로그가 감지하는 인스턴스를 수정하지만 100 개를 찾을 수는 없습니다 자동화 된 솔루션이있는 경우에는 리소스 사용을 권장하지 않습니다.

+0

'extract()'와 같은 변수 변수와 함수를 고려해야하기 때문에 어려울 것입니다. –

+1

"아니오"라고 생각하고 있습니다. @MikeB가 올바르게 지적했듯이'extract()'와 변수 변수 (그리고'$ GLOBALS ')는 코드를 실제로 실행하지 않는 코드 분석을 위해 거대한 스패너를 던졌습니다. 네, 일괄 처리로 스크립트를 실행 해보고 설정되지 않은 변수의 사용에 대한 불만을 찾아 볼 수는 있지만 어쨌든 코드가 깨져있을 가능성은 항상 있습니다 (이 경우 아직 수정이 필요함). 불행히도 수동 접근 방식이 여기있는 유일한 옵션 일 것입니다. Enjoy ... – 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

아파치 환경의'if (! preg_match ('/ \ php $ /', $ file))는 계속된다. ; '어리석은 증거가 아닙니다. 아파치는 PHP 스크립트로'/ \. php (\. | $) /'와 일치하는 것을 실행합니다 (Windows에서는'i' 한정자를가집니다). 이것은 PHP로 구동되는 사이트의 세계에서 많은 보안 허점을 차지하는 아파치 이상한 점입니다. 이것은 아마도 * 내가 * 이해할 수없는 물건을 upvoting하는 것처럼 느끼지 않고 그것 (특히''PHPParser'')이 당신에게 하나를주기 위해 무엇을하는지 충분히 알 수는 없지만, 아마 +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

네, 둘 다이 검사가 올바르다는 것인가 아닌가. 사용 중인지 아닌지 검사한다. –