2780

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

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

28 답변


8220

준비된 문과 매개 변수화 된 쿼리를 사용하십시오.이것은 매개 변수와 별도로 데이터베이스 서버로 보내고 구문 분석되는 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 이외의 데이터베이스에 연결하는 경우 참조 할 수있는 드라이버 관련 두 번째 옵션이 있습니다 (예 :pg_prepare()pg_execute()PostgreSQL 용). PDO는 보편적 인 옵션입니다.

제대로 연결 설정

사용시PDOMySQL 데이터베이스에 액세스하는 방법레알준비된 진술은기본적으로 사용되지 않음. 이 문제를 해결하려면 준비된 문장의 에뮬레이션을 비활성화해야합니다. 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뭔가 잘못되었을 때. 그리고 개발자에게catch모든 오류 (들)thrown으로PDOException에스.

무엇입니까의무적 인그러나, 첫 번째입니다setAttribute()PDO에게 에뮬레이션 된 준비된 명령문을 사용하지 않도록 설정하고 사용하도록 지시합니다.레알준비된 진술. 이렇게하면 문과 값이 PHP에 의해 구문 분석되어 MySQL 서버로 전송되지 않습니다 (공격자가 악성 SQL을 삽입 할 가능성이 없음).

설정은 가능하지만charset생성자의 옵션에서 PHP의 이전 버전 (<5.3.6)charset 매개 변수를 자동으로 무시함DSN에

설명

무슨 일이 일어날 지 당신이 전달하는 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(array('column' => $unsafeValue));

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

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

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

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


  • 여기에 다른 곳을 보지 못했기 때문에 추가하기 만하면됩니다. 또 다른 방어선은웹 응용 프로그램 방화벽(WAF)는 SQL 주입 공격을 찾기 위해 규칙을 설정할 수 있습니다. - jkerak
  • 또한, mysql_query의 공식 문서는 하나의 쿼리 만 실행할 수 있으므로 다른 쿼리도 수행 할 수 있습니다. 무시됩니다. 이미이 기능이 더 이상 사용되지 않지만 PHP 5.5.0에는 많은 시스템이 있으며이 기능을 사용할 수 있습니다.php.net/manual/en/function.mysql-query.php - Randall Valenciano
  • 이것은 나쁜 습관이지만 문제가있는 솔루션입니다. SQL 주입뿐만 아니라 모든 유형의 주입 (예 : F3 프레임 워크 v2에 뷰 템플릿 주입 구멍이 있음)에 대한 준비가 오래된 웹 사이트 또는 응용 프로그램이있는 경우 주입 불량에서 한 가지 해결책은 $ _POST와 같은 사전 전역 정의 된 값의 값을 부트 스트랩에서 이스케이프 된 값으로 재 할당하는 것입니다. PDO에 의해 여전히 substr ($ pdo- & gt; quote ($ str, \ PDO :: PARAM_STR), 1, -1)을 이스케이프 할 수 있습니다. - Alix
  • 이 대답은 준비된 성명서에 대한 설명이 부족합니다. 요청 중 준비된 성명서를 많이 사용하고 때로는 10 배의 성과를내는 경우가 있습니다. 더 나은 경우는 매개 변수 바인딩을 사용하지만 명령문 준비를 사용하지 않는 PDO를 사용하는 것입니다. - donis
  • 직접 쿼리를 사용하는 경우 PDO를 사용하는 것이 더 좋습니다. mysqli :: escape_string을 사용해야합니다. - Kassem Itani

1558

경고:이 답변의 샘플 코드 (예 : 질문의 샘플 코드)는 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기능.

매개 변수가있는 쿼리를 사용하려면 다음을 사용해야합니다.MySQLi오히려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();
?>

거기에서 읽을 핵심 기능은 다음과 같습니다.mysqli::prepare.

