준비된 명령문이 SQL 삽입 공격으로부터 어떻게 보호 할 수 있습니까?


172

준비된 명령문SQL 주입 공격을 방지하는 데 어떻게 도움이 됩니까?

위키피디아의 말 :

준비된 명령문은 나중에 다른 프로토콜을 사용하여 전송되는 매개 변수 값이 올바르게 이스케이프 될 필요가 없으므로 SQL 삽입에 대해 복원력이 뛰어납니다. 원래 명령문 템플리트가 외부 입력에서 파생되지 않은 경우 SQL 삽입이 발생할 수 없습니다.

이유를 잘 알 수 없습니다. 쉬운 영어와 몇 가지 예에서 간단한 설명은 무엇입니까?

답변:


290

아이디어는 매우 간단합니다. 쿼리와 데이터는 데이터베이스 서버로 개별적으로 전송 됩니다 .
그게 다야.

SQL 주입 문제의 근본은 코드와 데이터혼합에 있습니다.

실제로 SQL 쿼리는 합법적 인 프로그램 입니다. 그리고 우리는 그러한 데이터를 동적으로 생성하여 데이터를 즉시 추가하고 있습니다. 따라서 데이터는 프로그램 코드를 방해 하고 심지어 모든 SQL 인젝션 예제가 보여 주듯이 (PHP / Mysql의 모든 예제) 데이터를 변경시킬 수 있습니다.

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

정기적 인 쿼리를 생성합니다

SELECT * FROM users where id=1

이 코드 동안

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

악성 시퀀스를 생성합니다

SELECT * FROM users where id=1; DROP TABLE users;

데이터를 프로그램 본문에 직접 추가하고 프로그램의 일부가되기 때문에 작동하므로 데이터가 프로그램을 변경할 수 있으며 전달 된 데이터에 따라 정기적 인 출력 또는 테이블이 users삭제됩니다.

하지만 준비된 명령문의 경우 우리는 우리의 프로그램을 변경하지 않습니다, 그것은 그대로 유지
요점있다.

먼저 서버에 프로그램 을 보내고 있습니다

$db->prepare("SELECT * FROM users where id=?");

여기서 데이터는 매개 변수 또는 자리 표시 자라는 일부 변수 로 대체됩니다 .

데이터가 없어도 정확히 동일한 쿼리가 서버로 전송됩니다. 그런 다음 쿼리와 본질적으로 분리 된 두 번째 요청으로 데이터를 전송합니다 .

$db->execute($data);

따라서 프로그램을 변경하거나 해를 끼칠 수 없습니다.
아주 간단하지 않습니까?

내가 추가해야 할 유일한 것은 모든 매뉴얼에서 항상 생략되었습니다.

준비된 명령문은 데이터 리터럴 만 보호 할 수 있지만 다른 쿼리 파트와 함께 사용할 수 없습니다.
우리는, 역학적, 추가 말을하면 그래서, 식별자 예를 들어, 필드 이름을 - - 준비된 문은 우리를 도울 수 없다. 나는 한 최근이 문제를 설명 나 자신을 반복하지 않도록.


2
"예를 들어, 기본적으로 PDO는 준비된 명령문을 사용하지 않습니다"-PDO는 그러한 기능을 지원하지 않는 드라이버에 대해서만 준비된 명령문을 에뮬레이트하므로 정확하게 적용되지는 않습니다.
pinepain

3
@ zaq178miami : "PDO는이 기능을 지원하지 않는 드라이버에 대해서만 준비된 명령문을 에뮬레이트합니다"-사실이 아닙니다. MySQL은 현재 꽤 오랫동안 준비된 문장을 지원했습니다. PDO 드라이버도 있습니다. 그러나 아직 MySQL 쿼리는 기본적으로 PDO에서 마지막으로 확인했을 때 여전히 준비되었습니다.
cHao

9
사이의 차이가 무엇 $spoiled_data = "1; DROP TABLE users;"-> $query = "SELECT * FROM users where id=$spoiled_data";에 비해 : $db->prepare("SELECT * FROM users where id=?");-> $data = "1; DROP TABLE users;"-> $db->execute($data);. 그들은 같은 일을하지 않습니까?
Juha Untinen

14
@Juha Untinen 데이터는 무엇이든 가능합니다. 데이터를 구문 분석하지 않습니다. 그것은 명령이 아닌 DATA입니다. 따라서 $ data에 sql 명령이 포함되어 있어도 실행되지 않습니다. 또한 id가 숫자 인 경우 문자열 내용은 보고서 또는 값 0을 생성합니다.
Soley

21

다음은 예제를 설정하기위한 SQL입니다.

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

Inject 클래스는 SQL 주입에 취약합니다. 쿼리는 사용자 입력과 함께 동적으로 붙여집니다. 이 쿼리의 목적은 Bob에 대한 정보를 표시하는 것입니다. 사용자 입력에 따른 급여 또는 보너스. 그러나 악의적 인 사용자는 where 절과 '또는 true'에 해당하는 명령을 실행하여 쿼리를 손상시키는 입력을 조작하여 숨겨져 있던 Aaron에 대한 정보를 포함하여 모든 것이 반환됩니다.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

