PHP에서 SQL 주입을 어떻게 방지 할 수 있습니까?

Sep 13 2008

사용자 입력이 수정없이 SQL 쿼리에 삽입 되면 다음 예제와 같이 애플리케이션이 SQL 주입에 취약 해집니다 .

$unsafe_variable = $_POST['user_input']; 

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

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

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

이를 방지하기 위해 무엇을 할 수 있습니까?

답변

9121 PeeHaa Sep 13 2008 at 19:30

준비된 문과 매개 변수가있는 쿼리를 사용합니다. 이는 매개 변수와 별도로 데이터베이스 서버로 전송되고 구문 분석되는 SQL 문입니다. 이렇게하면 공격자가 악의적 인 SQL을 주입 할 수 없습니다.

기본적으로이를 달성하기위한 두 가지 옵션이 있습니다.

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

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute([ '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의 데이터베이스가 아닌 다른 데이터베이스에 연결하는 경우 (예를 들어, 참조 할 수 드라이버 별 두 번째 옵션이 pg_prepare()pg_execute()PostgreSQL을위한)가. PDO는 범용 옵션입니다.


연결을 올바르게 설정

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

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

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

위의 예에서 오류 모드는 꼭 필요한 것은 아니지만 추가하는 것이 좋습니다 . 이렇게하면 스크립트가 Fatal Error무언가 잘못 될 때 멈추지 않습니다 . 그리고 개발자에게 n이 s 인 catch오류에 대한 기회를 제공합니다 .throwPDOException

무엇 필수 , 그러나, 처음으로 setAttribute()비활성화 에뮬레이트 준비된 문 및 사용에 PDO를 알려줍니다 라인, 실제 준비된 문을. 이렇게하면 문과 값이 MySQL 서버로 보내기 전에 PHP가 구문 분석하지 않도록합니다 (공격자가 악의적 인 SQL을 주입 할 기회가 없음).

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


설명

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

여기서 중요한 것은 매개 변수 값이 SQL 문자열이 아닌 컴파일 된 명령문과 결합된다는 것입니다. SQL 주입은 데이터베이스로 보낼 SQL을 생성 할 때 스크립트가 악성 문자열을 포함하도록 속이는 방식으로 작동합니다. 따라서 매개 변수와는 별도로 실제 SQL을 보내면 의도하지 않은 결과가 나올 위험을 줄일 수 있습니다. ㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ

준비된 명령문을 사용할 때 보내는 모든 매개 변수는 문자열로 처리됩니다 (데이터베이스 엔진이 일부 최적화를 수행 할 수 있으므로 매개 변수도 물론 숫자로 끝날 수 있음). 위의 예에서 $name변수 'Sarah'; DELETE FROM employees에 결과가 포함 된 경우 단순히 문자열 검색 "'Sarah'; DELETE FROM employees"이 되고 빈 테이블로 끝나지 않습니다 .

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

아, 그리고 삽입을 위해 어떻게하는지 물 었으니, 여기에 예가 있습니다 (PDO 사용) :

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

준비된 문을 동적 쿼리에 사용할 수 있습니까?

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

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

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
1670 MattSheppard Sep 13 2008 at 16:48

Deprecated Warning : 이 답변의 샘플 코드 (예 : 질문의 샘플 코드)는 PHP MySQL확장을 사용합니다.이 확장은 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거되었습니다.

보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 SQL 주입을 방지하기 위해 불충분 , 사용 준비가 문을 대신. 귀하의 책임하에 아래에 설명 된 전략을 사용하십시오. (또한 mysql_real_escape_string()PHP 7에서 제거되었습니다.)

최신 버전의 PHP를 사용하는 경우 mysql_real_escape_string아래에 설명 된 옵션을 더 이상 사용할 수 없습니다 ( 최신 버전 임에도 불구하고 mysqli::escape_string). 요즘이 mysql_real_escape_string옵션은 이전 버전의 PHP에있는 레거시 코드에만 의미가 있습니다.


에서 특수 문자를 이스케이프 처리 unsafe_variable하거나 매개 변수화 된 쿼리를 사용하는 두 가지 옵션이 있습니다. 둘 다 SQL 주입으로부터 보호합니다. 매개 변수화 된 쿼리가 더 나은 방법으로 간주되지만 사용하기 전에 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접근 방식을 취하면 아래 주석에서 다항식 에 설명 된 문제가 발생 합니다. 이 경우는 정수가 따옴표로 묶이지 않기 때문에 더 까다롭기 때문에 사용자 입력에 숫자 만 포함되어 있는지 확인하여 처리 할 수 ​​있습니다.
  • 내가 알지 못하는 다른 경우가있을 수 있습니다. 당신은 찾을 수 이 발생할 수있는 미묘한 문제의 일부에 유용한 자원이다.
1087 YourCommonSense Nov 24 2011 at 16:50

여기에있는 모든 답변은 문제의 일부만 다룹니다. 실제로 SQL에 동적으로 추가 할 수있는 가지 쿼리 부분이 있습니다.-

  • 문자열
  • 숫자
  • 식별자
  • 구문 키워드

그리고 준비된 진술은 그중 두 가지만을 다루고 있습니다.

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

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

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

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

프로세스를 쉽게하기 위해 한 줄에 모든 작업을 수행 하는 화이트리스트 도우미 함수 를 작성했습니다.

$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name"); $query  = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe

식별자를 보호하는 또 다른 방법이 있습니다. 이스케이프 처리는하지만보다 강력하고 명시적인 접근 방식으로 화이트리스트에 집착합니다. 그러나 따옴표로 묶인 식별자가있는 한 따옴표 문자를 이스케이프하여 안전하게 만들 수 있습니다. 예를 들어, mysql의 경우 기본적 으로 이스케이프하려면 따옴표 를 두 배로 사용해야 합니다 . 다른 DBMS의 경우 이스케이프 규칙은 다를 수 있습니다.

그럼에도 불구하고,이 SQL 구문 키워드에 문제가 (예이며 AND, DESC등)하지만, 화이트리스트는이 경우의 유일한 접근 방식을 보인다.

따라서 일반적인 권장 사항은 다음과 같이 표현 될 수 있습니다.

  • SQL 데이터 리터럴 (또는 간단히 말하자면 SQL 문자열 또는 숫자)을 나타내는 모든 변수는 준비된 명령문을 통해 추가되어야합니다. 예외 없음.
  • SQL 키워드, 테이블, 필드 이름 또는 연산자와 같은 다른 쿼리 부분은 화이트리스트를 통해 필터링되어야합니다.

최신 정보

SQL 인젝션 보호와 관련된 모범 사례에 대한 일반적인 합의가 있지만 여전히 많은 나쁜 사례가 있습니다. 그리고 그들 중 일부는 PHP 사용자의 마음에 너무 깊이 뿌리 박고 있습니다. 예를 들어,이 페이지에는 80 개 이상의 삭제 된 답변 이 있습니다 (대부분의 방문자에게는 보이지 않지만) -모두 품질이 좋지 않거나 불량하고 오래된 관행을 홍보하기 때문에 커뮤니티에서 삭제되었습니다. 더 나쁜 것은 일부 나쁜 답변이 삭제되지 않고 오히려 번성한다는 것입니다.

예를 들어, there (1) are (2) still (3) many (4) answers (5) , 수동 문자열 이스케이프를 제안 하는 두 번째로 많은 찬성 응답 -안전하지 않은 것으로 입증 된 구식 접근 방식을 포함합니다.

또는 문자열 형식화의 또 다른 방법 을 제안 하고 궁극적 인 만병 통치약으로 자랑 하는 약간 더 나은 답변이 있습니다. 물론 그렇지 않습니다. 이 방법은 일반 문자열 형식보다 낫지는 않지만 모든 결점을 유지합니다. 문자열에만 적용 할 수 있으며 다른 수동 형식과 마찬가지로 본질적으로 선택적이고 의무적이지 않은 조치이며 모든 종류의 인적 오류가 발생하기 쉽습니다.

이 모든 것은 OWASP 나 PHP 매뉴얼 같은 권위자들이 지원하는 아주 오래된 미신 때문이라고 생각합니다.이 문서는 "이스케이프"와 SQL 주입으로부터의 보호 사이의 동등성을 선언합니다.

PHP 매뉴얼이 오랫동안 말한 내용과 관계없이 *_escape_string데이터를 안전하게 만들지 않으며 의도 한 적이 없습니다. 문자열 이외의 SQL 부분에 쓸모가 없다는 것 외에도 수동 이스케이프는 자동화와 반대로 수동이기 때문에 잘못되었습니다.

그리고 OWASP는 상황을 더욱 악화 시켜 완전히 넌센스 인 사용자 입력 을 피하는 것을 강조합니다 . 주입 방지와 관련하여 그러한 단어가 없어야합니다. 소스에 관계없이 모든 변수는 잠재적으로 위험합니다! 즉, 모든 변수는 소스에 관계없이 쿼리에 입력 할 수 있도록 적절한 형식을 지정해야합니다. 중요한 목적지입니다. 개발자가 양과 염소를 분리하기 시작하는 순간 (특정 변수가 "안전한"지 아닌지 생각하면서) 재난을 향한 첫 걸음을 내딛습니다. 단어조차도 이미 멸시되고 비난되고 제거 된 매우 마법의 따옴표 기능과 유사한 진입 점에서 대량 이스케이프를 제안한다는 것은 말할 것도 없습니다.

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

857 Kibbee Sep 13 2008 at 07:02

매개 변수가있는 SQL 쿼리를 실행하려면 PDO (PHP 데이터 개체)를 사용하는 것이 좋습니다 .

이렇게하면 SQL 주입으로부터 보호 할뿐만 아니라 쿼리 속도도 향상됩니다.

그리고보다는 PDO를 사용하여 mysql_, mysqli_pgsql_기능, 당신은 당신의 응용 프로그램에 더 스위치 데이터베이스 제공 업체에 가지고있는 드문에서, 데이터베이스에서 추출 조금합니다.

626 Imran Sep 13 2008 at 20:20

PDO쿼리를 사용 하고 준비합니다.

( $connPDO개체입니다)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)"); $stmt->bindValue(':id', $id); $stmt->bindValue(':name', $name); $stmt->execute();
556 Zaffy Oct 03 2012 at 21:07

보시다시피 사람들은 준비된 진술을 최대한 사용하도록 제안합니다. 잘못된 것은 아니지만 쿼리가 프로세스 당 한 번만 실행 되면 약간의 성능 저하가 발생합니다.

저는이 문제에 직면하고 있었지만 해커가 인용 부호를 사용하지 않기 위해 사용하는 방식 인 매우 정교한 방식으로 해결했다고 생각 합니다. 나는 이것을 에뮬레이트 된 준비된 진술과 함께 사용했습니다. 가능한 모든 종류의 SQL 주입 공격 을 방지하기 위해 사용합니다 .

내 접근 방식 :

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

  • 정수 16 진수 에서 다른 것을 기대 하면 . 16 진수를 사용하면 모든 입력을 완벽하게 이스케이프 할 수 있습니다. C / C ++에는라는 함수 mysql_hex_string()가 있습니다 bin2hex(). PHP에서는 .

    를 사용하더라도 mysql_real_escape_stringPHP는 동일한 용량을 할당 해야하므로 이스케이프 된 문자열의 크기가 원래 길이의 2 배가 될 것이라는 점에 대해 걱정하지 마십시오 ((2*input_length)+1).

  • 이 16 진수 방법은 바이너리 데이터를 전송할 때 자주 사용되지만 SQL 주입 공격을 방지하기 위해 모든 데이터에 사용하지 않을 이유가 없습니다. 대신 0xMySQL 함수 UNHEX를 사용하거나 데이터를 앞에 추가해야합니다 .

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

SELECT password FROM users WHERE name = 'root';

될 것입니다:

SELECT password FROM users WHERE name = 0x726f6f74;

또는

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

Hex는 완벽한 탈출구입니다. 주사 할 방법이 없습니다.

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

의견에 대한 논의가 있었기 때문에 마침내 명확하게 말하고 싶습니다. 이 두 가지 접근 방식은 매우 유사하지만 몇 가지면에서 약간 다릅니다.

0x접두사 만 같은 데이터 열을 사용할 수 있습니다 char, varchar, text, block, binary, 등
또한, 그것의 사용은 빈 문자열을 삽입하려고하면 복잡 조금이다. 완전히으로 바꿔야합니다 ''. 그렇지 않으면 오류가 발생합니다.

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


Hex 방법은 종종 공격으로 사용됩니다.

이 16 진수 방법은 정수가 문자열과 같고 .NET으로 이스케이프되는 SQL 주입 공격으로 자주 사용됩니다 mysql_real_escape_string. 그런 다음 따옴표 사용을 피할 수 있습니다.

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

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

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

SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;

이제 테이블 구조를 추출하십시오.

SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;

그런 다음 원하는 데이터를 선택하십시오. 멋지지 않나요?

그러나 주입 가능한 사이트의 코더가 16 진법을 사용하면 쿼리가 다음과 같기 때문에 주입이 불가능합니다.

SELECT ... WHERE id = UNHEX('2d312075...3635');
501 rahularyansharma Jun 17 2011 at 11:00

Deprecated Warning : 이 답변의 샘플 코드 (예 : 질문의 샘플 코드)는 PHP MySQL확장을 사용합니다.이 확장은 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거되었습니다.

보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 SQL 주입을 방지하기 위해 불충분 , 사용 준비가 문을 대신. 귀하의 책임하에 아래에 설명 된 전략을 사용하십시오. (또한 mysql_real_escape_string()PHP 7에서 제거되었습니다.)

중대한

SQL 주입을 방지하는 가장 좋은 방법 은 수락 된 답변 에서 알 수 있듯이 이스케이프 대신 준비된 문 을 사용하는 것입니다 .

개발자가 준비된 명령문을 더 쉽게 사용할 수 있도록하는 Aura.Sql 및 EasyDB 와 같은 라이브러리가 있습니다 . 준비된 명령문이 SQL 주입 을 중지하는 데 더 나은 이유에 대해 자세히 알아 보려면 이 mysql_real_escape_string()우회 및 최근 수정 된 WordPress의 유니 코드 SQL 주입 취약점을 참조하십시오 .

주입 방지 -mysql_real_escape_string ()

PHP에는 이러한 공격을 방지하기 위해 특별히 제작 된 기능이 있습니다. 당신이해야 할 일은 함수의 한 입을 사용하는 것 mysql_real_escape_string입니다.

mysql_real_escape_stringMySQL 쿼리에서 사용할 문자열을 가져 와서 모든 SQL 주입 시도가 안전하게 이스케이프 된 동일한 문자열을 반환합니다. 기본적으로 사용자가 입력 할 수있는 성가신 따옴표 ( ')를 MySQL 안전 대체물 인 이스케이프 된 따옴표 \'로 대체합니다.

참고 : 이 기능을 사용하려면 데이터베이스에 연결해야합니다!

// MySQL에 연결

$name_bad = "' OR 1'"; $name_bad = mysql_real_escape_string($name_bad); $query_bad = "SELECT * FROM customers WHERE username = '$name_bad'"; echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; $name_evil = mysql_real_escape_string($name_evil); $query_evil = "SELECT * FROM customers WHERE username = '$name_evil'"; echo "Escaped Evil Injection: <br />" . $query_evil;

MySQL-SQL Injection Prevention 에서 자세한 내용을 확인할 수 있습니다 .

470 KiranManiya Sep 13 2008 at 07:15

다음과 같이 기본적인 작업을 수행 할 수 있습니다.

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection); mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

이것은 모든 문제를 해결하지는 않지만 아주 좋은 디딤돌입니다. 변수의 존재, 형식 (숫자, 문자 등)을 확인하는 것과 같은 명백한 항목은 생략했습니다.

383 Rob Dec 08 2008 at 12:26

무엇을 사용하든 입력 내용이 이미 magic_quotes또는 다른 선의의 쓰레기로 인해 훼손되지 않았는지 확인하고 필요한 경우 실행 stripslashes하거나 소독 할 수 있습니다.

365 Cedric Jul 04 2011 at 04:50

Deprecated Warning : 이 답변의 샘플 코드 (예 : 질문의 샘플 코드)는 PHP MySQL확장을 사용합니다.이 확장은 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거되었습니다.

보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 SQL 주입을 방지하기 위해 불충분 , 사용 준비가 문을 대신. 귀하의 책임하에 아래에 설명 된 전략을 사용하십시오. (또한 mysql_real_escape_string()PHP 7에서 제거되었습니다.)

매개 변수가있는 쿼리 및 입력 유효성 검사를 사용하면됩니다. 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`");

두 경우 모두 '캡슐화를 보호하는 데 사용할 수 없습니다 .

출처 : 예상치 못한 SQL 인젝션 (이스케이프가 충분하지 않은 경우)

314 JohannesFahrenkrug Jul 03 2012 at 17:14

제 생각에는 PHP 애플리케이션 (또는 웹 애플리케이션)에서 일반적으로 SQL 주입을 방지하는 가장 좋은 방법은 애플리케이션의 아키텍처에 대해 생각하는 것입니다. SQL 인젝션으로부터 보호하는 유일한 방법이 데이터베이스와 대화 할 때마다 올바른 일을 수행하는 특수한 방법이나 함수를 사용하는 것임을 기억하는 것이라면 잘못된 것입니다. 이렇게하면 코드의 어느 시점에서 쿼리 형식을 올바르게 지정하는 것을 잊을 때까지 시간 문제 일뿐입니다.

MVC 패턴과 CakePHP 또는 CodeIgniter 와 같은 프레임 워크를 채택하는 것이 아마도 올바른 방법 일 것입니다. 보안 데이터베이스 쿼리 생성과 같은 일반적인 작업이 해결되고 이러한 프레임 워크에서 중앙에서 구현되었습니다. 웹 애플리케이션을 합리적인 방식으로 구성하고 단일 SQL 쿼리를 안전하게 구성하는 것보다 객체로드 및 저장에 대해 더 많이 생각할 수 있도록 도와줍니다.

303 ManishShrivastava Jul 23 2012 at 17:19

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

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 주입이 방지되지 않으면 매우 위험합니다.

302 Nikhil Nov 04 2009 at 01:05

보안 관점에서 저장 프로 시저 ( MySQL은 5.0부터 저장 프로 시저를 지원함 )를 선호 합니다. 장점은 다음과 같습니다.

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

단점은 다음과 같습니다.

  1. 그것들 (저장 절차)은 유지하기 어렵고 매우 빠르게 번식하는 경향이 있습니다. 이것은 그들을 관리하는 문제를 만듭니다.
  2. 동적 쿼리에는 적합하지 않습니다. 동적 코드를 매개 변수로 허용하도록 빌드 된 경우 많은 이점이 무효화됩니다.
268 RDK Oct 10 2012 at 21:53

누군가가 PHP와 MySQL 또는 다른 데이터베이스 서버를 사용하고 싶다면 다음과 같이 생각합니다.

  1. PDO (PHP Data Objects) 학습에 대해 생각해보십시오 . 여러 데이터베이스에 대한 균일 한 액세스 방법을 제공하는 데이터베이스 액세스 계층입니다.
  2. MySQLi 학습에 대해 생각해보십시오.
  3. strip_tags , mysql_real_escape_string 같은 네이티브 PHP 함수를 사용 하거나 변수가 숫자 인 경우 (int)$foo. 여기 에서 PHP의 변수 유형에 대해 자세히 알아보십시오 . 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 %입니다.

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

258 devOp Jun 12 2012 at 15:03

가능하면 매개 변수 유형을 캐스트하십시오. 그러나 int, bool 및 float와 같은 단순한 유형에서만 작동합니다.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
233 PeterMortensen Oct 15 2012 at 20:52

Redis 또는 Memcached 와 같은 캐시 엔진을 활용하려면 DALMP를 선택하는 것이 좋습니다. 순수 MySQLi를 사용합니다 . 이것을 확인하십시오 : PHP를 사용하는 MySQL 용 DALMP 데이터베이스 추상화 계층.

또한 쿼리를 준비하기 전에 인수를 '준비'하여 동적 쿼리를 작성하고 마지막에 완전히 준비된 명령문 쿼리를 가질 수 있습니다. PHP를 사용하는 MySQL 용 DALMP 데이터베이스 추상화 계층.

224 Xeoncross Sep 20 2012 at 01:10

( mysql_함수 에서 오는) PDO를 사용하는 방법을 잘 모르는 사람들을 위해 단일 파일 인 매우 간단한 PDO 래퍼 를 만들었습니다 . 응용 프로그램을 수행하는 데 필요한 모든 일반적인 작업을 수행하는 것이 얼마나 쉬운 지 보여주기 위해 존재합니다. PostgreSQL, MySQL 및 SQLite에서 작동합니다.

기본적으로, 그것을 읽고 당신이 매뉴얼 읽는 동안 간단 저장하고 형식의 값을 검색 할 수 있도록 실생활에서 사용하는 PDO 기능을 넣어하는 방법을보고 당신이 원하는합니다.

단일 열을 원합니다

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

나는 array (key => value) 결과를 원한다 (즉, 선택 상자를 만들기 위해)

$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));
223 NicolasFinelli Aug 04 2012 at 02:59

이 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
221 Danijel May 09 2013 at 23:36

SQL 문에서 특수 문자를 이스케이프하기위한 몇 가지 지침입니다.

MySQL을 사용하지 마십시오 . 이 확장은 더 이상 사용되지 않습니다. 대신 MySQLi 또는 PDO 를 사용하십시오 .

MySQLi

문자열에서 특수 문자를 수동으로 이스케이프하려면 mysqli_real_escape_string 함수를 사용할 수 있습니다 . mysqli_set_charset로 올바른 문자 집합을 설정하지 않으면 함수가 제대로 작동하지 않습니다 .

예:

$mysqli = new mysqli('host', 'user', 'password', 'database'); $mysqli->set_charset('charset');

$string = $mysqli->real_escape_string($string); $mysqli->query("INSERT INTO table (column) VALUES ('$string')");

준비된 명령문으로 값을 자동 이스케이프하려면 mysqli_prepare 및 mysqli_stmt_bind_param을 사용합니다. 여기서 적절한 변환을 위해 해당 바인드 변수의 유형을 제공해야합니다.

예:

$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)"); $stmt->bind_param("is", $integer, $string);

$stmt->execute();

준비된 문을 사용하든 또는를 사용하든 mysqli_real_escape_string항상 작업중인 입력 데이터의 유형을 알아야합니다.

따라서 준비된 문을 사용하는 경우 mysqli_stmt_bind_param함수 에 대한 변수 유형을 지정해야합니다 .

그리고의 사용은 mysqli_real_escape_string이름에서 알 수 있듯이 문자열에서 특수 문자를 이스케이프하기 때문에 정수를 안전하게 만들지 않습니다. 이 함수의 목적은 SQL 문에서 문자열이 끊어지는 것을 방지하고 이로 인해 발생할 수있는 데이터베이스 손상을 방지하는 것입니다. mysqli_real_escape_string적절하게 사용할 때, 특히 sprintf.

예:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0'; $query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer); echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999'; $query = sprintf("SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer); echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647
184 ApurvNerlekar Oct 25 2012 at 15:07

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

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

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

FLUSH PRIVILEGES; 

플러시 에 대한 자세한 정보 .

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

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

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

177 18revs,8users53%user1646111 Jan 29 2013 at 02:37

많은 유용한 답변과 관련 하여이 스레드에 가치를 더하고 싶습니다.

SQL 인젝션은 사용자 입력 (사용자가 입력 한 다음 쿼리 내에서 사용되는 입력)을 통해 수행 할 수있는 공격입니다. SQL 주입 패턴은 올바른 쿼리 구문이지만 다음과 같이 호출 할 수 있습니다. 나쁜 이유로 잘못된 쿼리이며, 보안의 세 가지 원칙 (기밀성)에 영향을 미치는 비밀 정보 (액세스 제어 우회)를 얻으려는 나쁜 사람이있을 수 있다고 가정합니다. , 무결성 및 가용성).

이제 우리의 요점은 SQL 인젝션 공격과 같은 보안 위협을 방지하는 것입니다. 질문 (PHP를 사용하여 SQL 인젝션 공격을 방지하는 방법),보다 현실적으로 사용자 입력 데이터를 내부에서 사용하는 경우 데이터 필터링 또는 입력 데이터 지우기가 있습니다. PHP 또는 다른 프로그래밍 언어를 사용하는 그러한 쿼리는 그렇지 않거나 준비된 명령문 또는 현재 SQL 주입 방지를 지원하는 다른 도구와 같은 최신 기술을 사용하도록 더 많은 사람들이 권장하는 것처럼 이러한 도구를 더 이상 사용할 수 없다고 생각하십니까? 애플리케이션을 어떻게 보호합니까?

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

데이터 필터링 (안전하지 않은 데이터를 안전한 데이터로 변환)

것을 고려 PDO 와 MySQLi는 사용할 수 없습니다. 애플리케이션을 어떻게 보호 할 수 있습니까? 강제로 사용합니까? PHP 이외의 다른 언어는 어떻습니까? 특정 언어뿐만 아니라 더 넓은 경계에 사용할 수있는 일반적인 아이디어를 제공하는 것을 선호합니다.

  1. SQL 사용자 (사용자 권한 제한) : 가장 일반적인 SQL 작업은 (SELECT, UPDATE, INSERT)입니다. 그러면 필요하지 않은 사용자에게 UPDATE 권한을 부여하는 이유는 무엇입니까? 예를 들어 로그인 및 검색 페이지 는 SELECT 만 사용하고 있는데,이 페이지에서 높은 권한을 가진 DB 사용자를 사용하는 이유는 무엇입니까?

규칙 : 모든 권한에 대해 하나의 데이터베이스 사용자를 생성하지 마십시오. 모든 SQL 작업에 대해 쉽게 사용할 수 있도록 (deluser, selectuser, updateuser)와 같은 스키마를 사용자 이름으로 만들 수 있습니다.

최소 권한 원칙을 참조하십시오 .

  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입니다. 이러한 단어가 감지되면 입력을 피할 수 있습니다.

업데이트 1 :

사용자가이 게시물이 쓸모 없다고 댓글을 달았습니다. 다음은 OWASP.ORG가 제공 한 것입니다 .

기본 방어 :

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

추가 방어 :

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

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

업데이트 2 :

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

이스케이프 및 SQL 주입

바인딩 된 변수는 서버에 의해 자동으로 이스케이프됩니다. 서버는 실행 전에 이스케이프 된 값을 적절한 위치에 문 템플릿에 삽입합니다. 적절한 변환을 만들려면 바인딩 된 변수 유형에 대한 힌트를 서버에 제공해야합니다. 자세한 내용은 mysqli_stmt_bind_param () 함수를 참조하십시오.

서버 내에서 값의 자동 이스케이프는 때때로 SQL 주입을 방지하는 보안 기능으로 간주됩니다. 입력 값이 올바르게 이스케이프되면 준비되지 않은 문으로 동일한 수준의 보안을 얻을 수 있습니다.

업데이트 3 :

준비된 문을 사용할 때 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

준비된 진술이 데이터를 이스케이프한다는 것은 분명합니다.

위의 진술에서도 언급했듯이

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

따라서 이것은 intval()쿼리를 보내기 전에 정수 값에 대한 데이터 유효성 검사 가 좋은 아이디어 임을 증명 합니다. 또한 쿼리를 보내기 전에 악의적 인 사용자 데이터를 방지 하는 것은 정확하고 유효한 방법 입니다.

자세한 내용은이 질문을 참조하십시오 : PDO는 원시 쿼리를 MySQL에 보내고 Mysqli는 준비된 쿼리를 보내며 둘 다 동일한 결과를 생성합니다.

참고 문헌 :

  1. SQL 인젝션 치트 시트
  2. SQL 주입
  3. 정보 보안
  4. 보안 원칙
  5. 데이터 유효성 검사
176 SoumalyaBanerjee Sep 14 2012 at 21:37

보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 SQL 주입을 방지하기 위해 불충분 , 사용 준비가 문을 대신. 귀하의 책임하에 아래에 설명 된 전략을 사용하십시오. (또한 mysql_real_escape_string()PHP 7에서 제거되었습니다.)

Deprecated Warning : mysql 확장은 현재 사용되지 않습니다. PDO 확장을 사용하는 것이 좋습니다.

웹 애플리케이션이 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";

이 질문 에는 이에 대한 좋은 답변이 있습니다.

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");
170 DeepakThomas Jun 26 2013 at 02:00

간단한 방법은 필터링 및 활성 레코드와 같은 기능이 내장 된 CodeIgniter 또는 Laravel 과 같은 PHP 프레임 워크를 사용하여 이러한 뉘앙스에 대해 걱정할 필요가 없습니다.

147 5ervant Apr 04 2013 at 15:15

경고 :이 답변에 설명 된 접근 방식은 매우 구체적인 시나리오에만 적용되며 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 주소 또는 심지어 그들의 쿠키, 기록, 브라우저 또는 기타 민감한 정보를 저장할 페이지 로 재 작성하거나 리디렉션 (사용자에 따라 다름) 하는 팁을 제공합니다. 계정을 금지하거나 당국에 연락하여 나중에 처리 할 수 ​​있습니다.

129 ThomasAhle Mar 20 2014 at 07:17

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 삽입뿐만 아니라 구문 오류도 방지합니다! 또한 한 번에 여러 결과를 필터링하거나 여러 연결에 적용하기 위해 메서드 체인을 사용하는 모델 컬렉션을 지원합니다.

128 ChintanGor Jan 30 2014 at 14:00

PHP 및 MySQL에 대한 답변은 매우 많지만 여기에는 SQL 주입 및 oci8 드라이버의 일반적인 사용을 방지 하기위한 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);
124 RakeshSharma Jan 17 2014 at 13:25

Deprecated Warning : 이 답변의 샘플 코드 (예 : 질문의 샘플 코드)는 PHP MySQL확장을 사용합니다.이 확장은 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거되었습니다.

보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 SQL 주입을 방지하기 위해 불충분 , 사용 준비가 문을 대신. 귀하의 책임하에 아래에 설명 된 전략을 사용하십시오. (또한 mysql_real_escape_string()PHP 7에서 제거되었습니다.)

PDO 및 MYSQLi를 사용 하는 것은 SQL 주입을 방지하는 좋은 방법이지만 MySQL 함수 및 쿼리로 작업하고 싶다면 사용하는 것이 좋습니다.

mysql_real_escape_string

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

이를 방지 할 수있는 더 많은 기능이 있습니다. 식별과 같이 입력이 문자열, 숫자, 문자 또는 배열 인 경우이를 감지하는 내장 함수가 너무 많습니다. 또한 이러한 기능을 사용하여 입력 데이터를 확인하는 것이 좋습니다.

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.

87 Calmarius Feb 19 2014 at 03:38

몇 년 전에이 작은 함수를 작성했습니다.

function sqlvprintf($query, $args)
{
    global $DB_LINK; $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array(); foreach ($args as $value) { if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'"; } else if (is_null($value))
        {
            $value = 'NULL'; } else if (!is_int($value) && !is_float($value)) { die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.'); } $values[] = $value; $ctr++;
    }
    $query = preg_replace_callback( '/{(\\d+)}/', function($match) use ($values) { if (isset($values[$match[1]])) { return $values[$match[1]]; } else { return $match[0];
            }
        },
        $query ); return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/) { $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results. return $results;
}

이렇게하면 다음과 같이 한 줄짜리 C # -ish String.Format에서 문을 실행할 수 있습니다.

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

변수 유형을 고려하여 이스케이프합니다. 테이블, 열 이름을 매개 변수화하려고하면 잘못된 구문 인 따옴표 안에 모든 문자열을 넣으므로 실패합니다.

보안 업데이트 : 이전 str_replace버전에서는 사용자 데이터에 {#} 토큰을 추가하여 주입을 허용했습니다. 이 preg_replace_callback버전은 교체에 이러한 토큰이 포함되어 있으면 문제를 일으키지 않습니다.