또한 다른 사람들이 제안했듯이 추상화 레이어를 유용하게 / 더 쉽게 찾을 수 있습니다.PDO.

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

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


  • ~을 사용하여mysql_real_escape_string충분합니까? 아니면 매개 변수화해야합니까? - peiman F.
  • @peimanF. 로컬 프로젝트에서도 매개 변수화 된 쿼리를 사용하는 것이 좋습니다. 매개 변수화 된 쿼리를 사용하면보증 된SQL 인젝션은 없을거야. 하지만 가짜 검색 (예 : HTML 코드를 텍스트에 넣는 것과 같은 XSS 삽입)이 발생하지 않도록 데이터를 삭제해야합니다.htmlentities예를 들면 - Goufalite
  • @peimanF. 매개 변수화 된 쿼리와 바인드 값을 사용하는 것이 좋지만 실제 이스케이프 문자열은 현재 좋습니다. - Richard
  • 나는mysql_real_escape_string()완전성을 위해,하지만 가장 오류가 발생하기 쉬운 방법을 먼저 나열하는 팬이 아닙니다. 독자는 첫 번째 예제를 빠르게 습득 할 수 있습니다. 지금은 더 이상 사용되지 않는 것이 좋습니다. :) - Time Sheep

980

모든 대답은 문제의 일부만을 다룹니다. 실제로, 거기있다동적으로 추가 할 수있는 다른 쿼리 부분 : -

  • 식별자
  • 구문 키워드

그리고 준비된 진술은 그 중 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 개 이상의 삭제 된 답변- 품질이 좋지 않거나 나쁘고 오래된 관행을 조장하여 커뮤니티에서 제거한 모든 것. 더 나쁜 것은 나쁜 대답의 일부는 삭제되지 않고 번성하는 것입니다.

예를 들어,거기 (1) (2) 아직도 (3) 많은 (4) 답변 (5), 포함하는두 번째로 가장 많은 upvoted 대답당신에게 수동 문자열 이스케이프 - 안전하지 않은 것으로 입증 된 구식 접근법을 제안합니다.

아니면 조금 더 나은 대답이 있습니다.문자열 형식화의 또 다른 방법심지어 궁극의 만병 통치약으로 그것을 자랑합니다. 물론, 그렇지 않습니다. 이 방법은 일반 문자열 형식보다 낫지 만 모든 단점을 유지합니다. 문자열에만 적용 할 수 있으며 다른 수동 형식과 마찬가지로 본질적으로 선택적이며 비 강제적 인 방법이므로 사람의 실수가 발생하기 쉽습니다.

나는이 모든 것이 매우 오래된 미신 때문에 그런 권위자들에 의해지지받는다고 생각한다.OWASP또는PHP 매뉴얼어떤 "탈출"과 SQL 주입으로부터의 보호 사이의 평등을 선언합니다.

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

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

그래서, 어떤 "탈출"과는 달리, 준비된 진술~이다.실제로 SQL 주입 (해당되는 경우)으로부터 보호하는 측정 값입니다.

아직 확신이 없다면 여기에 제가 쓴 단계별 설명이 있습니다.히치하이커의 SQL 주입 방지 가이드여기서 나는이 모든 문제를 자세히 설명하고 심지어 나쁜 습관과 공개에 전적으로 헌신하는 섹션을 작성했습니다.


  • 위대하고 잘 생각한 기사. 필자는 PHP의 Sanitize 필터를 사용하여 흰색 정렬 목록 (일종의 정확하지는 않음)을 추가 할 수도 있습니다. 예를 들어,FILTER_SANITIZE_NUMBER_INT숫자 문자 만 허용하므로 전체 문자열이 아닌 흰색 문자를 나열 할 수 있습니다. 준비된 진술과 함께 좋은 "벨트 및 멜빵"을 만듭니다. 접근. - Sablefoste
  • @Sablefoste 여기에 화이트리스트를 작성해야 할 필요가 없습니다. 모든 위생 처리가 중복됩니다. 뒤 따르는 규칙이 적을수록 실수는 줄어 듭니다. 어떤 검증을 할 수는 있지만, 응용 프로그램 로직을 위해 수행하고 데이터베이스는 수행하지 마십시오. - Your Common Sense