이것을 실행하는 첫 번째 경우는 정상적인 사용법이고 두 번째 경우는 악의적 인 주입입니다.

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

사용자 입력의 문자열 연결로 SQL 문을 작성해서는 안됩니다. 인젝션에 취약 할뿐만 아니라 서버에도 캐싱 영향을 미칩니다 (문이 변경되어 SQL 문 캐시 적중이 적어 지지만 바인드 예제는 항상 동일한 명령문을 실행 함).

이러한 종류의 주입을 피하기위한 바인딩의 예는 다음과 같습니다.

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

이전 예제와 동일한 입력으로 이것을 실행하면 해당 문자열과 일치하는 paymentType이 없기 때문에 악성 코드가 작동하지 않음을 나타냅니다.

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?

데이터베이스에 연결하는 프로그램에서 준비된 명령문을 사용하면 db의 일부인 준비된 명령문을 사용하는 것과 동일한 효과가 있습니까? 예를 들어 Postgres에는 자체 준비된 명령문이 있으며이를 사용하여 SQL 삽입을 막을 수 있습니까? postgresql.org/docs/9.2/static/sql-prepare.html
Celeritas

@Celeritas Postgresql에 대한 명확한 대답이 없습니다. 문서를 보면 효과가 같습니다. 매개 변수를 바인딩하는 명명 된 문을 실행 PREPARE하면서 이미 구문 분석 된 고정 명명 된 문을 만듭니다 (예 : 입력에 관계없이 문이 더 이상 변경되지 않음) EXECUTE. PREPARE세션 기간 만 있기 때문에 실제로는 psql 스크립트를 통한 주입을 막기위한 것이 아니라 성능상의 이유 인 것 같습니다. psql 액세스의 경우 저장 프로 시저에 권한을 부여하고 procs 내에서 매개 변수를 바인딩 할 수 있습니다.
Glenn

@ Celeleritas x86_64에서 PostgreSQL 11.1을 사용하여 위의 코드를 시도했지만 위의 SQLi 예제가 작동했습니다.
Krishna Pandey 2016 년

15

기본적으로 준비된 진술을 통해 잠재적 인 해커로부터 들어오는 데이터는 데이터로 취급되며 응용 프로그램 SQL과 혼합되거나 SQL로 해석 될 수있는 방법이 없습니다. 응용 프로그램 SQL).

준비된 명령문은 먼저 효율적인 쿼리 계획을 찾기 위해 SQL 쿼리를 "준비"하고 나중에 폼에서 나온 실제 값을 전송하므로 쿼리가 실제로 실행되기 때문입니다.

더 좋은 정보는 다음과 같습니다.

준비된 명령문 및 SQL 삽입


6

나는 그 해답을 읽고 준비된 진술의 본질을 밝히는 핵심 요점을 강조 할 필요성을 여전히 느꼈다. 사용자 입력이 관련된 데이터베이스를 쿼리하는 두 가지 방법을 고려하십시오.

순진한 접근

하나는 사용자 입력을 일부 SQL 문자열과 연결하여 SQL 문을 생성합니다. 이 경우 사용자는 악성 SQL 명령을 포함시킬 수 있으며이 명령은 실행을 위해 데이터베이스로 전송됩니다.

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

예를 들어, 악의적 사용자 입력을 초래할 수 SQLString이상임"SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

악의적 인 사용자로 인해 SQLString두 번째 문 ( "DROP TABLE CUSTOMERS")이 피해를 입힐 수 있는 두 개의 문 이 있습니다.

준비된 진술

이 경우, 쿼리와 데이터의 분리로 인해 사용자 입력은 SQL 문으로 취급 되지 않으므로 실행되지 않습니다 . 이런 이유로 주입 된 악의적 인 SQL 코드는 아무런 해를 끼치 지 않습니다. 따라서 "DROP TABLE CUSTOMERS"위의 경우에는 실행되지 않습니다.

간단히 말해서, 준비된 문장으로 사용자 입력을 통해 소개 된 악성 코드는 실행되지 않습니다!


정말? 받아 들여진 대답이 정확히 말하지 않습니까?
당신의 상식

@Your Common Sense 승인 된 답변은 많은 귀중한 정보로 채워져 있지만 데이터와 쿼리의 분리에 대한 구현 세부 정보가 무엇인지 궁금합니다. 악의적으로 주입 된 데이터 (있는 경우)가 실행되지 않을 것이라는 점에 초점을 두는 반면, 머리에는 못이 박 힙니다.
N.Vegeta

답변에 어떤 "구현 세부 정보"가 제공되지 않습니까?
당신의 상식

