본문 바로가기

코딩공부/WEB3 - PHP & MySQL

[6] PHP & MySQL 보안

데이터베이스를 연동한 접점에서 발생하는 사고들이 많다. 입력단과 출력단에서 보안적인 사고가 일어날 수 있다.

  • filtering(필터링) : 사용자가 입력한 정보에서 문제가 될만한 정보를 차단하는 행위
  • escaping(이스케이핑) : 저장되어있는 정보를 사용자에게 노출할 때 발생할 수 있는 문제를 차단하는 행위

 

'보안'에 있어서 고려할 원칙

1. 사용자로부터 정보를 받는 순간부터, 사용자가 입력하는 정보는 불신해야 한다.

2. 어떻게 불신해야 하는가.. 불신의 기술

 

입력 공격 차단

id라는 컬럼에 사용자가 sql 문을 입력하게 되면, 다음과 같이 코드의 sql 문에 직접적으로 들어가게 된다.

 

▶ 어떻게 해결해야 할까?

PHP의 mysqli_real_escape_string() 함수를 이용하면 된다.

인자로 들어온 데이터 중에서 sql injection 공격과 관련된 여러 가지 기호들을 문자로 바꾸는 함수이다.

  $filtered_id = mysqli_real_escape_string($conn, $_GET['id']);
  $sql = "SELECT * FROM topic WHERE id=$filtered_id";

 

create.php 파일의 사용자가 입력할 수 있는 부분에도 필터링 적용

입력한 정보를 submit 하면 process_create.php 로 전송되기 때문에, process_create.php 에서 사용자가 입력한 정보를 직접 받고 있는 코드를 수정

$filtered 배열에 정제된 데이터를 담고, 이 $filtered 에 담긴 데이터로 sql문을 생성한다.

$filtered = array(
  'title'=>mysqli_real_escape_string($conn, $_POST['title']),
  'description'=>mysqli_real_escape_string($conn, $_POST['description'])
);

$sql = "
  INSERT INTO topic
    (title, description, created)
    VALUES(
        '{$filtered['title']}',
        '{$filtered['description']}',
        NOW()
    )
";

 

 

 

SQL Injection 차단

 

mysqli_multi_query() : 다수개의 sql문 실행이 가능한 함수. ※주의※

보안적인 문제로 '단 하나의 sql문만을 실행'하는 mysqli_query() 함수를 사용하는 것을 권장

 

die($sql); 

$sql을 출력하고 이후 코드를 실행하지 않음

 

▶ SQL Injection 예제

INSERT INTO topic (title, description, created) VALUES( 'Hehe', 'haha', NOW() )

INSERT INTO topic (title, description, created) VALUES( 'Hehe', 'haha', '2018-1-1 00:00:00');-- ', NOW() )

 

 

1. mysqli_real_escape_string() 함수를 쓰지 않고, 다중 sql문 실행이 가능한 mysql_multi_query()함수로 코드를 수정한 뒤,

본문 입력 필드에 SQL Injection 시도

 

2. DB 조회를 통해 사용자가 입력한 날짜로 저장된 것을 확인할 수 있다.

 

3. Filtering 적용

<?php
$conn = mysqli_connect(
  'localhost',
  'root',
  '111111',
  'opentutorials');

$filtered = array(
  'title'=>mysqli_real_escape_string($conn, $_POST['title']),
  'description'=>mysqli_real_escape_string($conn, $_POST['description'])
);

$sql = "
  INSERT INTO topic
    (title, description, created)
    VALUES(
        '{$filtered['title']}',
        '{$filtered['description']}',
        NOW()
    )
";
echo $sql;
// die($sql);
$result = mysqli_query($conn, $sql);
if($result === false){
  echo '저장하는 과정에서 문제가 생겼습니다. 관리자에게 문의해주세요';
  error_log(mysqli_error($conn));
} else {
  echo '성공했습니다. <a href="index.php">돌아가기</a>';
}
?>

 

sql문을 조작할 수 있는 쿼터(') 앞에 백 슬래시(\) 처리를 하여 문자열로 인식하도록 하여 SQL Injection 을 막을 수 있다.

 

 

출력 공격(XSS) 차단

오염된 데이터가 들어왔을 때, 그것이 사용자에게 노출되지 않도록 하는 방법

 

사용자가 데이터를 입력하면서 악의적인 목적으로 자바스크립트를 주입할 수 있다.

다른 사용자가 8. atack 게시글을 클릭하면 스크립트 태그로 인해 다른 웹사이트로 이동된다.

 

 

▶ '<'는 html에서 태그의 시작을 알리는 특수한 기호이다. html에서 '<'를 문자 그대로 출력하고 싶다면?

'<'를 '&lt;' 로, '>'를 '&gt;' 로 쓰면 된다.

 

PHP에서는 htmlspecialchars()를 이용해서 html에서 문법적인 역할이 있는 태그들을 문자 그대로 화면에 출력해준다.

이러한 것들을 '우리가 그 임무로부터 해제시킨다'라는 의미에서 'escape'라고 한다.

 

 

애플리케이션에 적용

index.php에서 사용자에 의해서 입력한 정보들은 escape string 처리를 해야 한다. 

 

id 값은 auto increment를 통해서 자동으로 증가된 것이기 때문에 처리할 필요가 없다.

title과 description에 htmlspecialchars() 함수를 적용하고 attack 게시글을 클릭하면, 더 이상 스크립트가 실행되지 않고 문자열로 출력되는 것을 확인할 수 있다.

 

 

모든 시스템을 "입력", "저장", "출력" 3가지로 나누어서 3가지 요소 모두 어떤 공격의 요소가 있는지 확인해보면 된다.

[참고] PHP 보안