mysql 해킹 - PHP에서 SQL 삽입을 방지하려면 어떻게해야합니까?




기법 공격 (24)

사용자 입력을 수정하지 않고 SQL 쿼리에 삽입하면 다음 예제와 같이 응용 프로그램이 SQL 주입에 취약 해집니다.

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

그것은 사용자가 value'); DROP TABLE table;-- 와 같은 것을 입력 할 수 있기 때문입니다 value'); DROP TABLE table;-- value'); DROP TABLE table;-- , 쿼리는 다음과 같습니다.

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

이것이 일어나지 않도록하기 위해 할 수있는 일은 무엇입니까?


Answers

좋은 아이디어는 Idiorm 과 같은 '객체 관계형 매퍼' 를 사용하는 것 Idiorm .

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

SQL 인젝션뿐만 아니라 구문 오류로부터도 당신을 구할 수 있습니다! 또한 메서드 체인을 사용하여 한 번에 여러 결과에 대한 작업을 필터링하거나 적용 할 수있는 모델 모음을 지원합니다.


필자의 의견으로는 PHP 애플리케이션 (또는 웹 애플리케이션)에서 일반적으로 SQL 삽입을 방지하는 가장 좋은 방법은 애플리케이션의 아키텍처를 생각하는 것이다. SQL 인젝션으로부터 보호 할 수있는 유일한 방법은 데이터베이스에 연결할 때마다 올바른 방법을 수행하는 특별한 방법이나 기능을 사용하는 것을 잊지 않는 것입니다. 그렇게하면 코드의 어느 시점에서 쿼리의 형식을 올바르게 지정하지 않는 한 시간 문제 일뿐입니다.

MVC 패턴과 CakePHPCodeIgniter 와 같은 프레임 워크를 채택하는 것이 올바른 방법 일 것입니다. 안전한 데이터베이스 쿼리 작성과 같은 일반적인 작업은 이러한 프레임 워크에서 해결되고 중앙에서 구현됩니다. 이 도구를 사용하면 웹 응용 프로그램을 합리적인 방법으로 구성하고 단일 SQL 쿼리를 안전하게 작성하는 것보다 객체로드 및 저장에 대해 더 많이 생각하게 할 수 있습니다.


나는 보안상의 관점에서 저장 프로 시저 ( MySQL은 5.0 이후 프로 시저 지원을 저장했다)를 선호한다 - 장점은 -

  1. 대부분의 데이터베이스 ( MySQL 포함)는 사용자 액세스가 저장 프로 시저 실행으로 제한되도록합니다. 세분화 된 보안 액세스 제어는 권한 공격의 수준 상승을 방지하는 데 유용합니다. 이렇게하면 손상된 응용 프로그램이 데이터베이스에 대해 SQL을 직접 실행할 수 없게됩니다.
  2. 이들은 응용 프로그램에서 원시 SQL 쿼리를 추상화하므로 데이터베이스 구조에 대한 정보를 응용 프로그램에서 사용할 수 있습니다. 따라서 사람들이 데이터베이스의 기본 구조를 이해하고 적절한 공격을 설계하는 것이 더 어려워집니다.
  3. 매개 변수 만 허용하므로 매개 변수가있는 쿼리의 장점이 있습니다. 물론 - IMO에서는 여전히 입력을 위생해야합니다 - 특히 저장 프로 시저 내부에서 동적 SQL을 사용하는 경우.

단점은 -

  1. 그것들 (저장 프로 시저)은 유지하기가 어렵고 매우 빠르게 번식하는 경향이 있습니다. 이로 인해 문제를 관리하게됩니다.
  2. 동적 쿼리에 적합하지 않습니다. 동적 코드를 매개 변수로 허용하도록 빌드 된 경우 많은 이점이 무효화됩니다.

이 PHP 함수 mysql_escape_string()를 사용하면 빠르게 예방할 수 있습니다.

예 :

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - mysql_query에서 사용할 문자열 이스케이프

더 많은 예방을 위해 마지막에 추가 할 수 있습니다 ...

wHERE 1=1   or  LIMIT 1

마지막으로 다음을 얻습니다.

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

PDO를 사용하는 방법을 잘 모르는 사람들을 위해 ( mysql_필자는 함수 에서 왔음 ) 단일 파일 인 매우 간단한 PDO 래퍼 를 만들었습니다 . 애플리케이션이 수행해야하는 모든 일반적인 작업을 얼마나 쉽게 수행하는지 보여주기 위해 존재합니다. PostgreSQL, MySQL 및 SQLite에서 작동합니다.

기본적으로 PDOPDO PDO 기능을 실생활에 사용하여 원하는 형식으로 값을 저장하고 검색하는 방법을 쉽게 이해할 수 있습니다 .

나는 하나의 칼럼을 원한다.