내가 어디에서 왔는지 알고 자한다면 내 요점은 다음과 같다는 것을 알게 될 것이다. 구현 세부 사항을보고자하는 간절한 욕구는 악의적 인 사용자 입력으로 인해 발생하지 않는 명백한 이유를 이해해야 할 필요성에서 비롯되었다 해. 구현 세부 사항을 볼 필요가 없습니다. 그렇기 때문에 구현 세부 정보가 SQL에 악의적으로 입력되지 않도록 메시지가 전송되도록하는 이유를 깨닫게됩니다. 귀하의 답변은 (요청에 따라) 어떻게 질문에 대답합니까?하지만 다른 사람들 (나 같은)이 왜 간결한 응답에 만족할 것이라고 생각합니까?
N.Vegeta

암시적인 비판이 아니라 핵심을 밝히는 강화를 고려하십시오 (수락 된 응답의 저자가 누구인지 재확인).
N.Vegeta

5

준비된 명령문을 작성하여 DBMS에 보내면 실행을위한 SQL 쿼리로 저장됩니다.

나중에 DBMS가 해당 데이터를 실행 (매개 변수화)을위한 조회 매개 변수로 사용하도록 데이터를 조회에 바인드합니다. DBMS는 바인딩 된 데이터를 이미 컴파일 된 SQL 쿼리에 대한 보충 자료로 사용하지 않습니다. 단순히 데이터입니다.

이는 준비된 명령문을 사용하여 SQL 삽입을 수행하는 것이 기본적으로 불가능하다는 것을 의미합니다. 준비된 명령문의 특성과 DBMS와의 관계는이를 방지합니다.


4

SQL Server 에서는 입력 매개 변수가 쿼리를 구성하지 않기 때문에 준비된 문을 사용하는 것이 확실합니다. 실행 된 쿼리가 동적 쿼리가 아님을 의미합니다. SQL 주입 취약한 명령문의 예

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

이제 inoutusername 변수의 값이 a '또는 1 = 1과 같으면이 쿼리는 이제 다음과 같이됩니다.

select * from table where username='a' or 1=1 -- and password=asda

그리고 나머지는 다음에 주석 처리 --되므로 아래처럼 준비된 명령문 예제를 사용하여 실행 및 무시되지 않습니다.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

따라서 실제로 다른 매개 변수를 보낼 수 없으므로 SQL 삽입을 피하십시오 ...


3

핵심 문구는 need not be correctly escaped입니다. 즉, 대시, 아포스트로피, 따옴표 등을 던지는 사람들에 대해 걱정할 필요가 없습니다.

그것은 모두 당신을 위해 처리됩니다.


2
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

서블릿에 있다고 가정 해 봅시다. 악의적 인 사람이 '필터'에 대해 나쁜 값을 전달하면 데이터베이스를 해킹 할 수 있습니다.


0

근본 원인 # 1-구분자 문제

따옴표를 사용하여 문자열을 구분하고 문자열의 일부가되기 때문에 SQL 삽입이 가능하기 때문에 때로는 해석 할 수 없습니다. 문자열 데이터에 사용할 수없는 구분 기호가 있으면 SQL 삽입이 발생하지 않았을 것입니다. 분리 문자 문제를 해결하면 SQL 삽입 문제가 제거됩니다. 구조 쿼리가 그렇게합니다.

근본 원인 # 2-인간의 본성, 사람들은 교묘하고 일부 교활한 사람들은 악의적 이며 모든 사람들 실수를합니다

SQL 주입의 또 다른 근본 원인은 인간 본성입니다. 프로그래머를 포함한 사람들은 실수를합니다. 구조적 쿼리에서 실수를해도 시스템이 SQL 삽입에 취약하지 않습니다. 구조적 쿼리를 사용하지 않는 경우 실수로 인해 SQL 삽입 취약점이 발생할 수 있습니다.

구조적 쿼리가 SQL 주입의 근본 원인을 해결하는 방법

구조적 쿼리는 하나의 명령문에 sql 명령을 넣고 별도의 프로그래밍 명령문에 데이터를 넣어서 구분 기호 문제점을 해결합니다. 프로그래밍 문은 필요한 분리를 만듭니다.

구조화 된 쿼리는 인적 오류가 중요한 보안 허점을 만드는 것을 방지합니다. 실수를 저지르는 사람과 관련하여 구조 쿼리를 사용하면 SQL 삽입이 발생할 수 없습니다. 구조적 쿼리를 포함하지 않는 SQL 주입을 방지하는 방법이 있지만, 이러한 접근 방식의 정상적인 인적 오류는 일반적으로 SQL 주입에 약간 노출됩니다. 구조적 쿼리는 SQL 주입에서 안전하지 않습니다. 거의 모든 다른 프로그래밍과 마찬가지로 구조적 쿼리를 사용하여 세상에서 모든 실수를 저지를 수는 있지만 SQL 주입으로 인수 할 수있는 것은 없습니다. 이것이 사람들이 이것이 SQL 주입을 막는 올바른 방법이라고 말하는 이유입니다.

따라서 SQL 주입의 원인과 사용할 때 불가능한 자연 구조적 쿼리가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.