만족

[PHP] Magic Quote는 정말 SQL Injection을 완벽 방어할까? 본문

[PHP] Magic Quote는 정말 SQL Injection을 완벽 방어할까?

Backend/PHP Satisfaction 2020. 10. 26. 01:35

SQL Injection이란?

 

SQL 주입(삽입)이라고도 부르며, 개발자의 의도대로 동작하지 않는 구문을 추가하여 피해를 입히는 공격 방식이다.

 

요약짤

SQL Injection 예시

const removeAccount= (accountId)=>{
	database.executeQuery(`DELETE FROM Account WHERE id='${accountId}'`);
};

다음과 같은 코드가 있다고 해 보자.

 

유저가 정상적인 id를 입력할 때는 상관 없지만, 

공격자가 accountId 값에 

 

1' OR 1=1; --

를 입력하게 되면 쿼리문은 다음과 같이 변한다.

 

DELETE FROM Account WHERE id='1' OR 1=1;

따라서 모든 계정이 삭제되는 결과가 초래된다.

 

이런 식으로 데이터를 몽땅 날려버리거나, 

유저 정보를 조회하는 쿼리에 이 공격을 받으면 모든 유저 정보를 탈취할 수도 있다.

 

www.bloter.net/archives/280164

 

“2016년 가장 흔한 웹 공격은 SQL인젝션”

펜타시큐리티, 2016 웹 공격 동향 분석 보고서 발표

www.bloter.net

매우 흔한 공격임과 동시에 상당한 파괴력도 지니고 있기 때문에, 반드시 막아주어야 한다.

 

먼저 흔한 오해를 바로잡고 가보자.

 

PHP에선 Magic Quote가 있기 때문에 신경쓰지 않아도 된다?

Magic Quote는 따옴표 속에서 스트링 결합 시 스트링 변수를 자동으로 이스케이핑해 합쳐주는 기능이다.

(addslashes()와 동일하게 동작한다)

$accountId= "my'id";
$query= "DELETE FROM Account WHERE id='$accountId'";

//따옴표 속 $accoundId는 이스케이핑되어 my\'id가 된다.
//최종적으로 $query는 DELETE FROM Account WHERE id='my\'id';가 된다

 

결론부터 말하자면 틀렸다.

 

아래의 포스트를 확인해보시라.

 

blog.naver.com/PostView.nhn?blogId=skinfosec2000&logNo=220535626029&proxyReferer=https:%2F%2Fwww.google.com%2F

 

PHP addslashes(), magic_quotes_gpc 우회를 통한 SQL Injection 공격

addslashes(), magic_quotes_gpc 우회를 통한 SQL 인젝션 공격 가능 일부 웹 사이트에서 데이터베이스 ...

blog.naver.com

짧게 요약하자면,

 [어떤 특수한 문자]' (예: %aa')

의 경우 따옴표가 \'로 이스케이핑 되고 문자열 인코딩을 하는 과정에서 앞의 특수한 문자와 \가 하나로 합쳐져

따옴표가 이스케이핑되지 않는 상황이 발생한다는 것이다.

 

따라서 Magic Quote든, addslashes던 공격자에게 조금의 귀찮음을 유발할 뿐, 실질적인 방어 대책이 되지는 못한다는 뜻이다.

 

결국은 동적 SQL 구성을 막아야만 한다

유일한 해결법은 PreparedStatement를 통해 미리 SQL을 구조화시켜 동적 SQL 구성을 막아버리는 것이다.

 

<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);

// Check connection
if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}

// prepare and bind
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);

// set parameters and execute
// 첫 번쨰 쿼리 실행
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();

// 두 번쨰 쿼리 실행
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();

// 세 번쨰 쿼리 실행
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();

echo "New records created successfully";

// 끝!
$stmt->close();
$conn->close();
?>

 

preparedStatement를 이용한 쿼리 실행은 

prepare 시 쿼리를 미리 컴파일하고

bind/execute할 때 컴파일된 쿼리를 사용하기 때문에

동일한 쿼리 템플릿을 사용할 경우, 사용하지 않을 때보다 월등한 속도의 이점도 있다.

 

이제 statement는 가급적 지양하는 편이 좋겠다.



Comments