$count = DB::column('SELECT COUNT(*) FROM `user`);

나는 배열 (키 => 값) 결과를 원한다. (즉, 선택 상자 만들기)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

단일 행 결과를 원합니다.

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

나는 일련의 결과를 원한다.

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

PDOMYSQLi 사용 하는 것은 SQL 주입을 막는 좋은 방법이지만, MySQL 함수와 쿼리로 작업하고 싶다면 다음을 사용하는 것이 좋습니다.

mysql_real_escape_string()

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

ident처럼 - 입력이 문자열, 숫자, 문자 또는 배열이면 이것을 탐지하는 많은 내장 함수가 있습니다. 또한 이러한 기능을 사용하여 입력 데이터를 확인하는 것이 좋습니다.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

그리고 이러한 기능을 사용하여 입력 데이터를 확인하는 것이 훨씬 낫습니다 mysql_real_escape_string.


SQL 인젝션 및 기타 SQL 해킹을 방지하는 데는 여러 가지 방법이 있습니다. 인터넷 (Google 검색)에서 쉽게 찾을 수 있습니다. 물론 PDO는 좋은 솔루션 중 하나입니다. 그러나 나는 SQL Injection에 대한 좋은 링크를 제안하고 싶습니다.

SQL 인젝션 및 방지 방법

SQL 인젝션을위한 PHP 매뉴얼

PHP의 SQL 주입 및 예방에 대한 Microsoft의 설명

그리고 MySQL과 PHP로 SQL 주입 방지하기 같은 것들

이제 SQL 주입으로 인한 쿼리를 방지해야하는 이유는 무엇입니까?

다음과 같은 간단한 예제로 SQL 인젝션을 막으려 고 시도하는 이유는 무엇입니까?

로그인 인증 일치에 대한 쿼리 :

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

자, 누군가 (해커)가

$_POST['email']= [email protected]' OR '1=1

비밀 번호를 ....

쿼리는 다음과 같은 경우에만 시스템에 구문 분석됩니다.

$query="select * from users where email='[email protected]' OR '1=1';

다른 부분은 폐기됩니다. 그래서, 어떻게 될까요? 승인되지 않은 사용자 (해커)는 비밀번호가 없어도 관리자로 로그인 할 수 있습니다. 이제 그는 관리자 / 전자 메일 사용자가 할 수있는 모든 작업을 수행 할 수 있습니다. SQL 주입을 막지 않으면 매우 위험합니다.


모든 대답은 문제의 일부만을 다룹니다. 실제로 동적으로 추가 할 수있는 가지 쿼리 부분이 있습니다.

  • 식별자
  • 구문 키워드

그리고 준비된 진술은 그 중 2 개만을 다룹니다.

그러나 때로는 연산자 나 식별자를 추가하여 쿼리를 더욱 동적으로 만들어야합니다. 그래서, 우리는 다른 보호 기술이 필요합니다.

일반적으로 이러한 보호 방법은 화이트리스트를 기반으로합니다.

이 경우, 모든 동적 매개 변수는 스크립트에서 하드 코딩되어 해당 세트에서 선택되어야합니다. 예를 들어 동적 순서 지정을 수행하려면 다음을 수행하십시오.

$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

그러나 식별자를 보호하는 또 다른 방법, 즉 이스케이프가 있습니다. 따옴표로 묶인 식별자가있는 한, 두 배로 역 따옴표를 이스케이프 할 수 있습니다.

추가 단계로, 준비된 문에서 일부 자리 표시 자 (쿼리의 실제 값을 나타내는 프록시)를 사용하고 식별자 자리 표시자인 다른 유형의 자리 표시자를 만드는 진정한 아이디어를 빌릴 수 있습니다.

긴 이야기를 짧게하기 위해 : 그것은 자리 표시 자입니다 . 준비된 문장 은 은색으로 간주 할 수 없습니다.

따라서 일반 권장 사항은 다음과 같이 표현할 수 있습니다. 자리 표시자를 사용하여 동적 파트를 쿼리에 추가하는 경우 (물론 이러한 올바르게 처리 된 자리 표시 자) 쿼리가 안전하다는 것을 확신 할 수 있습니다 .

여전히 SQL 구문 키워드 (예 : AND , DESC 등)에 문제가 있지만이 경우 화이트리스트가 유일한 접근 방법 인 것처럼 보입니다.

최신 정보

SQL 주입 보호와 관련된 모범 사례에 대한 일반적인 동의가 있지만 여전히 많은 나쁜 습관이 있습니다. 그리고 그들 중 일부는 PHP 사용자의 마음에 너무 뿌리 깊게 뿌리 내리고 있습니다. 예를 들어,이 바로 페이지에는 80 개의 삭제 된 답변 ( 대부분의 방문객에게는 보이지 않지만)이 있습니다. 모두 품질이 좋지 않거나 악의적 인 구식 사례로 인해 커뮤니티에서 삭제되었습니다. 더 나쁜 것은 나쁜 대답의 일부는 삭제되지 않고 번성하는 것입니다.

예를 들어, there(1) are(2) still(3) many(4) answers(5) . 수동 문자열 이탈을 제안하는 share 포함하여 - 안전하지 않은 것으로 입증 된 구식 접근 방식입니다.

또는 문자열 형식 지정의 또 다른 방법 을 제시하고 궁극적 인 만병 통치약으로 자랑 할 수도있는 약간 더 나은 대답이 있습니다. 물론, 그렇지 않습니다. 이 방법은 일반 문자열 형식보다 낫지 만 모든 단점을 유지합니다. 문자열에만 적용 할 수 있으며 다른 수동 형식과 마찬가지로 필수적으로 선택 사항이며 필수적이지 않은 측정 값으로 어떤 유형의 사람의 실수도 발생하기 쉽습니다.

나는이 모든 것이 OWASPPHP 매뉴얼 과 같은 권위자들이 지원하는 매우 오래된 미신 때문에 "escaping"과 SQL injection으로부터의 보호 사이의 평등을 선언한다고 생각합니다.

PHP 매뉴얼이 오랫동안 언급 한 것에 관계없이 *_escape_string 결코 데이터를 안전하게 만들지 않으며 결코 의도 된 적이 없습니다. 문자열이 아닌 다른 SQL 부분에서는 쓸모가 없으며 수동 이탈은 잘못되었습니다.

그리고 OWASP은 사용자 입력 을 회피하는 것을 완전히 악의적 인 것으로 강조하여 더욱 악화됩니다. 주입 방지의 맥락에서 그러한 단어가 없어야합니다. 모든 변수는 잠재적으로 위험합니다 - 소스에 관계없이! 또는 다시 말하면 - 모든 변수는 소스에 상관없이 쿼리에 넣기 위해 올바른 형식을 가져야합니다. 중요한 곳입니다. 개발자가 양과 염소를 분리하기 시작한 순간 (특정 변수가 안전한지 아닌지를 생각할 때) 재난을 향한 첫 발걸음을 내딛습니다. 심지어 말씨조차도 진입 점에서 일괄 탈출을 제안합니다. 이미 매우 경멸적인 인용 기능을 닮았습니다.

따라서 "이스케이프"가 무엇이든간에 준비된 명령문 실제로 SQL 주입 (해당되는 경우)으로부터 보호하는 조치입니다.

아직도 확신하지 못했다면, 여기에 제가 작성한 단계별 설명, SQL 주입 방지에 대한 Hitchhiker 's Guide가 있습니다 . 여기서 모든 문제를 자세히 설명하고 나쁜 관행과 공개에 전적으로 헌신하는 섹션을 작성했습니다.


매개 변수화 된 쿼리 및 입력 유효성 검사가 필요합니다. mysql_real_escape_string() 이 사용 되었더라도 SQL 주입이 발생할 수있는 시나리오는 많이 있습니다.

이러한 예제는 SQL 삽입에 취약합니다.

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

또는

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

두 경우 모두 캡슐화를 보호하기 위해 사용할 수 없습니다.

Source : 예기치 않은 SQL 삽입 (이스 케이 핑이 충분하지 않을 때)


나는 누군가가 PHP와 MySQL 또는 다른 데이터베이스 서버를 사용하기를 원한다면 나는 생각한다.

  1. PDO (PHP Data Objects) 학습에 대해 생각하십시오 - 여러 데이터베이스에 대한 일관된 액세스 방법을 제공하는 데이터베이스 액세스 계층입니다.
  2. MySQLi 학습에 대해 생각해보십시오.MySQLi
  3. strip_tags , mysql_real_escape_string() 또는 가변 숫자 인 경우 와 같이 기본 PHP 함수를 사용하십시오 (int)$foo. PHP here 변수 유형에 대한 자세한 내용은 here . PDO 나 MySQLi 같은 라이브러리를 사용한다면, 항상 PDO::quote()mysqli_real_escape_string() .

라이브러리 예제 :

---- PDO

----- 자리 표시 자 없음 - SQL 주입에 익숙해졌습니다! 그것은 나쁜

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- 이름없는 자리 표시 자

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- 명명 된 자리 표시 자

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

추신 :

PDO는이 전투에서 쉽게 승리합니다. 12 개의 다른 데이터베이스 드라이버와 명명 된 매개 변수에 대한 지원으로, 작은 성능 손실을 무시하고 API에 익숙해 질 수 있습니다. 보안 관점에서 볼 때 개발자가 사용되는 방식대로 사용하는 한 둘 다 안전합니다.

그러나 PDO와 MySQLi는 모두 매우 빠르지 만 MySQLi는 벤치 마크에서는 준비되지 않은 문장이 ~ 2.5 %, 준비된 문장이 ~ 6.5 % 정도로 현저히 빠르게 수행됩니다.

그리고 모든 쿼리를 데이터베이스에 테스트하십시오. 이것은 주사를 방지하는 더 좋은 방법입니다.


많은 유용한 답변과 관련하여이 스레드에 몇 가지 값을 추가하겠습니다. SQL 주입은 사용자 입력 (사용자가 입력 한 다음 쿼리 내부에서 사용되는 입력)을 통해 수행 할 수있는 공격입니다. SQL 주입 패턴은 올바른 쿼리 구문으로, 호출 할 수 있지만 나쁜 이유 때문에 잘못된 쿼리가있을 수 있습니다. 보안 (기밀성, 무결성, 가용성)의 세 가지 원칙에 영향을 미치는 비밀 정보 (액세스 제어 무시)를 얻으려는 나쁜 사람입니다.

이제 우리의 요점은 SQL 인젝션 공격과 같은 보안 위협, PHP를 사용하는 SQL 인젝션 공격을 방지하는 방법, 더 현실적인 것, 데이터 필터링 또는 입력 데이터 지우기와 같은 보안 위협을 방지하는 것입니다. PHP 또는 다른 프로그래밍 언어를 사용하는 쿼리가 아니거나 준비된 문이나 현재 SQL 주입 방지를 지원하는 다른 도구와 같은 최신 기술을 사용하도록 더 많은 사람들이 권장하는 방법으로 이러한 도구를 더 이상 사용할 수 없다고 생각하십니까? 응용 프로그램 보안 방법

SQL 인젝션에 대한 나의 접근 방식은 사용자 입력 데이터를 데이터베이스로 보내기 전에 (모든 쿼리 내에서 사용하기 전에) 사용자 입력 데이터를 지우는 것입니다.

(안전한 데이터에 안전하지 않은 데이터를 변환)에 대한 데이터 필터링 이 고려 PDOMySQLi 응용 프로그램을 확보 할 수있는 방법을 사용할 수 없습니다를? 당신이 나를 사용하도록 강요합니까? PHP 이외의 다른 언어는 어떻게됩니까? 특정 언어뿐만 아니라 더 넓은 범위의 테두리에 사용할 수있는 일반 아이디어를 제공하는 것을 선호합니다.

  1. SQL 사용자 (제한 사용자 권한) : 가장 일반적인 SQL 작업은 (SELECT, UPDATE, INSERT)입니다. 그런 다음 필요없는 사용자에게 UPDATE 권한을주는 이유는 무엇입니까? 예를 들어 로그인과 검색 페이지 는 SELECT 만 사용합니다. 그렇다면이 페이지에서 DB 사용자를 높은 권한으로 사용하는 이유는 무엇입니까? RULE : 모든 SQL 작업에 대해 모든 권한에 대해 하나의 데이터베이스 사용자를 만들지 말고, 쉽게 사용할 수 있도록 사용자 이름으로 (deluser, selectuser, updateuser)와 같은 스키마를 만들 수 있습니다.

Principle of least privilege를 보라.

  1. 데이터 필터링 : 쿼리 사용자 입력을 작성하기 전에 유효성을 검사하고 필터링해야합니다. 프로그래머의 경우 각 사용자 입력 변수에 대한 속성 ( 데이터 유형, 데이터 패턴 및 데이터 길이) 을 정의하는 것이 중요합니다 . (x와 y) 사이의 숫자 인 필드는 정확한 규칙을 사용하여 문자열 (텍스트) 인 필드에 대해 정확히 유효성을 검사해야합니다. 예를 들어, 패턴은 대소 문자를 포함해야합니다. [a- zA-Z0-9_-] 길이는 (x와 n) 사이에서 달라지며 x와 n은 정수 (x <= n)입니다. 규칙 : 정확한 필터 및 유효성 검사 규칙을 만드는 것이 나에게 가장 좋습니다.

  2. 다른 도구 사용 : 여기에서는 준비된 문 (매개 변수화 된 쿼리) 및 저장 프로 시저에 동의합니다. 단점은 이러한 방법은 대부분의 사용자에게는 존재하지 않는 고급 기술을 필요로한다는 것인데, 여기서 기본적인 개념은 SQL 쿼리 그리고 내부에서 사용되는 데이터는 사용자 입력 데이터가 (any 또는 x = x와 같은) 원래 쿼리에 아무 것도 추가하지 않기 때문에 안전하지 않은 데이터에서도 두 방법 모두 사용할 수 있습니다. 자세한 내용은 OWASP SQL Injection Prevention Cheat Sheet를 참조하십시오 .

이제 고급 사용자 인 경우이 방어 방법을 원하는대로 사용할 수 있지만 초보자는 저장 프로 시저를 신속하게 구현할 수없고 문을 준비 할 수없는 경우 입력 데이터를 가능한 한 많이 필터링하는 것이 좋습니다.

마지막으로, 사용자가 사용자 이름을 입력하는 대신이 텍스트를 아래에 보냈다고 가정 해 보겠습니다.

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

이 입력은 준비된 명령문 및 저장 프로 시저없이 일찍 점검 될 수 있지만 사용자 측 데이터 필터링 및 유효성 검증 이후에이를 사용하여 안전 측에 있어야합니다.

마지막 요점은 더 많은 노력과 복잡성을 필요로하는 예기치 않은 동작을 감지하는 것입니다. 일반 웹 응용 프로그램에는 권장되지 않습니다. 위의 사용자 입력에서 예기치 않은 동작이 발생하면 SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root가 검색되고 입력을 피할 수 있습니다.

UPDATE1 :

사용자는이 게시물이 쓸모 없다고 댓글을 달았습니다. OWASP.ORG 제공 한 내용은 다음과 같습니다 .

기본 방어 :

옵션 # 1 : 준비된 문 사용 (매개 변수가있는 쿼리)
옵션 # 2 : 저장 프로 시저 사용
옵션 # 3 : 모든 사용자 제공 입력 이스케이프

추가 방어 :

또한 시행 : 최소 권한
또한 수행 : 화이트리스트 입력 유효성 검사

아시다시피, 기사에 대한 주장은 유효한 논증, 적어도 하나의 참조로 뒷받침되어야합니다! 그렇지 않으면 공격과 나쁜 주장으로 간주됩니다!

업데이트 2 :

PHP 매뉴얼에서 PHP : Prepared Statements - Manual :

도주 및 SQL 주입

바운드 변수는 서버에 의해 자동으로 이스케이프됩니다. 서버는 실행하기 전에 이스케이프 된 값을 적절한 위치에 명령문 템플리트에 삽입합니다. 적합한 변환을 작성하기 위해 바운드 변수 유형에 대한 힌트를 서버에 제공해야합니다. 더 자세한 정보는 mysqli_stmt_bind_param () 함수를 참고하십시오.

서버 내에서 자동으로 값을 이스케이프 처리하는 것은 때때로 SQL 주입을 방지하는 보안 기능으로 간주됩니다. 입력 값이 올바르게 이스케이프 된 경우 준비되지 않은 명령문을 사용하여 동일한 수준의 보안을 달성 할 수 있습니다.

Update3 :

준비된 명령문을 사용할 때 PDO와 MySQLi가 MySQL 서버에 쿼리를 보내는 방법을 알기위한 테스트 케이스를 만들었습니다.

PDO :

$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

검색어 로그 :

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi :

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

검색어 로그 :

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

준비된 진술이 데이터를 빠져 나오고 있다는 것은 분명합니다.

따라서 위의 진술에서 언급했듯이 The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly, 이것은 intval()쿼리를 보내기 전에 정수 값에 대한 좋은 아이디어이며, 쿼리를 보내기 전에 악의적 인 사용자 데이터를 방지하는 것과 같은 데이터 유효성 검사 가 정확하고 유효한 접근 방법 이라는 것을 증명합니다 .

자세한 내용은이 질문을 참조하십시오. PDO가 원시 쿼리를 MySQL로 보내고 Mysqli가 준비된 쿼리를 보내면 동일한 결과가 생성됩니다.

참고 문헌 :

  1. SQL 인젝션 치트 시트
  2. SQL 삽입
  3. 정보 보안
  4. 보안 원칙
  5. 데이터 유효성 검사

RedisMemcached 와 같은 캐시 엔진을 이용하려면 아마도 DALMP가 선택 될 수 있습니다. 그것은 순수 MySQLi 사용합니다 . 이것을 확인하십시오 : PHP를 사용하는 MySQL 용 DALMP Database Abstraction Layer.

또한 동적 u 리를 빌드 할 수 있도록 u 리를 준비하기 전에 인수를 '준비'할 수 있으며 결국에는 완전히 준비된 명령문 u 리를 갖습니다. PHP를 사용하는 MySQL 용 DALMP 데이터베이스 추상화 계층.


PHP와 MySQL에 대한 답변은 많이 있지만 다음은 oci8 드라이버의 일반적인 사용뿐만 아니라 SQL 주입을 방지 하기위한 PHP와 Oracle 용 코드입니다 .

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

간단한 방법은 CodeIgniter 또는 Laravel 과 같은 PHP 프레임 워크를 사용하는 것입니다.이 프레임 워크는 필터링 및 활성 레코드와 같은 inbuilt 기능을 가지고 있으므로 이러한 뉘앙스에 대해 걱정할 필요가 없습니다.


PDO (PHP Data Objects)를 사용하여 매개 변수가있는 SQL 쿼리를 실행하는 것이 좋습니다.

SQL 인젝션을 방지 할뿐만 아니라 쿼리 속도를 높입니다.

그리고 mysql_ , mysqli_ , pgsql_ 함수가 아닌 PDO를 사용하면 드물게 데이터베이스 공급자를 전환해야하는 상황에서 데이터베이스에서 앱을 좀 더 추상화 할 수 있습니다.


가능한 경우 매개 변수의 유형을 형변환하십시오. 하지만 int, bool 및 float와 같은 간단한 유형에서만 작동합니다.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

경고 : 이 답변의 샘플 코드 (질문의 샘플 코드와 같은)는 PHP의 mysql 확장을 사용합니다.이 확장은 PHP 5.5.0에서 사용되지 않으며 PHP 7.0.0에서 완전히 제거되었습니다.

최신 버전의 PHP를 사용하고 있다면, 아래에서 설명하는 mysql_real_escape_string 옵션은 더 이상 사용할 수 없게됩니다 ( mysqli::escape_string 은 현대의 기능입니다). 요즈음 mysql_real_escape_string 옵션은 구 버전 PHP의 레거시 코드에만 의미가 있습니다.

unsafe_variable 특수 문자를 이스케이프 처리하거나 매개 변수화 된 쿼리를 사용하는 두 가지 옵션이 있습니다. 둘 다 SQL 주입으로부터 당신을 보호 할 것입니다. 매개 변수화 된 쿼리는 더 나은 방법이지만 PHP를 사용하기 전에 PHP에서 더 새로운 mysql 확장으로 변경해야합니다.

먼저 영향을 덜받는 문자열을 이스케이프 처리합니다.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

mysql_real_escape_string 함수의 세부 사항도 보라.

매개 변수가있는 쿼리를 사용하려면 MySQL 함수 대신 MySQLi 를 사용해야합니다. 예를 재 작성하려면 다음과 같은 것이 필요할 것입니다.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

mysqli::prepare 는 당신이 읽고 싶어 할 핵심 기능이다.

또한 다른 사람들이 제안한 것처럼 PDO 와 같은 추상화 계층을 유용하고 쉽게 사용할 수 있습니다.

요청한 사례는 매우 간단하며 복잡한 사례의 경우 더 복잡한 접근법이 필요할 수 있습니다. 특히:

  • 사용자 입력을 기반으로 SQL 구조를 변경하려는 경우 매개 변수화 된 쿼리는 도움이되지 않으며 필요한 이스케이프 처리는 mysql_real_escape_string 다루지 않습니다. 이런 경우에는 허용 된 값만 '안전'하도록 허용하기 위해 허용 목록을 통해 사용자 입력을 전달하는 것이 좋습니다.
  • 조건에서 사용자 입력의 정수를 사용하고 mysql_real_escape_string 접근법을 사용하면 Polynomial 에 설명 된 문제가 발생합니다. 정수가 따옴표로 묶이지 않기 때문에이 경우가 더 까다 롭습니다. 따라서 사용자 입력에 숫자 만 포함되어 있는지 확인하여 처리 할 수 ​​있습니다.
  • 내가 알지 못하는 다른 경우가있을 수 있습니다. this 문제는 발생할 수있는보다 미세한 문제에 대한 유용한 자료입니다.

** 경고 :이 대답에 설명 된 접근법은 매우 특정한 시나리오에만 적용되며 SQL 주입 공격은 주입 할 수있는 것에 의존하지 않으므로 안전하지 않습니다 X=Y. **

공격자가 PHP의 $_GET변수 또는 URL의 쿼리 문자열을 통해 양식을 해킹하려고 시도 하는 경우 보안되지 않은 공격자 를 잡을 수 있습니다.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

때문에 1=1, 2=2, 1=2, 2=1, 1+1=2, 등 ... 공격자의 SQL 데이터베이스에 대한 일반적인 질문이다. 어쩌면 그것은 또한 많은 해킹 응용 프로그램에 의해 사용됩니다.

그러나주의해야합니다. 사이트에서 안전한 쿼리를 다시 작성하면 안됩니다. 위의 코드를 다시 작성하거나 리디렉션, 당신에게 팁을주고있다 (당신에 따라 다름) 것을 해킹 특정 공격자의 저장하는 페이지에 동적 쿼리 문자열 IP 주소를 그들의 쿠키, 역사, 브라우저를하거나, 또는 민감한 다른 정보를 제공하므로 나중에 자신의 계정을 금지하거나 관계 당국에 연락하여 대응할 수 있습니다.


이 문제에 대한 간단한 대안은 데이터베이스 자체에 적절한 권한을 부여하여 해결할 수 있습니다. 예 : mysql 데이터베이스를 사용하는 경우 터미널이나 제공된 UI를 통해 데이터베이스에 들어가 다음 명령을 따르십시오.

 GRANT SELECT, INSERT, DELETE ON database TO [email protected]'localhost' IDENTIFIED BY 'password';

이렇게하면 사용자는 지정된 쿼리에만 국한 될 수 있습니다. 삭제 권한을 제거하면 데이터가 PHP 페이지에서 실행 된 쿼리에서 삭제되지 않습니다. 두 번째로해야 할 일은 권한을 플러시하여 mysql이 권한과 업데이트를 새로 고치는 것이다.

FLUSH PRIVILEGES; 

flush 에 대한 자세한 정보 .

사용자에 대한 현재 권한을 보려면 다음 쿼리를 실행하십시오.

select * from mysql.user where User='username';

GRANT 대해 자세히 알아보십시오 .


준비된 문과 매개 변수화 된 쿼리를 사용하십시오. 이것은 매개 변수와 별도로 데이터베이스 서버로 보내고 구문 분석되는 SQL 문입니다. 이렇게하면 공격자가 악의적 인 SQL을 삽입하는 것은 불가능합니다.

기본적으로이 작업을 수행하는 데는 두 가지 옵션이 있습니다.

  1. PDO 사용 (지원되는 모든 데이터베이스 드라이버 용) :

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. MySQLi 사용 (MySQL 용) :

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

MySQL 이외의 데이터베이스에 연결하는 경우 참조 할 수있는 드라이버 관련 두 번째 옵션이 있습니다 (예 : PostgreSQL의 경우 pg_prepare()pg_execute() ). PDO는 보편적 인 옵션입니다.

제대로 연결 설정

PDO 를 사용하여 MySQL 데이터베이스에 액세스 할 때 실제로 준비된 명령문은 기본적으로 사용되지 않습니다 . 이 문제를 해결하려면 준비된 문장의 에뮬레이션을 비활성화해야합니다. PDO를 사용하여 연결을 만드는 예는 다음과 같습니다.

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

위의 예제에서 오류 모드는 꼭 필요한 것은 아니지만 추가하는 것이 좋습니다 . 이렇게하면 스크립트가 잘못되었을 때 Fatal Error 멈추지 않습니다. 그리고 개발자는 PDOException throw 하는 모든 오류를 catch 수 있습니다.

그러나 PDO에게 에뮬레이트 된 준비 문을 사용 불가능하게하고 실제로 준비된 문을 사용하도록 지시하는 첫 번째 setAttribute() 행은 필수 항목 입니다. 이렇게하면 문과 값이 PHP에 의해 구문 분석되어 MySQL 서버로 전송되지 않습니다 (공격자가 악성 SQL을 삽입 할 가능성이 없음).

생성자의 옵션에서 charset 을 설정할 수 있지만 '구 버전'PHP (<5.3.6)는 DSN 의 charset 매개 변수자동으로 무시 한다는 점에 유의해야합니다.

설명

데이터베이스 서버가 prepare 하기 prepare 전달한 SQL 문은 구문 분석되고 컴파일됩니다. 매개 변수 ( ? 또는 위의 예에서 :name 과 같은 명명 된 매개 변수)를 지정하여 데이터베이스 엔진에 필터링 할 위치를 지정하십시오. 그런 다음 execute 를 호출하면 준비된 명령문이 지정한 매개 변수 값과 결합됩니다.

여기서 중요한 점은 매개 변수 값이 SQL 문자열이 아닌 컴파일 된 명령문과 결합된다는 것입니다. SQL 주입은 데이터베이스에 보낼 SQL을 생성 할 때 악의적 인 문자열을 포함하도록 스크립트를 속여서 작동합니다. 따라서 실제 SQL을 매개 변수와 별도로 전송하면 의도하지 않은 것으로 끝날 위험성을 제한 할 수 있습니다. 준비된 명령문을 사용할 때 보내는 모든 매개 변수는 단지 문자열로 취급됩니다 (데이터베이스 엔진이 매개 변수가 숫자로 끝날 수 있도록 일부 최적화를 수행 할 수도 있음). 위의 예제에서 $name 변수에 'Sarah'; DELETE FROM employees 가 포함 된 경우 'Sarah'; DELETE FROM employees 'Sarah'; DELETE FROM employees 으로부터 'Sarah'; DELETE FROM employees 결과는 단순히 "'Sarah'; DELETE FROM employees" 문자열을 검색하고 "'Sarah'; DELETE FROM employees" 하십시오. 그러면 빈 테이블 이 생기지 않게됩니다.

준비된 명령문을 사용하는 또 다른 이점은 동일한 세션에서 동일한 명령문을 여러 번 실행하면 구문 분석 및 컴파일이 한 번만 수행되어 속도가 약간 향상된다는 것입니다.

아, 그리고 삽입을 위해 어떻게해야하는지에 대해 물어 본 이래로, 여기에 (PDO를 사용하는) 예가 있습니다 :

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

동적 쿼리에 대해 prepared statement를 사용할 수 있습니까?

쿼리 매개 변수에 대해 준비된 문을 계속 사용할 수는 있지만 동적 쿼리 자체의 구조는 매개 변수화 할 수 없으며 특정 쿼리 기능을 매개 변수화 할 수 없습니다.

이러한 특정 시나리오의 경우 가능한 값을 제한하는 화이트리스트 필터를 사용하는 것이 가장 좋습니다.

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

PDO 와 준비된 쿼리를 사용하십시오.

( $connPDO 객체입니다)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

보시다시피 사람들은 준비된 문장을 최대한 사용하는 것이 좋습니다. 잘못된 것은 아니지만 쿼리가 프로세스 당 한 번만 실행 되면 약간의 성능 저하가 발생합니다.

나는이 문제에 직면 해 있었지만 해커들이 따옴표 사용을 피하는 방법과 같이 매우 정교한 방법으로 해결했다고 생각합니다. 필자는 에뮬레이트 된 준비 문과 함께이 기능을 사용했습니다. 저는 모든 종류의 가능한 SQL 주입 공격을 막기 위해 그것을 사용합니다.

내 접근 방식 :

  • 입력이 정수가 될 것으로 예상되면 실제로 정수 여야합니다. PHP와 같은 변수 형 언어에서는 이것이 매우 중요합니다. sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input); 예를 들어 다음과 같이 간단하고 강력한 솔루션을 사용할 수 있습니다 sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • 당신이 정수 hex 로부터 다른 것을 기대한다면. 16 진수라면 모든 입력을 완벽하게 피할 수 있습니다. C / C ++에는 mysql_hex_string() 이라는 함수가 있습니다. PHP에서는 bin2hex() 사용할 수 있습니다.

    mysql_real_escape_string 을 사용하더라도 동일한 용량 ((2*input_length)+1) 을 할당해야하므로 이스케이프 된 문자열의 원래 길이가 2x 크기가되므로 걱정하지 마십시오.

  • 이 16 진수 메서드는 이진 데이터를 전송할 때 자주 사용되지만 SQL 주입 공격을 막기 위해 모든 데이터에 사용하지 않아도됩니다. 0x 데이터를 추가하거나 UNHEX 대신 MySQL 함수를 UNHEX 합니다.

