그냥보고 :
(출처 : https://xkcd.com/327/ )
이 SQL의 기능은 무엇입니까?
Robert'); DROP TABLE STUDENTS; --
나는 둘 다 알고 '
있고 --
주석이지만 DROP
, 같은 줄의 일부이므로 단어에 주석이 달리지 않습니까?
그냥보고 :
(출처 : https://xkcd.com/327/ )
이 SQL의 기능은 무엇입니까?
Robert'); DROP TABLE STUDENTS; --
나는 둘 다 알고 '
있고 --
주석이지만 DROP
, 같은 줄의 일부이므로 단어에 주석이 달리지 않습니까?
답변:
학생 테이블을 떨어 뜨립니다.
학교 프로그램의 원래 코드는 다음과 같습니다.
q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";
이것은 쿼리에 텍스트 입력을 추가하는 순진한 방법이며 보시 다시피 매우 나쁩니다 .
이름, 중간 이름 텍스트 상자 FNMName.Text ( Robert'); DROP TABLE STUDENTS; --
) 및 성 이름 텍스트 상자 LName.Text (호출 Derper
)의 값이 나머지 쿼리와 연결되면 결과는 실제로 두 개의 쿼리로 구분됩니다. 명령문 종결 자 (세미콜론). 두 번째 쿼리는 첫 번째 쿼리에 삽입 되었습니다. 코드가 데이터베이스에 대해이 쿼리를 실행하면 다음과 같습니다.
INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')
일반 영어로 두 가지 쿼리로 대략적으로 번역됩니다.
이름 값이 'Robert'인 Students 테이블에 새 레코드를 추가하십시오.
과
학생 테이블 삭제
두 번째 쿼리 이후의 모든 내용 은 주석으로 표시됩니다 . --', 'Derper')
'
학생의 이름은이 닫는의 댓글 아닌 문자열 분리 . 학생의 이름은 문자열이므로 가상 쿼리를 완료하려면 구문 적으로 필요합니다. 주입 공격은 주입 한 SQL 쿼리가 유효한 SQL 결과를 얻을 때만 작동 합니다 .
INSERT
이면 괄호가 더 의미가 있습니다. 또한 데이터베이스 연결이 읽기 전용 모드가 아닌 이유도 설명합니다.
INSERT
. 거꾸로 생각하면 SELECT
테이블에 Little Bobby Tables 삽입이 이미 테이블을 삭제했기 때문에 어쨌든 실행되지 않습니다.
Students
하나 이상의 열 (원래의 올바른 명령문이 두 개의 열을 제공함) 이상을 기대하기 때문에 실패 합니다. 즉, 두 번째 열이 있으면 주석이 필요한 이유를 보여주는 데 도움이됩니다. Bobby의 이름을 바꿀 수 없기 때문에이 관찰을 각주로 거의 그대로 두는 것이 가장 좋습니다.
이름이 변수에 사용되었다고 가정 해 봅시다 $Name
.
그런 다음이 쿼리 를 실행하십시오 .
INSERT INTO Students VALUES ( '$Name' )
코드가 실수로 사용자가 변수로 제공 한 것을 배치하고 있습니다.
SQL 이되기를 원했습니다 .
학생의 가치관에 삽입하기 ( ' Robert Tables`)
그러나 영리한 사용자는 원하는 것을 제공 할 수 있습니다.
학생의 가치관에 삽입하기 ( ' Robert'); DROP TABLE Students; --')
당신이 얻는 것은 :
INSERT INTO Students VALUES ( 'Robert' ); DROP TABLE STUDENTS; --' )
그 --
줄의 나머지 부분 만 주석으로 처리합니다.
다른 사람들이 이미 지적했듯이, ');
마지막 진술 은 닫히고 두 번째 진술이 이어집니다. PHP와 같은 언어를 포함한 대부분의 프레임 워크는 현재 하나의 SQL 문자열에 여러 명령문을 허용하지 않는 기본 보안 설정을 가지고 있습니다. 예를 들어, PHP에서는 mysqli_multi_query
함수 를 사용하여 하나의 SQL 문자열에서만 여러 명령문을 실행할 수 있습니다 .
그러나 두 번째 명령문을 추가하지 않고도 SQL 삽입을 통해 기존 SQL 문을 조작 할 수 있습니다. 이 간단한 선택으로 사용자 이름과 비밀번호를 확인하는 로그인 시스템이 있다고 가정 해 봅시다.
$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);
당신이 제공하는 경우 peter
사용자 이름으로 및 secret
암호와 같은 결과 SQL 문자열은 다음과 같을 것이다 :
SELECT * FROM users WHERE username='peter' and (password='secret')
모든것이 괜찮아. 이제이 문자열을 암호로 제공한다고 가정하십시오.
' OR '1'='1
그런 다음 결과 SQL 문자열은 다음과 같습니다.
SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')
암호를 몰라도 모든 계정에 로그인 할 수 있습니다. 따라서 SQL 삽입을 사용하기 위해 두 개의 명령문을 사용할 필요는 없지만 여러 명령문을 제공 할 수 있으면 더 파괴적인 작업을 수행 할 수 있습니다.
아니요, '
SQL의 주석이 아니라 구분 기호입니다.
엄마는 데이터베이스 프로그래머가 다음과 같이 요청했다고 가정했습니다.
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');
(예를 들어) $xxx
형식을 확인하거나 특수 문자를 이스케이프하지 않고 변수 내용을 HTML 양식에서 직접 가져온 새 학생을 추가합니다 .
따라서 데이터베이스 프로그램이 $firstName
포함되어 있으면 Robert'); DROP TABLE students; --
DB에서 다음 요청을 직접 실행합니다.
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');
즉. insert 문을 일찍 종료하고 크래커가 원하는 악성 코드를 실행 한 다음 나머지 코드가 있으면 주석 처리합니다.
음, 나는 너무 느리다. 나는 주황색 밴드에서 나의 앞에 8 개의 대답을 벌써 본다. :-) 대중적인 주제, 그것은 보인다.
- 응용 프로그램은에 시도하지 않고,이 경우 '낸시'의 입력을 받아 들인다 - 같은 특수 문자를 이스케이프로, 입력을 살균 학교 => INSERT INTO의 학생 VALUES ( '낸시' ); INSERT 0 1 -데이터베이스 명령으로의 입력이 조작 될 때 SQL 주입이 발생합니다 .-데이터베이스 서버가 임의의 SQL School 을 실행하게합니다 => 학생들에게 INSERT INTO VALUES ( 'Robert' ) DROP TABLE 학생 ; - '); INSERT 0 1 DROP 테이블 -학생 기록이 이제 사라졌습니다. 훨씬 더 나빠졌을 수도 있습니다! 학교 => 선택 * 학생들 로부터 ; 오류 : 관계 "학생" 이 존재 하지 않습니다 라인 1 : 선택 * 학생들 로부터 ; ^
( 이 답변의 모든 코드 예제는 PostgreSQL 9.1.2 데이터베이스 서버에서 실행되었습니다. )
무슨 일이 일어나고 있는지 명확히하기 위해 이름 필드 만 포함하는 간단한 테이블로 시도하고 단일 행을 추가하십시오.
학교 => CREATE TABLE의 학생 ( 이름 TEXT PRIMARY KEY를 ); 주의 사항 : CREATE TABLE / PRIMARY KEY를 합니다 만들 암시 인덱스 "students_pkey"를 위한 테이블 "학생들은" CREATE TABLE의 학교 => INSERT INTO 학생 VALUES ( '요' ); INSERT 0 1
애플리케이션이 다음 SQL을 사용하여 데이터를 테이블에 삽입한다고 가정 해 봅시다.
INSERT INTO의 학생 VALUES ( '는 foobar' );
foobar
학생의 실제 이름으로 교체하십시오 . 일반적인 삽입 작업은 다음과 같습니다.
-입력 : Nancy school => 학생 INSERT INTO VALUES ( 'Nancy' ); INSERT 0 1
테이블을 쿼리하면 다음과 같은 결과를 얻습니다.
학교 => 선택 * 학생들 로부터 ; 이름 ------- 남자 낸시 ( 2 행 )
Little Bobby Tables의 이름을 테이블에 삽입하면 어떻게됩니까?
-입력 : Robert '); DROP TABLE 학생; - 학교 => 학생 INSERT INTO VALUES ( 'Robert' ); DROP TABLE 학생 ; - '); INSERT 0 1 DROP 테이블
여기에 SQL 삽입은 명령문을 종료하고 별도의 DROP TABLE
명령을 포함하는 학생 이름의 결과입니다 . 입력 끝에있는 두 개의 대시는 남은 코드를 주석 처리하여 오류가 발생할 수 있습니다. 출력의 마지막 행은 데이터베이스 서버가 테이블을 삭제했음을 확인합니다.
INSERT
조작 중에 응용 프로그램이 특수 문자에 대한 입력을 점검하지 않으므로 임의의 입력을 SQL 명령에 입력 할 수 있습니다. 이는 악의적 인 사용자가 일반적으로 사용자 입력을 목적으로하는 필드에 따옴표와 같은 특수 기호를 임의의 SQL 코드와 함께 삽입하여 데이터베이스 시스템이이를 실행하도록하여 SQL 주입 을 수행 할 수 있음을 의미합니다 .
결과?
학교 => 선택 * 학생들 로부터 ; 오류 : 관계 "학생" 이 존재 하지 않습니다 라인 1 : 선택 * 학생들 로부터 ; ^
SQL 주입은 운영 체제 또는 응용 프로그램 의 원격 임의 코드 실행 취약점 과 동등한 데이터베이스 입니다. 성공적인 SQL 주입 공격의 잠재적 영향은 데이터베이스 시스템 및 응용 프로그램 구성에 따라 공격자가 데이터 손실 (이 경우와 같이)을 유발하거나 데이터에 대한 무단 액세스를 얻거나 실행하는 데 사용될 수 있습니다. 호스트 시스템 자체의 임의 코드
XKCD 만화에서 알 수 있듯이 SQL 주입 공격으로부터 보호하는 한 가지 방법은 특수 문자를 이스케이프 처리하는 등의 방법으로 기본 SQL 명령을 수정할 수 없으므로 임의의 SQL 코드가 실행될 수 없도록 데이터베이스 입력을 삭제하는 것입니다. SqlParameter
ADO.NET에서 사용하는 것과 같이 매개 변수화 된 쿼리를 사용하는 경우 SQL 주입을 방지하기 위해 최소한 입력이 자동으로 삭제됩니다.
그러나 응용 프로그램 수준에서 입력을 삭제해도 고급 SQL 주입 기술이 중단되지 않을 수 있습니다. 예를 들어, PHP 함수 를 우회하는 방법이 mysql_real_escape_string
있습니다 . 추가 보호를 위해 많은 데이터베이스 시스템이 준비된 명령문을 지원 합니다 . 백엔드에서 올바르게 구현 된 경우 준비된 명령문은 데이터 입력을 의미 적으로 나머지 명령과 분리하여 처리하여 SQL 삽입을 불가능하게 할 수 있습니다.
다음과 같이 학생 생성 방법을 순진하게 작성했다고 가정 해보십시오.
void createStudent(String name) {
database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}
그리고 누군가 이름을 입력합니다 Robert'); DROP TABLE STUDENTS; --
데이터베이스에서 실행되는 것은 다음 쿼리입니다.
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')
세미콜론은 삽입 명령을 종료하고 다른 명령을 시작합니다. -나머지 줄을 주석으로 처리합니다. DROP TABLE 명령이 실행됩니다 ...
이것이 바인드 매개 변수가 좋은 이유입니다.
작은 따옴표는 문자열의 시작과 끝입니다. 세미콜론은 문장의 끝입니다. 그래서 그들이 이와 같은 선택을하고 있다면 :
Select *
From Students
Where (Name = '<NameGetsInsertedHere>')
SQL은 다음과 같습니다.
Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
-- ^-------------------------------^
일부 시스템에서는 select
먼저 drop
명령문 이 실행됩니다! 메시지는 다음과 같습니다. SQL에 값이 없습니다. 대신 매개 변수를 사용하십시오!
데이터베이스 작성자가 아마
sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);
student_name이 주어진 이름이면 "Robert"라는 이름으로 선택을 수행 한 다음 테이블을 삭제합니다. "-"부분은 주어진 쿼리의 나머지 부분을 주석으로 변경합니다.
이 경우 '는 주석 문자가 아닙니다. 문자열 리터럴을 구분하는 데 사용됩니다. 만화가는 문제의 학교에 다음과 같은 동적 SQL이 있다고 생각합니다.
$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";
이제 '문자는 프로그래머가 예상하기 전에 문자열 리터럴을 끝냅니다. 와 결합하여; 공격자는 이제 원하는 SQL을 추가 할 수 있습니다. 끝에-주석은 원래 명령문에 남아있는 SQL이 서버에서 쿼리를 컴파일하지 못하게하는 것입니다.
FWIW, 나는 또한 문제의 만화가 중요한 세부 사항이 잘못되었다고 생각 합니다. 만화가 제안한 것처럼 데이터베이스 입력을 살균 하는 것에 대해 생각 하고 있다면 여전히 잘못하고 있습니다. 대신 데이터베이스 입력 을 격리 하는 관점에서 생각해야하며 이를 수행하는 올바른 방법은 매개 변수화 된 쿼리를 사용하는 것입니다.
작동 방식 : 관리자가 학생의 기록을 찾고 있다고 가정합니다.
Robert'); DROP TABLE STUDENTS; --
관리자 계정에는 높은 권한이 있으므로이 계정에서 테이블을 삭제할 수 있습니다.
요청에서 사용자 이름을 검색하는 코드는
이제 쿼리는 다음과 같습니다 (학생 테이블을 검색하기 위해)
String query="Select * from student where username='"+student_name+"'";
statement.executeQuery(query); //Rest of the code follows
결과 쿼리는
Select * from student where username='Robert'); DROP TABLE STUDENTS; --
사용자 입력이 삭제되지 않았으므로 위의 쿼리는 두 부분으로 조작됩니다.
Select * from student where username='Robert');
DROP TABLE STUDENTS; --
이중 대시 (-)는 쿼리의 나머지 부분을 주석 처리합니다.
비밀번호 인증이있는 경우 비밀번호 인증을 무효화 할 수 있으므로 위험합니다.
첫 번째는 일반 검색을 수행합니다.
두 번째 계정은 계정에 충분한 권한이있는 경우 테이블 학생을 삭제합니다 (일반적으로 학교 관리자 계정은 이러한 쿼리를 실행하고 위에서 언급 한 권한을 갖습니다).
SELECT* FROM sutdents ...
-당신은 "s"를 잊어 버렸습니다. 이것은 당신이 떨어지는 것입니다. DROP TABLE STUDENTS;
아무도 전에 이것을 지적하지 않았으므로 나는 당신 중 일부에게 경고 할 수 있습니다.
대부분 양식 입력을 패치하려고합니다. 그러나 이것이 SQL 인젝션으로 공격받을 수있는 유일한 곳은 아닙니다. GET 요청을 통해 데이터를 보내는 URL로 매우 간단한 공격을 수행 할 수 있습니다. 다음 예를 고려하십시오.
<a href="/show?id=1">show something</a>
URL은 http://yoursite.com/show?id=1로 보입니다.
이제 누군가 이런 식으로 시도 할 수 있습니다
http://yoursite.com/show?id=1;TRUNCATE table_name
table_name을 실제 테이블 이름으로 바꾸십시오. 그가 당신의 테이블 이름을 알면 그들은 당신의 테이블을 비울 것입니다! (단순한 스크립트로이 URL을 강제로 처리하는 것은 매우 쉽습니다)
귀하의 검색어는 다음과 같습니다.
"SELECT * FROM page WHERE id = 4;TRUNCATE page"
<?php
...
$id = $_GET['id'];
$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch();
/************* You have lost your data!!! :( *************/
...
<?php
...
$id = $_GET['id'];
$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...