787

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

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

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


  • MySQL DB를위한 PDO 랩 mysqli가 없습니까? 어떤 경우에는 mysqli보다 더 빠를 수는 없습니다. 그래도 나는 그것을 여전히 권장합니다. mysqli API보다 훨씬 우수한 인터페이스. - Peter Bagnall
  • 매개 변수화 된 쿼리를 사용하면 쿼리 속도가 빨라집니다. 기술적으로 mysqli는 매우 작은 마진으로 더 빠를 수도 있습니다. 서버가 쿼리에 응답하는 데 실제로 걸리는 시간은 래퍼를 사용하기 때문에 발생할 수있는 타이밍의 차이를 풉니 다. 그러나 mysqli는 데이터베이스에 묶여있다. 다른 데이터베이스 엔진을 사용하려면 mysqli를 사용하는 모든 호출을 변경해야합니다. PDO는 그렇지 않습니다. - Kibbee
  • PDO가 동적을 지원하지 않습니다.order by불행히도 :( - Horse

584

용도PDO준비된 쿼리.

($conn~이다.PDO목적)

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


  • 에서위키피디아: 나중에 다른 프로토콜을 사용하여 전송되는 매개 변수 값이 올바르게 이스케이프 될 필요가 없기 때문에 준비된 문은 SQL 주입에 대해 복원력이 있습니다. 원래 명령문 템플리트가 외부 입력에서 파생되지 않으면 SQL 삽입이 발생할 수 없습니다. - Imran

519

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

나는이 문제에 직면했다. 그러나 나는 그것을 해결했다고 생각한다.대단히정교한 방법 - 해커가 따옴표 사용을 피하는 방법 필자는 에뮬레이트 된 준비 문과 함께이 기능을 사용했습니다. 나는 그것을 방지하기 위해 사용한다.모든가능한 SQL 주입 공격의 종류.

내 접근 방식 :

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

  • 정수에서 다른 것을 기대한다면16 진수. 16 진수라면 모든 입력을 완벽하게 피할 수 있습니다. C / C ++에는 다음과 같은 함수가 있습니다.mysql_hex_string(), PHP에서 사용할 수 있습니다bin2hex().

    이스케이프 된 문자열은 원래 길이의 2 배 크기이므로 걱정하지 마십시오.mysql_real_escape_string, PHP는 같은 용량을 할당해야한다.((2*input_length)+1), 그것은 동일합니다.

  • 이 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')

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

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

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

** 0x ** 접두사는 다음과 같은 데이터 열에 만 사용할 수 있습니다.char, varchar, 텍스트, 블록, 바이너리 등.

또한 빈 문자열을 삽입하려는 경우 약간 복잡합니다. 당신은 완전히 그것을 대체해야합니다.'', 또는 오류가 발생합니다.

UNHEX ()~에서 작동하다어떤기둥; 빈 문자열에 대해 걱정할 필요가 없습니다.


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

이 hex 메서드는 정수가 문자열과 같고 이스케이프 된 경우 SQL 주입 공격으로 자주 사용됩니다mysql_real_escape_string. 그런 다음 따옴표를 사용하지 않아도됩니다.

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

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

공격은 당신을 매우 주입시킬 수있다.용이하게. 스크립트에서 반환 된 다음 주입 코드를 고려하십시오.

SELECT ... WHERE id =-1 유니온 모두 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')


  • @SumitGupta 네가 했어. MySQL과 연결하지 못함+하지만 함께CONCAT. 그리고 성능 : mysql은 데이터를 파싱해야하기 때문에 성능에 영향을 미쳤다고 생각합니다. 근원이 문자열 또는 16 진수이면 문제가되지 않습니다. - Zaffy
  • @ YouCommonSense 어떤 오류가 발생합니까? 구체적으로 말하십시오. - Zaffy
  • @ YourCommonSense 당신은 개념을 이해하지 못한다 ... 당신이 mysql에서 문자열을 원한다면 이것을 인용한다.'root'또는 16 진수 수 있습니다.0x726f6f74하지만 숫자를 원하면 문자열로 보내면 & # 39; 42 & # 39; CHAR (42) ... & # 42; & # 39; 16 진수는0x3432아니0x42 - Zaffy
  • @ YourCommonSense 나는 말할 것도 없다. 단지 lol ... 숫자 필드에서 16 진수를 시도하고 싶다면 두 번째 주석을 보라. 나는 그것이 당신에게 효과가 있다고 확신합니다. - Zaffy
  • @ YourCommonSense 당신은 여전히 이해가 안되니? 문자열이 비어 있으면 오류로 종료되므로 0x와 concat을 사용할 수 없습니다. 당신이 당신의 질문에 대한 간단한 대안을 원한다면 이것을 시도하십시오SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ') - Zaffy

469

중대한

SQL 삽입을 방지하는 가장 좋은 방법은준비된 진술 탈출하는 대신, 같이받아 들인 대답보여줍니다.

다음과 같은 라이브러리가 있습니다.Aura.SqlEasyDB개발자는 준비된 문장을 더 쉽게 사용할 수 있습니다. 준비된 진술이 왜 더 나은지에 대해 자세히 알아 보려면SQL 삽입 중지, 인용하다mysql_real_escape_string()우회로최근에 WordPress의 유니 코드 SQL 주입 취약점이 수정되었습니다..

사출 방지 -mysql_real_escape_string ()

PHP는 이러한 공격을 막기 위해 특별히 제작 된 함수를 가지고 있습니다. 함수의 입소문 만 사용하면됩니다.mysql_real_escape_string.

mysql_real_escape_string는 MySQL 쿼리에 사용될 문자열을 취하여 모든 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 인젝션 방지.


  • 이것은 레거시 mysql 확장으로 할 수있는 최선의 방법입니다. 새 코드의 경우 mysqli 또는 PDO로 전환하는 것이 좋습니다. - Álvaro González
  • 나는 이러한 공격을 막기 위해 특별히 제작 된 기능과 동의하지 않습니다. 내 생각에는mysql_real_escape_string목적은 모든 입력 데이터 문자열에 대해 올바른 SQL 쿼리를 작성할 수있게합니다. 예방 SQL 주입은이 함수의 부작용입니다. - sectus
  • 당신은 올바른 입력 데이터 문자열을 쓰는 함수를 사용하지 않습니다. 이스케이프가 필요하지 않거나 이미 이스케이프 처리 된 올바른 텍스트를 작성하기 만하면됩니다. mysql_real_escape_string ()은 당신이 염두에두고 언급 한 목적으로 디자인되었을 수도 있지만, 그 유일한 가치는 주입을 막는 것이다. - Nazca
  • 경고! mysql_real_escape_string() 틀림 없다. - eggyal
  • mysql_real_escape_string이제는 더 이상 사용할 수없는 옵션이므로 더 이상 사용되지 않습니다. 앞으로 PHP에서 제거 될 것입니다. PHP 또는 MySQL 사람들이 권장하는 것에 최선을 다할 것입니다. - jww

425

보안 경고:이 답변은 보안 모범 사례와 일치하지 않습니다.이스케이프는 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 . "')");

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


  • 나는 당신의 모범을 시험해 보았고 나에게 잘 해줬습니다. "모든 문제를 해결할 수 있습니다." - Chinook
  • 문자열을 따옴표로 묶지 않아도 여전히 삽입 할 수 있습니다. 갖다$q = "SELECT col FROM tbl WHERE x = $safe_var";예를 들면. 환경$safe_var1 UNION SELECT password FROM users이 경우 따옴표가 없기 때문에 작동합니다. 다음을 사용하여 문자열을 검색어에 삽입 할 수도 있습니다.CONCATCHR. - Polynomial
  • @ 폴리 노미 글 완전하게 맞았지만 잘못된 사용으로 보입니다. 오랫동안 올바르게 사용한다면 확실히 작동 할 것입니다. - glglgl
  • 경고! mysql_real_escape_string() 틀림 없다. - eggyal
  • mysql_real_escape_string이제는 더 이상 사용할 수없는 옵션이므로 더 이상 사용되지 않습니다. 앞으로 PHP에서 제거 될 것입니다. PHP 또는 MySQL 사람들이 권장하는 것에 최선을 다할 것입니다. - jww