예를 들어, 쿼리는 다음과 같습니다.

SELECT password FROM users WHERE name = 'root'

될 것입니다:

SELECT password FROM users WHERE name = 0x726f6f74

또는

SELECT password FROM users WHERE name = UNHEX('726f6f74')

16 진수는 완벽한 탈출구입니다. 주사 할 방법이 없어.

UNHEX 함수와 0x 접두어의 차이점

코멘트에 약간의 토론이 있었기 때문에 나는 그것을 명확하게 밝히고 싶습니다. 이 두 가지 접근법은 매우 유사하지만 몇 가지면에서 조금 다릅니다.

** 0x ** 접두어는 char, varchar, text, block, binary 등과 같은 데이터 열에 만 사용할 수 있습니다.
또한 빈 문자열을 삽입하려는 경우 약간 복잡합니다. 완전히 '' 교체해야합니다. 그렇지 않으면 오류가 발생합니다.

UNHEX ()모든 열에서 작동합니다. 빈 문자열에 대해 걱정할 필요가 없습니다.

16 진법은 종종 공격으로 사용됩니다.

이 16 진법은 정수가 문자열과 같고 mysql_real_escape_string 하여 이스케이프 처리되는 SQL 주입 공격으로 자주 사용됩니다. 그런 다음 따옴표를 사용하지 않아도됩니다.

