PHP에서 SQL 인젝션을 보다 효율적으로 방어하는 방법 (MySQL)

Program/PHP

SQL 인젝션이 무엇인가요?

SQL 인젝션은 비정상적인 방법으로 웹/앱 서비스를 사용하여 SQL 쿼리에 직접 공격을 가하는 가장 기초적인 서비스 공격방식입니다.

 

여기에서는 MySQL에서 사용되는 SQL 인젝션을 막는 방법을 서술하고자 합니다.

 

SQL 인젝션이 왜 발생하나요?

개발자의 보안 인식 결여, 마감 시간에 쫒기다 보니 보안 처리를 하지 않음(...) 등 여러가지 이유가 있습니다.

PHP PDO 등을 사용하면 SQL 인젝션을 충분히 막을 수 있지만,

기존에 사용되는 소스코드나 서비스에는 적용하기가 쉽지 않은게 현실입니다.

 

시작하기 전에 드리는 말씀...

1. SQL 인젝션 방어 코드에 addslashes를 사용하지 말아주세요. - PHP 설정에 따라 오히려 털릴 수도 있습니다.

2. htmlspecialchars를 사용해도, 인젝션 방어에 그리 큰 도움이 되지 않을 수 있습니다.

3. strip_tags를 사용하지 말아주세요. 태그 싹 날아갑니다... ㅠ...

 

그럼 시~~~작!

SQL 인젝션을 방어하기 위해선 다음과 같은 함수를 사용하면 큰 도움이 됩니다.

그리고 캐릭터셋은 반드시 UTF-8인게 좋습니다. EUC-KR, CP949를 사용하면 취약점이 생깁니다.

 

1. 범용적으로 인젝션을 막는 방법

Tip. 게시판 html이나, 검색 질의어, 기타 클라이언트가 사용하기 좋은 다용도의 목적.

<?php
// 1번
mysqli_real_escape_string($링크, '여기에 내용을 집어넣기!');

// 1번 사용이 매번 링크를 넣기 불편하다면...
$link = mysqli_connect('서버주소','아이디','패스워드','데이터베이스 이름');

function sql_escape(String $content){
  global $link;
  return mysqli_real_escape_string($link, $content);
}

 

위 처럼 쓰면 일단 다 필터링은 되어서 좋은데, 문제점이 특정 문자만 필터링하려면 어떻게 사용해야할까요?

 

2. 숫자만 나오게 필터링 하기

만약 숫자만 삽입해야하는데, 임의의 문자가 들어가기 염려스럽다면...

Tip. 휴대폰 번호 작성할 때, 생년월일/주민등록번호 등

<?php
// content를 string으로 두는 이유는, 
// int형식으로 설정하는 이유는..
// 만약 휴대폰 전화번호가 010-1234-5678 이라면,
// 10-1234-5678 처럼 저장되기 때문입니다.

function only_number(String $content){
  return preg_replace('#[^0-9]#', '', $content);
}

 

3. 숫자와 영문자만 나오게 필터링하기

만약 숫자와 영문자만 들어가야 한다면..

Tip. 아이디 만들때 주로 사용

<?php
// 내용에 있는 A-Z는 기호에 맞게 수정하시면 됩니다.
function only_alpha_number(String $content){
  return preg_replace('#[^a-zA-Z0-9]#', '', $content);
}

 

4. 한글만 나오게 필터링 하기

만약 한글만 들어가야 한다면...

Tip. 이름 입력

<?php
function only_hangul(String $content){
  return preg_replace('#[^가-힣]#', '', $content);
}

 

번외 - htmlspecialchars 를 써야할 때

<?php
$link = mysqli_connect('서버주소','아이디','패스워드','데이터베이스 이름');

function sql_escape(String $content){
  global $link;
  return mysqli_real_escape_string($link, $content);
}

$search_text = sql_escape($stx);

$search_query = mysqli_query(
  $link,
  "SELECT
    `title`, `content`
  FROM
    `books_library`
  WHERE
    `title` like '%{$search_text}%'
    || `content` like '%{$search_text}%';
  "
);

// 검색 과정 처리
// ~~~~~~~~~~~~~
// 검색 과정 처리 끝

?>

<!-- 여러분이 입력한 값을 그대로 반환할 때 (1번) -->
당신의 검색어 : <?php echo htmlspecialchars($stx); ?>

<!-- SQL에 저장 된 값을 그대로 반환할 때 (2번) -->
찾은 내용들 : 
<?php
for(~~~~~){
  echo "제목 : ".htmlspecialchars($변수['title']);
}
?>

 

이 작업이 너무 귀찮아요..

PDO를 사용하시면 SQL 인젝션에 대해서 안전합니다.

PDO를 사용하시거나..

 

라라벨에서 사용하는 데이터베이스 오브젝트화 패키지가 있습니다.

컴포저를 다룰 줄 아시면 쉽게 사용할 수 있는데요.

composer require illuminate/database

저걸로 설치한 뒤,

SIR에 있는 게시물(아래에 링크)을 활용하시면 됩니다.

https://sir.kr/g5_tip/11220

 

illuminate/database 사용하기 > SIR

SQL injection 을 피해갈수 있는 가장 확실한(?!) 방법

 

- 일단 컴포저를 사용할 수 있는 서버환경에서 패키지를 내려받습니다.
    composer require illuminate/database
- 로컬 로컬

sir.kr

 

이상입니다.

Name(이름)
Password(비밀번호)
Homepage(홈페이지)
Secret(비밀글)