360

당신이 무엇을 사용하든, 입력이 이미 맹 글링되지 않았는지 확인하십시오.magic_quotes또는 다른 어떤 의미있는 쓰레기가 있다면, 필요하다면stripslashes또는 그것을 위생적으로하는 것이 무엇이든간에.


  • 과연; magic_quotes를 켜고 실행하면 불쌍한 실습 만하면됩니다. 그러나 때로는 서버를 관리 할 수있는 권한이 없거나 이러한 구성에 의존하는 응용 프로그램과 응용 프로그램이 공존해야하는 등의 환경을 항상 해당 수준으로 제어 할 수없는 경우도 있습니다. 이러한 이유 때문에 이식 가능한 응용 프로그램을 작성하는 것이 좋습니다. 물론 배포 환경을 제어하는 경우에는 노력이 낭비됩니다. 사내 응용 프로그램이거나 특정 환경에서만 사용되기 때문입니다. - Rob
  • PHP 5.4 이후로, & # 39; magic quotes & # 39; 있었다살해당한. 그리고 나쁜 쓰레기에 좋은 riddance. - BryanH

343

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

이러한 예제는 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 삽입 (이스 케이 핑이 충분하지 않을 때)


  • 길이, 유형 및 구문에 대해 정의 된 규칙 세트와 비즈니스 규칙에 대해 사용자 입력이 인증되는 입력 유효성 검증 기술을 채택하면 SQL 주입을 방지 할 수 있습니다. - Josip Ivic

