2010-03-29 5 views
5

나는 데이터베이스 클래스를 만들고 있으며 SQL 주입 방지 (duh!)를 통합하는 것이 좋습니다.데이터베이스 클래스에서 SQL 주입 방지하기

class DB 
{ 
    var $db_host = 'localhost'; 
    var $db_user = 'root'; 
    var $db_passwd = ''; 
    var $db_name = 'whatever'; 

    function query($sql) 
    { 
     $this->result = mysql_query($sql, $this->link); 
     if(!$this->result) 
     { 
      $this->error(mysql_error()); 
     } else { 
      return $this->result; 
     } 
    } 
} 

이보다 수업 시간에 더하지만이에 대한 그냥 자르고 해요 : 다음은 데이터베이스 쿼리를 실행하는 방법입니다. 내가 직면하고있는 문제는 단지 mysql_real_escape_string($sql, $this->link);을 사용하면 전체 쿼리를 이스케이프하고 SQL 구문 오류가 발생한다는 것입니다. 이스케이프해야하는 변수를 어떻게 동적으로 찾을 수 있습니까? 내 기본 코드 블록에서 mysql_real_escape_string()을 사용하지 않으려 고합니다. 차라리 함수에서 사용하고 싶습니다.

감사합니다.

+4

나는 당신의 이전 포스트에서 이것을 말했지만 반복하도록하겠습니다. PDO를 사용하십시오. 준비된 진술과 함께 PDO를 사용한다면, 당신은 심지어 그것들을 도피하는 것에 대해 걱정할 필요조차 없습니다. – ryeguy

+0

매개 변수화 된 quires를 사용하고, adodb 또는 pdo를 사용하십시오. – rook

답변

0

SQL 주입 공격을 막기위한 전체 아이디어는 사용자가 자신의 SQL을 실행하지 못하게하는 것입니다. 자, 사용자가 자신의 SQL을 실행할 수있게하려는 것 같습니다. 왜 제한할까요?

또는 사용자가 SQL을 query() 메소드에 전달하는 것을 허용하지 않는 경우. 이스케이프 매개 변수를 구현하기에는 수준이 너무 낮습니다. 깨달은 것처럼 전체 SQL 문이 아닌 매개 변수 만 이스케이프하려고합니다.

SQL을 매개 변수화하면 매개 변수 만 이스케이프 처리 할 수 ​​있습니다. 이 경우 사용자가 SQL이 아닌 매개 변수의 값에 영향을 줄 수 있다고 가정합니다.

PDO의 bindParam() 또는 Zend_DB의 query() 메소드를 통해 다른 데이터베이스 인터페이스에서 어떻게 구현되는지 알 수 있습니다.

+0

로그인 폼 등에서 사용하게 될 경우 (악의적 인 SQL을 입력하는 사용자를 신뢰하고 싶지는 않습니다.) – Joe

+0

분명히 코드 전체에서 재사용 할 클래스입니다 ... –

+0

"로그인 양식에 사용 하시겠습니까?" 바비 테이블 조심해 ... http://xkcd.com/327/ – Simon

0

이 작업을 올바르게 수행하려면 쿼리를 작성할 때 위생적으로 처리하거나 클래스에 넣어야 핵심 쿼리와 독립적 인 매개 변수를 전달해야합니다.

1

문제는 SQL 쿼리를 작성하기에 앞서 변수를 찾아 주입을 방지 할 수 없을 정도로 늦은 것입니다. 그렇지 않으면 이미 PHP에 내장되어있을 것입니다.

쿼리를 작성할 때 이스케이프 처리를 훨씬 빨리 수행해야합니다. u 리 빌딩 클래스를 사용할 수 있습니다. 여기 active record pattern를 사용하여 데이터베이스에 대한 완벽한 인터페이스를 제공 base db entity class에서 파생 예 user object이며, 객체로 데이터베이스 테이블을 제공하는 레이어를 가지고있다 -

그러나 나는 다른 접근 방법을 추천 할 것입니다 iterator pattern.

몇 가지 예를 들어 설명해 드리겠습니다. 여기서 깔끔한 것은 반복자 (iterator)입니다. 여러분이 더 많이 추상화 할 수 있고 데이터를 추출하기 위해 라인을 따라 꽤 일반적인 클래스를 더 가질 수 있습니다.

$user = new DbUser(); 
$user->set_email('[email protected]'); 
if ($user->load_from_fields()) 
{ 
} 

레코드를 반복하려면 :

$user_iterator = DbUser::begin(); 
if ($user_iterator->begin()) 
{ 
    do 
    { 
     $user = $user_iterator->current(); 
     echo $user->get_email(); 
    } while ($user_iterator->next()); 
} 
+1

이것은 나쁜 생각이 아닙니다. 핵심 단어는 "활성 레코드 패턴"입니다. Zend Framework, Ruby on Rails 및 일부 다른 프레임 워크는 이러한 유형의 기능을 제공합니다. –

+0