예를 들어, 다음과 같이하면됩니다.

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

공격은 당신을 아주 쉽게 주사 할 수 있습니다 . 스크립트에서 반환 된 다음 주입 코드를 고려하십시오.

SELECT ... where id = -1 union 모두 information_schema.tables에서 table_name을 선택합니다.

이제 테이블 구조 만 추출하면됩니다.

SELECT ... where id = -1 union 모두 information_schema.column의 column_name을 선택합니다. 여기서 table_name = 0x61727469636c65

그리고 원하는 데이터를 선택하십시오. 멋지지 않니?

그러나 주사 할 수있는 사이트의 코더가 16 진수 일 경우 쿼리는 다음과 같을 것이므로 주사는 불가능합니다 : SELECT ... WHERE id = UNHEX('2d312075...3635')


웹 응용 프로그램이 SQL 주입에 취약하지 않도록 세 가지 방법을 사용합니다.

  1. 의 사용 mysql_real_escape_string()에 미리 정의 된 함수, PHP ,이 코드는 다음 문자로 백 슬래시를 추가 : \x00, \n, \r, \, ', "\x1a. 입력 값을 매개 변수로 전달하여 SQL 주입 가능성을 최소화하십시오.
  2. 가장 진보 된 방법은 PDO를 사용하는 것입니다.