292

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

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


  • 제 첫 단락이 중요하다고 생각합니다. 이해가 핵심입니다. 또한 모든 사람들이 한 회사에서 일하고 있지 않습니다. 많은 수의 사람들에게 프레임 워크는 실제로이해. 기한 내에 일하는 동안 기초에 친밀 해지는 것은 가치 있지 않을지도 모르지만, 밖에 do-it-yourselfers는 손을 더럽히는 것을 즐긴다. 프레임 워크 개발자들은 다른 사람들이 절대로 실수를해서는 안된다고 생각하는 특권이 없습니다. 결정을 내리는 힘은 여전히 중요합니다. 앞으로 나의 프레임 워크가 다른 계획을 대체하지 못한다고 누가 말하겠습니까? - Anthony Rutledge
  • @AnthonyRutledge 당신은 절대적으로 옳습니다. 그것은 매우 중요합니다.알다무슨 일이 벌어지고 왜. 그러나, 진실한 시도 및 적극적으로 사용 및 개발 된 프레임 워크가 많은 문제를 해결하고 많은 보안 취약점을 패치 한 가능성은 이미 상당히 높습니다. 코드 품질에 대한 느낌을 얻으려면 소스를 살펴 보는 것이 좋습니다. 테스트되지 않은 혼란이 있다면 아마 안전하지 않을 것입니다. - Johannes Fahrenkrug
  • 이리. 이리. 좋은 지적이야. 그러나 많은 사람들이 MVC 시스템을 연구하고 배울 수 있다는 것에 동의하지만 모든 사람이 수동으로 컨트롤러와 서버를 재현 할 수있는 것은 아닙니다. 하나는이 시점에서 너무 멀리 갈 수 있습니다. 내 여자 친구가 날 만들었던 땅콩 버터 피칸 쿠키를 가열하기 전에 전자 렌지를 알아야합니까? ;-) - Anthony Rutledge
  • @AnthonyRutledge 나는 동의한다! 유스 케이스도 차이가 있다고 생각합니다. 개인 홈페이지 용 사진 갤러리를 만들거나 온라인 뱅킹 웹 응용 프로그램을 만들 계획입니까? 후자의 경우 보안의 세부 사항을 이해하는 것이 중요하며 내가 사용하고있는 프레임 워크가이를 어떻게 처리하는지는 중요합니다. - Johannes Fahrenkrug
  • 아, 보안상의 예외는 자신에게 달려 있습니다. 보세요, 저는 그것을 모두 위험에 빠뜨리고 파산하려고합니다. :-) 키드. 충분한 시간을 들여 사람들은 꽤 안전한 응용 프로그램을 만드는 법을 배울 수 있습니다. 너무 많은 사람들이 급하게 있습니다. 그들은 손을 들고 프레임 워크가더 안전한. 결국, 그들은 테스트하고 사물을 파악할 시간이 없습니다. 또한 보안은 전담 학습이 필요한 분야입니다. 이것은 알고리즘과 디자인 패턴을 이해함으로써 프로그래머가 깊이 알고있는 것만은 아닙니다. - Anthony Rutledge

