사자자리
[PHP & MySQL] 보안: Filtering & Escaping 본문
보안
보안의 주요 사고
입력 | 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의 특수 문자가 일반 문자로 인식되게 바꾸는 함수
- 예를 들어 < 와 >는 각각 < 와 > 로 바뀐다.
<!--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']);
}
?>
입력한 것이 그냥 기호로 출력된다.
'웹기초 > 생활코딩 WEB 3 - PHP & MySQL' 카테고리의 다른 글
[PHP & MySQL] DELETE (0) | 2022.08.07 |
---|---|
[PHP & MySQL] UPDATE (0) | 2022.08.07 |
[PHP & MySQL] 테이블에서 데이터를 가져와서 PHP에서 활용하기 (0) | 2022.08.03 |
[PHP & MySQL] 웹페이지에 입력한 데이터를 테이블에 추가하기 (0) | 2022.08.03 |
[PHP & MySQL] PHP와 MySQL의 연동 (0) | 2022.08.02 |