개발 블로그

[웹 보안] 방어 기법 적용 본문

Study/Web Security

[웹 보안] 방어 기법 적용

카메얀짱 2026. 5. 26. 04:43

🐢 안녕하세요, 카메입니다.
저번에 공격 실험을 수행했던 취약한 웹 페이지를 오늘은 여러 방어 기법들을 적용하여 취약점을 막는 실습을 하려고 합니다.


방어 1: Prepared Statement 적용 (로그인 페이지)

sudo nano /var/www/html/login_secure.php
<?php
$conn = mysqli_connect("localhost", "webuser", "webpass123", "web_security");

if (isset($_POST['login'])) {
    $user = $_POST['username'];
    $pass = $_POST['password'];

    // Prepared Statement 사용
    $stmt = mysqli_prepare($conn, "SELECT * FROM users WHERE username=? AND password=?");
    mysqli_stmt_bind_param($stmt, "ss", $user, $pass);
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);

    echo "<h3>입력한 값:</h3>";
    echo "<p>아이디: $user</p>";
    echo "<p>비밀번호: $pass</p>";

    if (mysqli_num_rows($result) > 0) {
        $row = mysqli_fetch_assoc($result);
        echo "<h2 style='color:green;'>로그인 성공! 환영합니다, " . $row['username'] . "님</h2>";
    } else {
        echo "<h2 style='color:red;'>로그인 실패! 아이디 또는 비밀번호가 틀렸습니다.</h2>";
    }
}
?>

<html>
<head><title>로그인 (보안 적용)</title></head>
<body>
    <h1>로그인 페이지 (Prepared Statement 적용)</h1>
    <form method="POST">
        아이디: <input type="text" name="username"><br><br>
        비밀번호: <input type="text" name="password"><br><br>
        <button type="submit" name="login">로그인</button>
    </form>
</body>
</html>

 
방어 2: 입력값 검증 및 필터링 적용 (검색 페이지)

sudo nano /var/www/html/search_secure.php
<?php
$conn = mysqli_connect("localhost", "webuser", "webpass123", "web_security");

if (isset($_GET['keyword'])) {
    $keyword = $_GET['keyword'];

    // 입력값 필터링: 특수문자 이스케이프
    $keyword_safe = mysqli_real_escape_string($conn, $keyword);

    // 추가 필터링: SQL 키워드 차단
    $blocked = array('UNION', 'SELECT', 'ORDER BY', 'DROP', 'DELETE', 'INSERT', '--', '#');
    $is_blocked = false;
    foreach ($blocked as $word) {
        if (stripos($keyword, $word) !== false) {
            $is_blocked = true;
            break;
        }
    }

    echo "<h3>입력한 검색어: $keyword</h3>";

    if ($is_blocked) {
        echo "<h2 style='color:red;'>차단됨! 허용되지 않는 입력이 감지되었습니다.</h2>";
    } else {
        $query = "SELECT * FROM board WHERE title LIKE '%$keyword_safe%'";
        $result = mysqli_query($conn, $query);

        if ($result && mysqli_num_rows($result) > 0) {
            echo "<table border='1' cellpadding='8'>";
            echo "<tr><th>ID</th><th>제목</th><th>내용</th><th>작성자</th></tr>";
            while ($row = mysqli_fetch_assoc($result)) {
                echo "<tr>";
                echo "<td>" . $row['id'] . "</td>";
                echo "<td>" . $row['title'] . "</td>";
                echo "<td>" . $row['content'] . "</td>";
                echo "<td>" . $row['author'] . "</td>";
                echo "</tr>";
            }
            echo "</table>";
        } else {
            echo "<p>검색 결과가 없습니다.</p>";
        }
    }
}
?>

<html>
<head><title>게시판 검색 (보안 적용)</title></head>
<body>
    <h1>게시판 검색 (입력값 검증 적용)</h1>
    <form method="GET">
        검색어: <input type="text" name="keyword">
        <button type="submit">검색</button>
    </form>
</body>
</html>

 
방어 3: Prepared Statement 적용 (Blind 페이지)

sudo nano /var/www/html/blind_secure.php
<?php
$conn = mysqli_connect("localhost", "webuser", "webpass123", "web_security");

if (isset($_GET['id'])) {
    $id = $_GET['id'];

    // 입력값 검증: 숫자만 허용
    if (!ctype_digit($id)) {
        echo "<h3>입력한 ID: $id</h3>";
        echo "<h2 style='color:red;'>차단됨! ID는 숫자만 입력할 수 있습니다.</h2>";
    } else {
        $stmt = mysqli_prepare($conn, "SELECT * FROM board WHERE id=?");
        mysqli_stmt_bind_param($stmt, "i", $id);
        mysqli_stmt_execute($stmt);
        $result = mysqli_stmt_get_result($stmt);

        echo "<h3>입력한 ID: $id</h3>";

        if ($result && mysqli_num_rows($result) > 0) {
            echo "<h2 style='color:green;'>게시글이 존재합니다.</h2>";
        } else {
            echo "<h2 style='color:red;'>게시글이 존재하지 않습니다.</h2>";
        }
    }
}
?>

<html>
<head><title>게시글 조회 (보안 적용)</title></head>
<body>
    <h1>게시글 조회 (입력값 검증 + Prepared Statement 적용)</h1>
    <form method="GET">
        게시글 ID: <input type="text" name="id">
        <button type="submit">조회</button>
    </form>
</body>
</html>

 
동작 확인: 방어 테스트
이제 이전에 성공했던 공격들을 방어 버전에서 다시 시도하여 확인합니다.
 
테스트 1: http://localhost/login_secure.php

 
' OR 1=1 -- / aaa → 이번에는 로그인 실패가 뜹니다.
 
테스트 2: http://localhost/search_secure.php

 
' UNION SELECT 1,username,password,4 FROM users -- → "차단됨!" 메시지가 뜹니다.
 
테스트 3: http://localhost/blind_secure.php

 
1 AND 1=1 → "숫자만 입력할 수 있습니다" 메시지가 뜹니다.