277

나는 좋아한다.저장 프로 시저(MySQL은 5.0 이후 프로 시저 지원을 저장했다.) 보안 관점에서 - 장점은 -

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

단점은 -

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


273

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']= admin@emali.com' OR '1=1

비밀 번호를 ....

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

$query="select * from users where email='admin@emali.com' OR '1=1';

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


250

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

  1. 학습에 대해 생각해보십시오.PDO(PHP Data Objects) - 여러 데이터베이스에 대한 일관된 액세스 방법을 제공하는 데이터베이스 액세스 계층입니다.
  2. 학습에 대해 생각해보십시오.MySQLi
  3. 다음과 같은 기본 PHP 함수를 사용하십시오.strip_tags,mysql_real_escape_string또는 변수 숫자 인 경우 그냥(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는이 전투에서 쉽게 승리합니다. 열 두 개를 지원합니다. 다른 데이터베이스 드라이버와 명명 된 매개 변수는 무시할 수 있습니다. 작은 성능 손실을 경험하고 API에 익숙해집니다. 보안에서 관점에서 볼 때 개발자가 사용하는 한 둘 다 안전합니다. 그들이 사용되기로되어있는 길

그러나 PDO와 MySQLi 모두 매우 빠르지 만 MySQLi는 벤치 마크에서 미미하게 더 빠름 - 준비되지 않은 경우 ~ 2.5 % 진술서, ~ 준비된 환자의 ~ 6.5 %.

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


240

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

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

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


  • 이것은 "이스케이프 된 값"을 사용하는 몇 가지 경우 중 하나입니다. 준비된 진술 대신. 그리고 정수형 변환은 매우 효율적입니다. - HoldOffHunger

221

캐시 엔진을 활용하려는 경우레디 스또는Memcached아마도 DALMP가 선택 일 수 있습니다. 그것은 순수를 사용합니다.MySQLi. 이것을 확인하십시오 :PHP를 사용하는 MySQL 용 DALMP 데이터베이스 추상화 계층.

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


213

PDO 사용 방법을 잘 모르는 사람들을 위해 (mysql_함수), 나는매우 간단한 PDO wrapper그것은 하나의 파일입니다. 응용 프로그램을 수행하는 데 필요한 모든 일반적인 작업을 수행하는 것이 얼마나 쉬운지를 보여주기 위해 존재합니다. 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));


211

이 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


193

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

prepared statement 나 mysqli_real_escape_string을 사용하든 관계없이 항상 작업중인 입력 데이터의 유형을 알아야합니다.

따라서 준비된 문장을 사용한다면, mysqli_stmt_bind_param 함수를위한 변수의 타입을 지정해야한다.

mysqli_real_escape_string의 사용은 이름에서 알 수 있듯이 문자열에서 특수 문자를 이스케이프 처리하므로 정수가 안전하지 않을 수 있습니다. 이 함수의 목적은 SQL.에서. 자열을 손상시키지 못하게하고 데이터베이스의 손상을 f 지하기위한 것입니다. 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


  • 질문은 매우 일반적입니다. 위의 몇 가지 위대한 답변이지만 대부분은 준비된 진술을 제안합니다. MySQLi async는 준비된 명령문을 지원하지 않으므로 sprintf는이 상황에 적합한 옵션입니다. - Dustin Graham

173

이 문제에 대한 간단한 대안은 데이터베이스 자체에 적절한 권한을 부여하여 해결할 수 있습니다. 예 : 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';

