답변:
아이디어는 매우 간단합니다. 쿼리와 데이터는 데이터베이스 서버로 개별적으로 전송 됩니다 .
그게 다야.
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);
따라서 프로그램을 변경하거나 해를 끼칠 수 없습니다.
아주 간단하지 않습니까?
내가 추가해야 할 유일한 것은 모든 매뉴얼에서 항상 생략되었습니다.
준비된 명령문은 데이터 리터럴 만 보호 할 수 있지만 다른 쿼리 파트와 함께 사용할 수 없습니다.
우리는, 역학적, 추가 말을하면 그래서, 식별자 예를 들어, 필드 이름을 - - 준비된 문은 우리를 도울 수 없다. 나는 한 최근이 문제를 설명 나 자신을 반복하지 않도록.
$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);
. 그들은 같은 일을하지 않습니까?
다음은 예제를 설정하기위한 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=?
PREPARE
하면서 이미 구문 분석 된 고정 명명 된 문을 만듭니다 (예 : 입력에 관계없이 문이 더 이상 변경되지 않음) EXECUTE
. PREPARE
세션 기간 만 있기 때문에 실제로는 psql 스크립트를 통한 주입을 막기위한 것이 아니라 성능상의 이유 인 것 같습니다. psql 액세스의 경우 저장 프로 시저에 권한을 부여하고 procs 내에서 매개 변수를 바인딩 할 수 있습니다.
기본적으로 준비된 진술을 통해 잠재적 인 해커로부터 들어오는 데이터는 데이터로 취급되며 응용 프로그램 SQL과 혼합되거나 SQL로 해석 될 수있는 방법이 없습니다. 응용 프로그램 SQL).
준비된 명령문은 먼저 효율적인 쿼리 계획을 찾기 위해 SQL 쿼리를 "준비"하고 나중에 폼에서 나온 실제 값을 전송하므로 쿼리가 실제로 실행되기 때문입니다.
더 좋은 정보는 다음과 같습니다.
나는 그 해답을 읽고 준비된 진술의 본질을 밝히는 핵심 요점을 강조 할 필요성을 여전히 느꼈다. 사용자 입력이 관련된 데이터베이스를 쿼리하는 두 가지 방법을 고려하십시오.
순진한 접근
하나는 사용자 입력을 일부 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"
위의 경우에는 실행되지 않습니다.
간단히 말해서, 준비된 문장으로 사용자 입력을 통해 소개 된 악성 코드는 실행되지 않습니다!
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 삽입을 피하십시오 ...
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");
서블릿에 있다고 가정 해 봅시다. 악의적 인 사람이 '필터'에 대해 나쁜 값을 전달하면 데이터베이스를 해킹 할 수 있습니다.
근본 원인 # 1-구분자 문제
따옴표를 사용하여 문자열을 구분하고 문자열의 일부가되기 때문에 SQL 삽입이 가능하기 때문에 때로는 해석 할 수 없습니다. 문자열 데이터에 사용할 수없는 구분 기호가 있으면 SQL 삽입이 발생하지 않았을 것입니다. 분리 문자 문제를 해결하면 SQL 삽입 문제가 제거됩니다. 구조 쿼리가 그렇게합니다.
근본 원인 # 2-인간의 본성, 사람들은 교묘하고 일부 교활한 사람들은 악의적 이며 모든 사람들 은 실수를합니다
SQL 주입의 또 다른 근본 원인은 인간 본성입니다. 프로그래머를 포함한 사람들은 실수를합니다. 구조적 쿼리에서 실수를해도 시스템이 SQL 삽입에 취약하지 않습니다. 구조적 쿼리를 사용하지 않는 경우 실수로 인해 SQL 삽입 취약점이 발생할 수 있습니다.
구조적 쿼리가 SQL 주입의 근본 원인을 해결하는 방법
구조적 쿼리는 하나의 명령문에 sql 명령을 넣고 별도의 프로그래밍 명령문에 데이터를 넣어서 구분 기호 문제점을 해결합니다. 프로그래밍 문은 필요한 분리를 만듭니다.
구조화 된 쿼리는 인적 오류가 중요한 보안 허점을 만드는 것을 방지합니다. 실수를 저지르는 사람과 관련하여 구조 쿼리를 사용하면 SQL 삽입이 발생할 수 없습니다. 구조적 쿼리를 포함하지 않는 SQL 주입을 방지하는 방법이 있지만, 이러한 접근 방식의 정상적인 인적 오류는 일반적으로 SQL 주입에 약간 노출됩니다. 구조적 쿼리는 SQL 주입에서 안전하지 않습니다. 거의 모든 다른 프로그래밍과 마찬가지로 구조적 쿼리를 사용하여 세상에서 모든 실수를 저지를 수는 있지만 SQL 주입으로 인수 할 수있는 것은 없습니다. 이것이 사람들이 이것이 SQL 주입을 막는 올바른 방법이라고 말하는 이유입니다.
따라서 SQL 주입의 원인과 사용할 때 불가능한 자연 구조적 쿼리가 있습니다.