이게 당신을 도울 수 있기를 바랍니다.

다음 쿼리를 고려하십시오.

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string ()은 여기서 보호하지 않습니다. 쿼리 내에서 변수 주위에 작은 따옴표 ( '')를 사용하면이 문제를 방지 할 수 있습니다. 다음은이를위한 해결책입니다.

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

question 에는 이에 question 좋은 대답이 있습니다.

PDO를 사용하는 것이 가장 좋은 방법이라고 제안합니다.

편집하다:

mysql_real_escape_string()PHP 5.5.0부터는 더 이상 사용되지 않습니다. mysqli 또는 PDO를 사용하십시오.

mysql_real_escape_string ()의 대안은 다음과 같습니다.

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

예:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

이 경우 '는 주석 문자가 아닙니다. 문자열 리터럴을 구분하는 데 사용됩니다. 만화 아티스트는 문제의 학교가 다음과 같은 어딘가에 동적 SQL을 가지고 있다는 아이디어에 기반을두고 있습니다.

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

이제 프로그래머가 기대하기 전에 '문자는 문자열 리터럴을 끝냅니다. 함께; 문자를 사용하여 명령문을 끝내면 공격자는 원하는 SQL을 추가 할 수 있습니다. - 결국 주석은 원본 문장의 나머지 SQL이 서버에서 쿼리가 컴파일되지 않도록합니다.

FWIW, 나는 또한 문제의 만화가 중요한 세부 사항을 잘못 생각했다고 생각한다. 만약 당신이 데이터베이스 입력을 살균 하는 것에 대해 생각하고 있다면, 만화가 암시 하듯이 여전히 잘못하고있다. 대신 데이터베이스 입력을 격리 하는 방법을 고려해야하며 올바른 방법은 매개 변수가있는 쿼리를 사용하는 것입니다.





php mysql sql security sql-injection