그리고 orm/active 레코드 토론 중 일부를 여기 stackoverflow에서 살펴보십시오. http://stackoverflow.com/questions/494816/using-an-orm-or-plain-sql – VolkerK

1

이 두 가지 방법이 있습니다 사용자 레코드를 읽으려면

$user = new DbUser(); 
$user->create(); 
$user->set_email('[email protected]'); 
$user->write(); 

:

은 위의 방법을 사용하여 사용자 레코드를 만들려면 SQL 주입 공격을 방지하기 위해 블랙리스트 기반 접근법 및 화이트리스트 기반 접근법.

블랙리스트 방식은 전체 쿼리 문자열을 확인하고 원치 않는 코드를 찾아 제거해야 함을 의미합니다. 지옥이 너무 어렵습니다. 대신 매개 변수화 된 SQL을 사용하여 화이트리스트 방식을 사용하십시오. 이렇게하면 실행되는 유일한 쿼리가 코드를 사용하여 의도적으로 작성한 쿼리가 될 것이고 모든 주입 쿼리가 매개 변수의 일부가 될 것이므로 모든 주입 시도가 실패하고 따라서 실행되지 않을 것입니다. 데이터 베이스. 쿼리가 이미 생성 된 후에 주입을 방지하는 방법을 찾고 있습니다.이 방법은 간접적으로 블랙리스트 기반 방법을 의미합니다.

전역 적으로 적용되는 보안 코딩 원칙 중 하나 인 코드에서 매개 변수화 된 SQL을 사용해보십시오.

1

어느 매개 변수화 또는 같은 것을 사용 (그리고 값을 사용할 수 있습니다 무엇이든) WHERE에 대한 모든 값을 전달하기 위해 DB 클래스를 구축하려고 :

$db->where(x,y); 

즉. 이 여러 입력을 지원하기 위해 청소해야 물론

$db->where('userid','22'); 

그리고 클래스 코퍼스의

function where(var x, var y) // method 
{ 
    $this->where .= x . ' = '.mysql_real_escape_string(y); 
} 

같은 것을 사용합니다.

0

필자는 쿼리 기능에 매개 변수를 추가하여이 문제를 해결했습니다. codeigniter가 꽤 멋지게 나왔다. 그래서 나는 그것을 자신의 취향에 맞게 조정했다.

예 :

$result = Database::query('INSERT INTO table (column1,column2,column3) VALUES(?,?,?)',array($value1,$value2,$value3)); 



public static $bind_marker = '?'; 
public static function query($query, $binds = FALSE) 
    { 
     if($binds !== FALSE) 
     { 
      $query = self::compile_binds($query,$binds); 
     } 
     // $query now should be safe to execute 
} 

private static function compile_binds($query, $binds) 
    { 
     if(strpos($query, self::$bind_marker) === FALSE) 
     { 
      return $query; 
     } 

     if(!is_array($binds)) 
     { 
      $binds = array($binds); 
     } 

     $segments = explode(self::$bind_marker, $query); 

     if(count($binds) >= count($segments)) 
     { 
      $binds = array_slice($binds, 0, count($segments)-1); 
     } 

     $result = $segments[0]; 
     $i = 0; 
     foreach($binds as $bind) 
     { 
      if(is_array($bind)) 
      { 
       $bind = self::sanitize($bind); 
       $result .= implode(',',$bind); 
      } 
      else 
      { 
       $result .= self::sanitize($bind); 
      } 

      $result .= $segments[++$i]; 
     } 

     return $result; 
    } 

public static function sanitize($variable) 
{ 
    if(is_array($variable)) 
    { 
     foreach($variable as &$value) 
     { 
      $value = self::sanitize($value); 
     } 
    } 
    elseif(is_string($variable)) 
    { 
     mysql_real_escape_string($variable); 
    } 
    return $variable; 
} 

난 "IN"을 이용하는데 유용한 파라미터로 배열을 사용할 수있다 CodeIgniter의 버전으로부터 추가 주요 추

$parameters = array 
    (
     'admin', 
     array(1,2,3,4,5) 
    ); 

$result = Database::query("SELECT * FROM table WHERE account_type = ? AND account_id IN (?)",$parameters); 
1

요점의 코드 삽입 방지는 사용자가 주입 한 sql과 sql을 구별해야한다는 것입니다. 이 최저 수준에서는 더 이상 할 수 없습니다. 예를 들어 줄 : 이것은 완벽 유효한 SQL 보이지만, 그것은 또한 SQL이 버전 주입 될 수있다

select * from users where username='test' and password='itisme' or '4'='4' 

:

"select * from users where username='test' and password='" . "itisme' or '4'='4". "'" 

그래서 당신은 당신의 코드에 더 그것을해야, 또는 사용을 래퍼 (wrappers)는 다른 제안대로.

0

왜 바퀴를 다시 태어 났을 까? PDO을 확장하고 매개 변수가있는 쿼리를 사용하십시오. 그것이 무엇인지 모르는 경우 시작하기위한 많은 예제가 들어있는 문서를 읽으십시오.