$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;--')
이런 일이 일어나지 않도록 하려면 어떻게 해야 합니까?
어떤 데이터베이스를 사용하든 SQL 주입 공격을 피하는 올바른방법은 데이터를 SQL에서 분리하여 데이터 가 데이터로 유지되고 SQL 파서에서 명령 으로 해석되지 않도록 하는 것입니다. 올바른 형식의 데이터 부분으로 SQL 문을 생성할 수 있지만 세부 사항을 완전히이해하지 못하는 경우 항상 준비된 문과 매개 변수화된 쿼리를 사용해야 합니다. 이들은 매개변수와 별도로 데이터베이스 서버로 전송 및 구문 분석되는 SQL 문입니다. 이렇게 하면 공격자가 악성 SQL을 주입하는 것이 불가능합니다.
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name'); $stmt->execute([ 'name' => $name ]); foreach ($stmt as $row) { // Do something with $row }
$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', 'password'); $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
위의 예에서 오류 모드는 꼭 필요한 것은 아니지만 추가하는 것이 좋습니다 . 이런 식으로 스크립트는 문제가 발생했을 때 Fatal ErrorPDOExceptionthrow 되는 오류 catch 수 있는 기회를 제공합니다.
그러나 필수 사항 은 첫 번째 setAttribute() 행으로, 이는 PDO에게 에뮬레이트된 준비된 명령문을 비활성화하고 실제 준비된 명령문을 사용하도록 지시합니다. 이렇게 하면 MySQL 서버로 보내기 전에 명령문과 값이 PHP에서 구문 분석되지 않도록 합니다(공격자에게 악의적인 SQL을 주입할 기회를 주지 않음).
prepare 하기 위해 전달한 SQL 문은 데이터베이스 서버에서 구문 분석되고 컴파일됩니다. 매개 변수를 지정하여 (중 하나 ? 나 같은 명명 된 매개 변수 :name 위의 예에서) 당신이 필터링 할 위치를 데이터베이스 엔진을 말한다. 그런 다음 execute 를 호출하면 준비된 명령문이 지정한 매개변수 값과 결합됩니다.
여기서 중요한 것은 매개변수 값이 SQL 문자열이 아니라 컴파일된 문과 결합된다는 것입니다. SQL 인젝션은 스크립트가 데이터베이스에 보낼 SQL을 생성할 때 악성 문자열을 포함하도록 속임으로써 작동합니다. 따라서 매개변수와 별도로 실제 SQL을 보내면 의도하지 않은 결과가 발생할 위험을 제한할 수 있습니다.
준비된 명령문을 사용할 때 보내는 모든 매개변수는 문자열로 처리됩니다(데이터베이스 엔진이 일부 최적화를 수행할 수 있으므로 매개변수도 숫자로 끝날 수 있음). 위의 예에서 $name'Sarah'; DELETE FROM employees 가 포함되어 있으면; 'Sarah'; DELETE FROM employees"'Sarah'; DELETE FROM employees" 문자열에 대한 검색 이 될 것이며 빈 테이블로 끝나지는 않을 것입니다.
준비된 명령문을 사용하는 또 다른 이점은 동일한 세션에서 동일한 명령문을 여러 번 실행하는 경우 한 번만 구문 분석되고 컴파일되므로 속도가 약간 향상된다는 것입니다.
아, 그리고 삽입을 위해 그것을 하는 방법에 대해 물었으므로 다음은 (PDO 사용) 예입니다:
쿼리 매개변수에 대해 준비된 명령문을 계속 사용할 수 있지만 동적 쿼리 자체의 구조는 매개변수화할 수 없으며 특정 쿼리 기능은 매개변수화할 수 없습니다.
이러한 특정 시나리오의 경우 가장 좋은 방법은 가능한 값을 제한하는 화이트리스트 필터를 사용하는 것입니다.
// Value whitelist // $dir can only be 'DESC', otherwise it will be 'ASC' if (empty($dir) || $dir !== 'DESC') { $dir = 'ASC'; }
Community Wiki
더 이상 사용되지 않는 경고: 이 답변의 샘플 코드(질문 샘플 코드와 같은)는 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거된 MySQL
보안 경고 : 이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프는 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 확장으로 변경해야 합니다.
<?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(); ?>
귀하가 문의한 사례는 상당히 간단한 사례이며 보다 복잡한 사례는 보다 복잡한 접근 방식이 필요할 수 있습니다. 특히:
사용자 입력을 기반으로 SQL의 구조를 변경하려는 경우 매개변수화된 쿼리는 도움이 되지 않으며 필요한 이스케이프는 mysql_real_escape_string 다루지 않습니다. 이러한 종류의 경우 '안전한' 값만 허용되도록 허용 목록을 통해 사용자 입력을 전달하는 것이 좋습니다.
조건에서 사용자 입력의 정수를 사용하고 mysql_real_escape_string 접근 방식을 취하면 아래 주석에서 다항식 에서 설명한 문제를 겪을 것입니다. 이 경우는 정수가 따옴표로 묶이지 않기 때문에 더 까다롭습니다. 따라서 사용자 입력에 숫자만 포함되어 있는지 확인하여 처리할 수 있습니다.
내가 모르는 다른 경우가 있을 수 있습니다. 이것은 당신이 직면할 수 있는 좀 더 미묘한 문제에 대한 유용한 리소스라는 것을 알게 될 것입니다.
Community Wiki
여기에 있는 모든 답변은 문제의 일부만 다룹니다. 실제로 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개 이상의 삭제된 답변 이 있습니다(대부분의 방문자에게는 표시되지 않음). 모두 품질이 좋지 않거나 좋지 않은 관행을 조장하기 때문에 커뮤니티에서 삭제했습니다. 설상가상으로 나쁜 답변 중 일부는 삭제되지 않고 오히려 번성하고 있습니다.
또는 문자열 형식 지정의 또 다른 방법 을 제안하고 궁극적인 만병 통치약으로 자랑하는 약간 더 나은 답변이 있습니다. 물론 그렇지 않습니다. 이 방법은 일반 문자열 형식화보다 좋지는 않지만 모든 단점을 유지합니다. 문자열에만 적용할 수 있으며 다른 수동 형식화와 마찬가지로 본질적으로 선택적이며 필수가 아니며 모든 종류의 사람이 실수하기 쉽습니다.
나는 이 모든 것이 "탈출"과 SQL 주입으로부터의 보호 사이의 평등을 선언하는 OWASP 또는 PHP 매뉴얼 과 같은 기관에서 지원하는 하나의 아주 오래된 미신 때문이라고 생각합니다.
PHP 매뉴얼이 오랜 세월 동안 말한 것과 상관없이 *_escape_string 은 결코 데이터를 안전하게 만들지 않으며 의도한 적이 없습니다. 문자열 이외의 SQL 부분에는 쓸모가 없다는 것 외에도 수동 이스케이프는 자동 이스케이프와 반대로 수동이기 때문에 잘못되었습니다.
그리고 OWASP는 사용자 입력 을 피하는 것을 강조하여 상황을 더욱 악화시킵니다. 이는 완전히 넌센스입니다. 주입 보호의 맥락에서 그런 단어는 없어야 합니다. 모든 변수는 잠재적으로 위험합니다. 출처에 상관없이! 또는 다시 말해, 모든 변수는 소스에 관계없이 쿼리에 입력할 수 있도록 적절한 형식을 지정해야 합니다. 중요한 것은 목적지입니다. 개발자가 양과 염소를 분리하기 시작하는 순간(특정 변수가 "안전한"지 여부를 생각함) 재난을 향한 첫 걸음을 내딛습니다. 문구조차 진입점에서 대량 이스케이프를 제안하는 것은 말할 것도 없고, 이미 경멸되고, 더 이상 사용되지 않으며 제거된 마법의 따옴표 기능과 유사합니다.
따라서 "이스케이프"와 달리 준비된 명령문 은 실제로 SQL 주입(해당되는 경우)으로부터 보호하는 수단입니다.
Community Wiki
매개변수화된 SQL 쿼리를 실행하려면 PDO (PHP Data Objects)를 사용하는 것이 좋습니다.
이것은 SQL 주입으로부터 보호할 뿐만 아니라 쿼리 속도를 높입니다.
mysql_ , mysqli_ 및 pgsql_ 함수 대신 PDO를 사용하면 데이터베이스 공급자를 전환해야 하는 드문 경우이지만 애플리케이션을 데이터베이스에서 조금 더 추상화할 수 있습니다.
보시다시피, 사람들은 기껏해야 준비된 진술을 사용하도록 제안합니다. 잘못된 것은 아니지만 쿼리가 프로세스당 한 번만 실행되면 약간의 성능 저하가 발생합니다.
이 문제에 직면했지만 해커가 따옴표 사용을 피하기 위해 사용하는 방식인 매우 정교한 방식으로 해결했다고 생각합니다. 에뮬레이트된 준비된 문과 함께 이것을 사용했습니다. 가능한 모든 종류의 SQL 주입 공격을 방지하는 데 사용합니다.
내 접근 방식:
입력이 정수일 것으로 예상하는 경우 실제로 정수인지 확인하십시오. PHP와 같은 변수 유형 언어에서는 이것이 매우 중요합니다. 예를 들어 다음과 같이 매우 간단하지만 강력한 솔루션을 사용할 수 있습니다. sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
정수 16 진수 에서 다른 것을 기대하면 . 16진수로 하면 모든 입력을 완벽하게 피할 수 있습니다. C/C++에는 mysql_hex_string() 이라는 함수가 있으며 PHP에서는 bin2hex() 사용할 수 있습니다.
mysql_real_escape_string 을 사용하더라도 PHP는 동일한 용량 ((2*input_length)+1) 을 할당해야 하기 때문에 이스케이프된 문자열이 원래 길이의 2배 크기를 가질 것이라는 점에 대해 걱정하지 마십시오.
이 16진수 방법은 바이너리 데이터를 전송할 때 자주 사용되지만 SQL 주입 공격을 방지하기 위해 모든 데이터에 사용하지 않을 이유가 없습니다. 데이터 앞에 0x 를 추가하거나 대신 MySQL 함수 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 접두사의 차이점
댓글에서 약간의 토론이 있었고, 그래서 나는 마침내 그것을 분명히 하고 싶습니다. 이 두 가지 접근 방식은 매우 유사하지만 몇 가지 면에서 약간 다릅니다.
0xchar , 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 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');
Community Wiki
더 이상 사용되지 않는 경고: 이 답변의 샘플 코드(질문 샘플 코드와 같은)는 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거된 MySQL
보안 경고 : 이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프는 SQL 주입을 방지하기에 부적절 하므로 준비된 명령문을 대신 사용하십시오. 아래에 설명된 전략을 사용하는 데 따른 위험은 자신이 감수해야 합니다. (또한 mysql_real_escape_string() 은 PHP 7에서 제거되었습니다.)
중요한
SQL 주입을 방지하는 가장 좋은 방법 은 허용되는 답변 에서 알 수 있듯이 이스케이프 대신준비된 문 을 사용하는 것입니다.
이것이 모든 문제를 해결하지는 못하지만 매우 좋은 디딤돌입니다. 변수의 존재 여부, 형식(숫자, 문자 등) 확인 등 눈에 띄는 항목은 생략했습니다.
Community Wiki
결국 사용하는 것이 무엇이든 입력이 이미 magic_quotes 또는 다른 좋은 의미의 쓰레기에 stripslashes 또는 무엇이든 처리하여 삭제하십시오.
Community Wiki
더 이상 사용되지 않는 경고: 이 답변의 샘플 코드(질문 샘플 코드와 같은)는 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거된 MySQL
보안 경고 : 이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프는 SQL 주입을 방지하기에 부적절 하므로 준비된 명령문을 대신 사용하십시오. 아래에 설명된 전략을 사용하는 데 따른 위험은 자신이 감수해야 합니다. (또한 mysql_real_escape_string() 은 PHP 7에서 제거되었습니다.)
매개변수화된 쿼리 및 입력 유효성 검사가 올바른 방법입니다. mysql_real_escape_string() 이 사용되었음에도 불구하고 SQL 주입이 발생할 수 있는 많은 시나리오가 있습니다.
제 생각에는 일반적으로 PHP 애플리케이션(또는 그 문제에 대한 모든 웹 애플리케이션)에서 SQL 주입을 방지하는 가장 좋은 방법은 애플리케이션의 아키텍처에 대해 생각하는 것입니다. SQL 주입으로부터 보호하는 유일한 방법이 데이터베이스와 대화할 때마다 올바른 작업을 수행하는 특별한 방법이나 함수를 사용하는 것을 기억하는 것이라면 잘못하고 있는 것입니다. 그렇게 하면 코드의 특정 지점에서 쿼리 형식을 올바르게 지정하는 것을 잊어버릴 때까지 시간 문제입니다.
MVC 패턴과 CakePHP 또는 CodeIgniter 와 같은 프레임워크를 채택하는 것이 아마도 올바른 방법일 것입니다. 보안 데이터베이스 쿼리 생성과 같은 일반적인 작업이 이러한 프레임워크에서 해결되고 중앙에서 구현되었습니다. 웹 애플리케이션을 합리적인 방식으로 구성하고 단일 SQL 쿼리를 안전하게 구성하는 것보다 객체 로드 및 저장에 대해 더 많이 생각할 수 있도록 도와줍니다.
대부분의 데이터베이스( MySQL 포함)에서는 저장 프로시저 실행으로 사용자 액세스를 제한할 수 있습니다. 세분화된 보안 액세스 제어는 권한 공격의 확대를 방지하는 데 유용합니다. 이렇게 하면 손상된 응용 프로그램이 데이터베이스에 대해 SQL을 직접 실행할 수 없습니다.
응용 프로그램에서 원시 SQL 쿼리를 추상화하므로 응용 프로그램에서 사용할 수 있는 데이터베이스 구조 정보가 줄어듭니다. 이것은 사람들이 데이터베이스의 기본 구조를 이해하고 적절한 공격을 설계하는 것을 어렵게 만듭니다.
매개변수만 허용하므로 매개변수화된 쿼리의 장점이 있습니다. 물론 - IMO는 여전히 입력을 삭제해야 합니다. 특히 저장 프로시저 내에서 동적 SQL을 사용하는 경우에는 더욱 그렇습니다.
단점은 -
저장 프로시저(저장 프로시저)는 유지 관리하기 어렵고 매우 빠르게 증식하는 경향이 있습니다. 이것은 그들을 관리하는 것을 문제로 만듭니다.
동적 쿼리에는 적합하지 않습니다. 동적 코드를 매개변수로 허용하도록 구축된 경우 많은 이점이 무효화됩니다.
Community Wiki
SQL 인젝션 및 기타 SQL 해킹을 방지하는 방법에는 여러 가지가 있습니다. 인터넷(구글 검색)에서 쉽게 찾을 수 있습니다. 물론 PDO는 좋은 솔루션 중 하나입니다. 하지만 SQL 인젝션에서 좋은 링크 방지 몇 가지를 제안하고 싶습니다.
mysql_ 함수에서 가져옴)를 사용하는 방법을 잘 모르는 사람들을 위해 단일 파일 인 매우 간단한 PDO 래퍼 를 만들었습니다. 응용 프로그램에서 수행해야 하는 모든 일반적인 작업을 수행하는 것이 얼마나 쉬운지 보여주기 위해 존재합니다. PostgreSQL, MySQL 및 SQLite와 함께 작동합니다.
기본적으로, 그것을 읽고 당신이 매뉴얼 읽는 동안 간단 저장하고 원하는 형식의 값을 검색 할 수 있도록 실생활에서 사용하는 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));
Community Wiki
SQL 문에서 특수 문자를 이스케이프하기 위한 몇 가지 지침입니다.
MySQL을 사용하지 마십시오. 이 확장은 더 이상 사용되지 않습니다. 대신 MySQLi 또는 PDO 를 사용하십시오.
준비된 명령문을 사용하든 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
Community Wiki
이 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
Community Wiki
이 문제에 대한 간단한 대안은 데이터베이스 자체에서 적절한 권한을 부여하여 해결할 수 있습니다. 예: MySQL 데이터베이스를 사용하는 경우 터미널 또는 제공된 UI를 통해 데이터베이스에 입력하고 다음 명령을 따르십시오.
GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';
이렇게 하면 사용자가 지정된 쿼리로만 제한됩니다. 삭제 권한을 제거하면 PHP 페이지에서 실행된 쿼리에서 데이터가 삭제되지 않습니다. 두 번째로 해야 할 일은 권한을 플러시하여 MySQL이 권한과 업데이트를 새로 고치도록 하는 것입니다.
SQL 인젝션은 사용자 입력(사용자가 채우고 쿼리 내에서 사용하는 입력)을 통해 수행할 수 있는 공격입니다. SQL 주입 패턴은 올바른 쿼리 구문이지만 다음과 같이 부를 수 있습니다. 나쁜 이유에 대한 나쁜 쿼리, 우리는 보안의 세 가지 원칙(기밀성 , 무결성 및 가용성).
이제 우리의 요점은 SQL 인젝션 공격과 같은 보안 위협을 방지하는 것입니다. 질문(PHP를 사용하여 SQL 인젝션 공격을 방지하는 방법), 보다 현실적, 데이터 필터링 또는 입력 데이터 삭제는 내부에서 사용자 입력 데이터를 사용하는 경우입니다. PHP 또는 기타 프로그래밍 언어를 사용하는 이러한 쿼리는 해당되지 않거나 더 많은 사람들이 준비된 명령문 또는 현재 SQL 주입 방지를 지원하는 기타 도구와 같은 현대 기술을 사용하도록 권장하는 대로 이러한 도구를 더 이상 사용할 수 없다고 생각하십니까? 애플리케이션을 어떻게 보호합니까?
SQL 주입에 대한 나의 접근 방식은 사용자 입력 데이터를 데이터베이스로 보내기 전에 지우는 것입니다(쿼리 내에서 사용하기 전에).
데이터 필터링(안전하지 않은 데이터를 안전한 데이터로 변환)
PDO 및 MySQLi 를 사용할 수 없음을 고려하십시오. 애플리케이션을 어떻게 보호할 수 있습니까? 당신은 내가 그들을 사용하도록 강요합니까? PHP 이외의 다른 언어는 어떻습니까? 특정 언어뿐만 아니라 더 넓은 경계에 사용할 수 있으므로 일반적인 아이디어를 제공하는 것을 선호합니다.
SQL 사용자(사용자 권한 제한): 가장 일반적인 SQL 작업은 (SELECT, UPDATE, INSERT)인데 왜 필요하지 않은 사용자에게 UPDATE 권한을 부여합니까? 예를 들어 로그인 페이지와 검색 페이지 는 SELECT만 사용하고 있는데 왜 이 페이지에서 높은 권한을 가진 DB 사용자를 사용합니까?
규칙: 모든 권한에 대해 하나의 데이터베이스 사용자를 생성하지 마십시오. 모든 SQL 작업에 대해 쉽게 사용할 수 있도록 사용자 이름으로 (deluser, selectuser, updateuser)와 같은 체계를 만들 수 있습니다.
데이터 필터링: 쿼리 사용자 입력을 작성하기 전에 유효성을 검사하고 필터링해야 합니다. 프로그래머에게는 데이터 유형, 데이터 패턴 및 데이터 길이와 같은 각 사용자 입력 변수에 대한 몇 가지 속성을 정의하는 것이 중요합니다. (x와 y) 사이의 숫자인 필드는 정확한 규칙을 사용하여 정확하게 검증되어야 하며 문자열(텍스트)인 필드의 경우 패턴이 해당됩니다. 예를 들어 사용자 이름에는 일부 문자만 포함되어야 합니다. [a-zA-Z0-9_-.]라고 말합니다. 길이는 (x와 n) 사이에서 다양합니다. 여기서 x와 n(정수, x <=n)입니다. 규칙: 정확한 필터 및 유효성 검사 규칙을 만드는 것이 가장 좋습니다.
다른 도구 사용: 여기에서는 또한 준비된 명령문(매개변수화된 쿼리) 및 저장 프로시저에 동의합니다. 여기서 단점은 이러한 방법을 사용하려면 대부분의 사용자에게 존재하지 않는 고급 기술이 필요하다는 것입니다. 여기서 기본 개념은 SQL 쿼리와 내부에서 사용되는 데이터를 구별하는 것입니다. 여기에 있는 사용자 입력 데이터는 (any 또는 x=x)와 같은 원래 쿼리에 아무것도 추가하지 않기 때문에 두 접근 방식 모두 안전하지 않은 데이터에도 사용할 수 있습니다.
바인딩된 변수는 서버에서 자동으로 이스케이프됩니다. 서버는 실행 전에 명령문 템플릿의 적절한 위치에 이스케이프된 값을 삽입합니다. 적절한 변환을 생성하려면 바인딩된 변수의 유형에 대한 힌트를 서버에 제공해야 합니다. 자세한 정보는 mysqli_stmt_bind_param() 함수를 참조하십시오.
서버 내에서 값의 자동 이스케이프는 SQL 주입을 방지하기 위한 보안 기능으로 간주되는 경우가 있습니다. 입력 값이 올바르게 이스케이프되면 준비되지 않은 명령문에서도 동일한 수준의 보안을 달성할 수 있습니다.
업데이트 3:
준비된 명령문을 사용할 때 PDO와 MySQLi가 MySQL 서버에 쿼리를 보내는 방법을 알기 위한 테스트 케이스를 만들었습니다.
보안 경고 : 이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프는 SQL 주입을 방지하기에 부적절 하므로 준비된 명령문을 대신 사용하십시오. 아래에 설명된 전략을 사용하는 데 따른 위험은 자신이 감수해야 합니다. (또한 mysql_real_escape_string() 은 PHP 7에서 제거되었습니다.)
더 이상 사용되지 않는 경고 : mysql 확장은 현재 사용되지 않습니다. PDO 확장을 사용하는 것이 좋습니다.
내 웹 응용 프로그램이 SQL 주입에 취약하지 않도록 방지하기 위해 세 가지 다른 방법을 사용합니다.
PHP 에서 미리 정의된 함수인 mysql_real_escape_string()\x00 , \n , \r , \ , ' , " 및 \x1a 문자에 백슬래시를 추가합니다. 입력 값을 매개변수로 전달합니다. SQL 인젝션의 가능성을 최소화합니다.
가장 발전된 방법은 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";
1=1 , 2=2 , 1=2 , 2=1 , 1+1=2 등은 공격자의 SQL 데이터베이스에 대한 일반적인 질문이기 때문입니다. 또한 많은 해킹 응용 프로그램에서 사용됩니다.
그러나 사이트에서 안전한 쿼리를 다시 작성하지 않도록 주의해야 합니다. 위의 코드는 해킹 관련 동적 쿼리 문자열을 공격자의 IP 주소 또는 쿠키, 기록, 브라우저 또는 기타 민감한 정보를 저장할 페이지로 다시 작성하거나 리디렉션(귀하에게 따라 다름) 하는 팁을 제공합니다. 나중에 해당 계정을 차단하거나 당국에 연락하여 처리할 수 있습니다.
SQL 주입뿐만 아니라 구문 오류도 방지합니다! 또한 한 번에 여러 결과 및 여러 연결에 작업을 필터링하거나 적용하기 위해 메서드 체인을 사용하여 모델 컬렉션을 지원합니다.
Community Wiki
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);
Community Wiki
더 이상 사용되지 않는 경고: 이 답변의 샘플 코드(질문 샘플 코드와 같은)는 PHP 5.5.0에서 더 이상 사용되지 않고 PHP 7.0.0에서 완전히 제거된 MySQL
보안 경고 : 이 답변은 보안 모범 사례와 일치하지 않습니다. 이스케이프는 SQL 주입을 방지하기에 부적절 하므로 준비된 명령문을 대신 사용하십시오. 아래에 설명된 전략을 사용하는 데 따른 위험은 자신이 감수해야 합니다. (또한 mysql_real_escape_string() 은 PHP 7에서 제거되었습니다.)
PDO 와 MYSQLi를 사용하는 것은 SQL 인젝션을 방지하는 좋은 방법이지만 실제로 MySQL 기능 및 쿼리로 작업하려면 다음을 사용하는 것이 좋습니다.