더 자세히 알아보기부여.


  • 이 대답은근본적으로 잘못된주입 예방을 방지하는 대신 그 결과를 약화시키는 데 도움이되기 때문에. 헛된. - Your Common Sense
  • 맞습니다. 솔루션을 제공하지는 않지만 손을 피하기 위해 먼저 해결할 수있는 방법입니다. - Apurv Nerlekar
  • @Apurv 내 목표가 데이터베이스에서 개인 정보를 읽는 것이고 DELETE 권한이 없다면 아무 것도 의미하지 않습니다. - Alex Holsgrove
  • @AlexHolsgrove : 쉽습니다. 결과를 부드럽게하기위한 좋은 습관을 제안했습니다. - Apurv Nerlekar
  • @Apurv "결과를 부드럽게하기"를 원하지 않는다면, 가능한 한 모든 일을하고 싶다. 공평하게 말하자면 올바른 사용자 액세스를 설정하는 것이 중요하지만 OP가 요구하는 것은 실제로는 아닙니다. - Alex Holsgrove

167

웹 응용 프로그램이 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");


162

간단한 방법은 PHP 프레임 워크를 사용하는 것입니다.CodeIgniter의또는Laravel필터링 및 액티브 레코드와 같은 내장 기능이있어 이러한 뉘앙스에 대해 걱정할 필요가 없습니다.


  • 질문의 요점은 이러한 틀을 사용하지 않고이 문제를 해결하는 것입니다. - Sanke

162

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

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

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

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

  1. SQL 사용자 (제한 사용자 권한) : 가장 일반적인 SQL 작업은 (SELECT, UPDATE, INSERT)입니다. 그런 다음 필요없는 사용자에게 UPDATE 권한을주는 이유는 무엇입니까? 예를 들어로그인 및 검색 페이지SELECT를 사용하는 경우에만 높은 권한으로이 페이지에서 DB 사용자를 사용하는 이유는 무엇입니까?RULE : 모든 SQL 작업에 대해 모든 권한에 대해 하나의 데이터베이스 사용자를 생성하지 마십시오. 사용하기 쉬운 사용자 이름으로 (deluser, selectuser, updateuser)와 같은 스키마를 만들 수 있습니다.

만나다최소 권한 원칙

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

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

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

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

[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. 데이터 유효성 검사


136

** 경고 :이 답변에 설명 된 접근법은 매우 특정한 시나리오에만 적용되며 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 주소또는 쿠키, 역사, 브라우저 또는 기타 민감한 정보가 포함되어 있으므로 나중에 계정을 금지하거나 관계 당국에 문의하여 거래 할 수 있습니다.


125

많은 답변이 있습니다.PHP와 MySQL,하지만 여기에 대한 코드입니다PHP와 오라클oci8 드라이버의 일반적인 사용뿐만 아니라 SQL 삽입을 방지하기 위해 :

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


121

좋은 아이디어는'객체 - 관계형 매퍼'처럼이디엄:

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


118

사용PDOMYSQLiSQL 인젝션을 방지하는 좋은 방법이지만, 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.


  • 또한 is_string ()을 사용하여 $ _POST 배열 멤버를 검사 할 필요가 전혀 없습니다. - Your Common Sense
  • 경고! mysql_real_escape_string() 틀림 없다. - eggyal
  • mysql_real_escape_string이제는 더 이상 사용할 수없는 옵션이므로 더 이상 사용되지 않습니다. 앞으로 PHP에서 제거 될 것입니다. PHP 또는 MySQL 사람들이 권장하는 것에 최선을 다할 것입니다. - jww
  • 주제 : 사용자가 제출 한 데이터를 신뢰하지 마십시오. 기대하는 것은 특수 문자 또는 부울 논리가있는 가비지 데이터입니다.이 논리 데이터 자체는 실행중인 SQL 쿼리의 일부가되어야합니다. $ _POST 값은 SQL 부분이 아닌 데이터 전용으로 유지하십시오. - Bimal Poudel

84

몇 년 전에이 작은 기능을 작성했습니다.

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 #에서 String.Format과 같은 문을 실행할 수 있습니다.

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

그것은 가변 타입을 고려하여 빠져 나간다. 테이블, 열 이름을 매개 변수화하려고하면 모든 문자열을 잘못된 구문 인 따옴표로 묶으므로 실패합니다.

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

최근 질문