사자자리

[PHP & MySQL] 보안: Filtering & Escaping 본문

웹기초/생활코딩 WEB 3 - PHP & MySQL

[PHP & MySQL] 보안: Filtering & Escaping

renne 2022. 8. 3. 21:53

보안

보안의 주요 사고

 

입력 Filtering 들어오는 정보 중 문제 있는 정보를 막기
출력 Escaping 문제 있는 정보가 이미 들어와있을 때, 그 정보가 사용자들에게 노출되는 것을 막기

 

mysqli_real_escape_string( )

 - 인자로 들어온 데이터 중에서 SQL을 주입하는 공격과 관련된 기호들을 문자로 바꾸는 API

 - 즉, SQL Injection을 막을 수 있다.

<!--htdocs\index.php-->
<!--htdocs\create.php-->
<?php
(생략)
    //id값이 있을 때, 즉 목차를 클릭했을 때 description을 불러오기
    if(isset($_GET['id'])){
      $filtered_id = mysqli_real_escape_string($conn, $_GET['id']);
      $sql = "SELECT * FROM topic WHERE id={$filtered_id}";
      $result = mysqli_query($conn, $sql);
      $row = mysqli_fetch_array($result);
      $article['title'] = $row['title'];
      $article['description'] = $row['description'];
    }
?>
<!--htdoc\process_create.php-->
<?php
$conn = mysqli_connect('localhost', 'root', '021103', '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())
";
$result = mysqli_query($conn, $sql);

if($result === false){
  echo "ERROR";
  error_log(mysqli_query($conn));
}
else {
  echo "성공했습니다.\n<a href=\"index.php\">BACK TO PAGE</a>";
}
?>

 

SQL Injection 공격 실습

SQL 주석: --  (-- 뒤에 띄어쓰기 하나 있어야 함)

MariaDB [opentutorials]> -- SELECT * FROM topic;
MariaDB [opentutorials]>

 

NOW( ) 함수는 SQL Injection 공격을 이용해서 다른 값으로 바꿀 수 있다. 이를 실행하기 위해서는 mysqli_multi_query( ) 함수를 사용해야 한다. 이 함수는 복수의 SQL문을 실행시켜주며, 보안적인 문제가 있다. 그래서 하나의 SQL문만 실행시킬 수 있는 mysqli_query( )함수를 사용하는 것이다.

 

SQL Injection 공격을 단순 문자열로 바꿔주는 mysqli_real_escape_string 함수를 안 쓰고, 복수의 SQL문을 실행시켜주는  mysqli_multi_query 함수를 쓴 상태에서 해킹을 시도해 보자.

<!--htdoc\process_create.php-->
<?php
$conn = mysqli_connect('localhost', 'root', '021103', 'opentutorials');
$filtered = array(
  'title'=>($_POST['title']),	#mysqli_real_escape_string 함수 없음
  'description'=>($_POST['description'])
);

$sql="
  INSERT INTO topic(title, description, created)
  VALUES('{$filtered['title']}', '{$filtered['description']}', NOW())
";
$result = mysqli_multi_query($conn, $sql);	#mysqli_multi_query 함수 씀

if($result === false){
  echo "ERROR";
  error_log(mysqli_query($conn));
}
else {
  echo "성공했습니다.\n<a href=\"index.php\">BACK TO PAGE</a>";
}
?>

 

위와 같이 입력함으로써 생성된 SQL문이다.

INSERT INTO topic(title, description, created) VALUES('I am', 'First', '2022-08-02 00:00:00');-- NOW())

 

실제로 NOW 함수가 주석처리 되고, 시간이 조작되었다.

MariaDB [opentutorials]> SELECT * FROM topic;
+----+------------+-------------------+---------------------+
| id | title      | description       | created             |
+----+------------+-------------------+---------------------+
|  4 | MySQL      | MySQL is ...      | 2022-08-01 23:23:34 |
|  5 | Oracle     | Oracle is ...     | 2022-08-01 23:24:00 |
|  6 | SQL Server | SQL Server is ... | 2022-08-01 23:24:17 |
|  7 | MongoDB    | MongoDB is ...    | 2022-08-01 23:24:31 |
|  8 | MariaDB    | MariaDB is ...    | 2022-08-02 21:34:31 |
|  9 | I am       | First             | 2022-08-02 00:00:00 |
+----+------------+-------------------+---------------------+
6 rows in set (0.000 sec)

 

그러나, mysqli_real_escape_string 함수를 사용하면

<!--htdoc\process_create.php-->
<?php
$conn = mysqli_connect('localhost', 'root', '021103', '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())
";
$result = mysqli_multi_query($conn, $sql);

if($result === false){
  echo "ERROR";
  error_log(mysqli_query($conn));
}
else {
  echo "성공했습니다.\n<a href=\"index.php\">BACK TO PAGE</a>";
}
?>

 

SQL문으로 위장하여 공격을 시도했던 기호들이 단순한 문자로 취급된 것을 볼 수 있다. (작은따옴표 앞에 \가 추가됨)

MariaDB [opentutorials]> SELECT * FROM topic;
+----+------------+------------------------------------+---------------------+
| id | title      | description                        | created             |
+----+------------+------------------------------------+---------------------+
|  4 | MySQL      | MySQL is ...                       | 2022-08-01 23:23:34 |
|  5 | Oracle     | Oracle is ...                      | 2022-08-01 23:24:00 |
|  6 | SQL Server | SQL Server is ...                  | 2022-08-01 23:24:17 |
|  7 | MongoDB    | MongoDB is ...                     | 2022-08-01 23:24:31 |
|  8 | MariaDB    | MariaDB is ...                     | 2022-08-02 21:34:31 |
|  9 | I am       | First                              | 2022-08-02 00:00:00 |
| 10 | I am       | First', '2022-08-02 00:00:00');--  | 2022-08-02 22:06:39 |
+----+------------+------------------------------------+---------------------+
7 rows in set (0.001 sec)

 

XSS(Cross Site Scripting)

사용자를 다른 사이트로 이동시키는 자바스크립트문을 description에 입력한다.

 

생성된 XSS 목차를 클릭하니 실제로 저 사이트로 이동된다.

htmlspecialchars 함수

 - HTML의 특수 문자가 일반 문자로 인식되게 바꾸는 함수

 - 예를 들어 < 와 >는 각각 &lt; 와 &gt; 로 바뀐다.

<!--htdocs\index.php-->
<?php
//title을 목차로 만들기
$conn = mysqli_connect('localhost', 'root', '021103', 'opentutorials');
$sql = "SELECT * FROM topic";
$result = mysqli_query($conn, $sql);
$list = '';
while($row = mysqli_fetch_array($result)){
  $escaped_title = htmlspecialchars($row['title']);
  $list = $list."<li><a href=\"index.php?id={$row['id']}\">{$escaped_title}</a></li>";
}
//기본적인 상태
$article = array(
  'title'=>'Welcome',
  'description'=>'Hello, WEB'
);
//id값이 있을 때, 즉 목차를 클릭했을 때 description을 불러오기
if(isset($_GET['id'])){
  $filtered_id = mysqli_real_escape_string($conn, $_GET['id']);
  $sql = "SELECT * FROM topic WHERE id={$filtered_id}";
  $result = mysqli_query($conn, $sql);
  $row = mysqli_fetch_array($result);
  $article['title'] = htmlspecialchars($row['title']);
  $article['description'] = htmlspecialchars($row['description']);
}
?>

 

입력한 것이 그냥 기호로 출력된다.

 

Comments