php - 해킹 - real_escape_string




PHP에서 SQL 삽입을 방지하려면 어떻게해야합니까? (19)

경고 : 이 답변의 샘플 코드 (질문의 샘플 코드와 같은)는 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 문제는 발생할 수있는보다 미세한 문제에 대한 유용한 자료입니다.

https://code.i-harness.com

사용자 입력을 수정하지 않고 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;--')

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


보안 경고 :이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프 처리는 SQL 삽입을 방지하기에는 부적절합니다 . 대신 준비된 명령문을 사용하십시오. 아래에 설명 된 전략을 따른다는 위험 부담이 있습니다. (또한 mysql_real_escape_string() 은 PHP 7에서 삭제되었습니다.)

다음과 같이 기본적인 것을 할 수 있습니다 :

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

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


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

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

단점은 -

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

당신이 무엇을 사용하든간에, magic_quotes 나 다른 의미있는 쓰레기에 의해 입력이 아직 훼손되지 않았는지 확인하고, 필요하다면 stripslashes 또는 위생 처리를 위해 실행하십시오.


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

  • 식별자
  • 구문 키워드

그리고 준비된 진술은 그 중 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가 있습니다 . 여기서 모든 문제를 자세히 설명하고 나쁜 관행과 공개에 전적으로 헌신하는 섹션을 작성했습니다.


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

나는이 문제에 직면 해 있었지만 해커들이 따옴표 사용을 피하는 방법과 같이 매우 정교한 방법으로 해결했다고 생각합니다. 필자는 에뮬레이트 된 준비 문과 함께이 기능을 사용했습니다. 저는 모든 종류의 가능한 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')


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

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

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


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

( $connPDO 객체입니다)

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

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')" );

Prepared 문을 사용하여 값을 자동으로 이스케이프하려면 mysqli_preparemysqli_stmt_bind_param 여기서 적절한 바인드 변수의 유형은 적절한 변환을 위해 제공되어야합니다.

예:

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

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

prepared statement 나 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

나는 누군가가 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 주입에 취약하지 않도록 세 가지 방법을 사용합니다.

  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 주입 공격은 주입 할 수있는 것에 의존하지 않으므로 안전하지 않습니다 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 주소를 그들의 쿠키, 역사, 브라우저를하거나, 또는 민감한 다른 정보를 제공하므로 나중에 자신의 계정을 금지하거나 관계 당국에 연락하여 대응할 수 있습니다.


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.


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);

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


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

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

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

많은 유용한 답변과 관련하여이 스레드에 몇 가지 값을 추가하겠습니다. 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. 데이터 유효성 검사

이 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

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





sql-injection