[Web Application-모의해킹] SQL Injection : 시큐어 코딩(Prepared Statement) #3-4

2024. 4. 27. 11:25Project/Web Application-모의해킹

 

시큐어 코딩 : Prepared Statement

 

1) 개요

 

  Project:Web Site 웹 사이트 로그인 페이지 user_id 파라미터에서 발견된 SQL Injection 취약점을 방지하기 위하여 대응 방안 중 하나인 Prepared Statement 기능을 구현하고 적용시켜 실제로 SQL Injection 공격을 방지할 수 있는지 확인한다.

 

 

2) 방법

 

  Prepared Statement 기능은 사용자 입력 값과 마찬가지로 Client Side에서 구현을 하게 되면 공격자에 의해 변조될 가능성이 존재하므로 보안을 위해 Server Side에서 구현을 한다.

 

 

3) 시큐어 코딩

 

[1] login_proc.php 페이지 전문

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
        // 로그인 페이지에서 전송된 변수 저장
        $user_id = $_POST['user_id'];
        $user_pass = $_POST['user_pass'];
 
        session_start();
 
        // URL 직접 입력 접근 방지
        if(!strlen($user_id|| !strlen($user_pass))
        {?>
            <script>
                alert('권한이 없습니다.');
                window.location.replace('/php/login.php');
            </script>
            <?php
                exit();
        }
 
        define('DB_SERVER''localhost');
        define('DB_USERNAME''db_id');
        define('DB_PASSWORD''db_pass');
        define('DB_NAME''db_name');
        
        $connect = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
        $sql"SELECT * FROM test_user WHERE user_id=?";
        
        $stmt = $connect->stmt_init();
        $stmt = $connect->prepare($sql);
        $stmt->bind_param('s'$user_id);
        $stmt -> execute();
        $result = $stmt->get_result();
        $row = $result->fetch_assoc();
        $stmt->close();
 
        // 입력된 비밀번호와 데이터베이스의 비밀번호가 일치여부를 비교하여 일치하면 TRUE, 불일치하면 FALSE를 반환한다.
        $match_pass = password_verify($user_pass$row['user_pass']);
 
        // $match_pass 값이 TRUE인 경우(비밀번호 일치)
        if($match_pass){
            $_SESSION['user_id'= $row['user_id'];
            header("Location:/php/");
            exit();
        }
 
        // $match_pass 값이 FALSE인 경우(비밀번호 불일치)
        else{?>
            <script>
                alert('아이디 혹은 비밀번호가 틀렸습니다.');
                window.location.replace('/php/login.php');
            </script>
        <?php
        }
?>
cs

 

[2] Prepared Statement 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
        define('DB_SERVER''localhost');
        define('DB_USERNAME''db_id');
        define('DB_PASSWORD''db_pass');
        define('DB_NAME''db_name');
        
        $connect = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
        $sql"SELECT * FROM test_user WHERE user_id=?";
        
        $stmt = $connect->stmt_init();
        $stmt = $connect->prepare($sql);
        $stmt->bind_param('s'$user_id);
        $stmt -> execute();
        $result = $stmt->get_result();
        $row = $result->fetch_assoc();
        $stmt->close();
?>
 
cs

 

  login_proc.php 페이지에서 사용된 Preparde Statement 코드는 19번 라인부터 33번 라인까지이다. Prepared Statement 코드는 DB로 전달하는 SQL 쿼리의 사용자 입력 값을 바인드 변수인 '?'로 입력해야 한다. 위 코드에서 $sql 변수에 입력된 SQL 쿼리를 보면 user_id의 사용자 입력 값이 '?'로 되어있는 것을 볼 수 있다. 해당 위치의 실제 값은 12번 라인의bind_param() 함수를 통해 입력할 수 있다. 위 코드처럼 구현을 하게 된다면 더 이상 $user_id 파라미터에서는 SQL Injection 공격이 통하지 않게 된다.

 

[3] 동작 화면

 

 

  로그인 페이지에 접속하여 로그인 가능한 계정 정보를 입력하고 로그인 버튼을 클릭한다. 사용된 계정은 vanhart, 비밀번호는 123123이다.

 

페이로드 : vanhart' and '1'='1

 

  모의해킹에서 SQL Injection 취약점을 발견했을 때 사용한 페이로드를 입력하고 로그인 가능한지 확인한다. 위 페이로드를 아이디 입력 칸에 입력하고 로그인 버튼을 클릭한다.

 

 

  로그인 버튼을 클릭하게 되면 '아이디 혹은 비밀번호가 틀렸습니다.'라는 경고문과 함께 로그인 페이지로 이동된다.

 

페이로드 : vanhart'+and+'1'='2

 

  위와 같은 페이로드를 입력하여 다시 시도해봐도 '아이디 혹은 비밀번호가 틀렸습니다.'라는 경고문과 함께 로그인 페이지로 이동되는 것으로 보아 SQL Injection 공격이 통하지 않는다는 것을 확인할 수 있다.