본문 바로가기

개발과제/보안추가

[노말틱 모의 해킹 취업반 개발과제 (3)] 보안 관련해서 보완 하기

안녕하세요! 저는 이태까지 GET방식이든 POST 방식이든 일단 받으면  mysqli escape 시켜서 

 

SQL Injection을 피하려고 했습니다! 근데 이제 스크립트 인젝션을 방지하기 위해 filter_var() 함수을 이용하겠습니다.

 

숫자값이 들어와야 하는데 숫자 이외의 값이 들어오는 것을 걸러내는 필터링 작업해 주는 함수도 있습니다

 

filter_var() 함수 입니다.

 

예시 보겠습니다

 

input에 악의적으로 alert을 썻다고 합시다.

 

쨘! 제가 바라지 않은 자바스크립트가 나옵니다

 

이렇게 악의적으로 쓸 수 있습니다. 이제 output에는 filter_var 함수로 걸러지고 나오게 해 보았습니다.

 

이번엔 output 표출이에요

 

쨘!

 

이렇게 자바 스크립트가 아예 실행 안 되는 것을 볼 수 있습니다.

실제로 저렇게 html 구문인 <,>,'," 등 html에 쓰이는 특수기호를 전부 무시합니다.

 

근데 또 불안하죠 호출은 안전하지만 내부에서 말썽을 피울 수 있기 때문입니다!

그야 < > 이런 특수 기호는 사라져도 script라는 클라이언트에 실행 가능한 자바 스크립트는 남아있기 때문이죠

 

그때! 같이 쓰는 함수가 있습니다 자바 스크립트 관련 태그를 없애주는 함수 strip_tags() 입니다!

 

함께 보시죠

 

사라졌네요!

 

여기서 또 주의할 게 있습니다.

 

 

이거는 filter하고 strip한 것 입니다!

 

이러면 뭐가 나올까요? 그저 순서만 바뀌었는데 말이죠

 

쨘!

이번엔 script가 나왔습니다! 이 차이는 filter_var에서는 특수 기호만 인코딩을 하여 다른 값으로 바꿉니다.

그리고 호출할 땐 그 인코딩의 특수기호가 그대로 나오죠 근데! strip는 인코딩을 한 값을 그대로 받아들여서

 

<script> 태그를 확인 못하고 그대로 표출하게 됩니다.

 

이걸 방지하기 위해 일반적으로 strip_tags 함수 먼저 이용하고 난 뒤에 filter 함수를 적용한다고 합니다!

 

특수기호만 바뀌어도 대부분의 악의적인 스크립트는 막히지만 행여나 불안해서 이 strip_tags 함수 쓴다고 보시면 돼요.

 

 

 

 

저는 strip_tags()filter_var() 함수 이용해 모든 GET방식, POST로 받은 값들을 보호하겠습니다!

.

.

.

.

.

.

.

.

조금.. 고된 작업이 되겠네요.. 그래도 보안을 위해서 차차 해봅시다.

 

우선 로그인 페이지 먼저 가겠습니다.

 

-login.php

딱히 고칠 게 없네요 패스

 

다음은 회원가입입니다

-join.php

에도 딱히 없네요 패스

 

아! id란에 placeholder 적겠습니다

전 경고했어요!

 

 

-process_login.php

 

비밀번호는 어차피 해쉬로 저장을 해서 이러한 특수 기호를 작성해도 상관없습니다.

 

근데 ID는 손 보겠습니다.

만약 ID가 이메일 형식으로 적으시라고 하셨으면 

// 변수 정의 및 초기화
$email = "john.doe@example.com";

// FILTER_VALIDATE_EMAIL 필터 사용하여 이메일 유효성 검사
if (filter_var(strip_tags($email), FILTER_VALIDATE_EMAIL)) {
    echo "유효한 이메일입니다.";
} else {
    echo "유효하지 않은 이메일입니다.";
}

 

이 형식으로 하시면 돼요!

 

근데 저는 이메일 작성란이 딱히 없기에 이렇게 작성하면 되는구나라고만 알고 있겠습니다.

 

어쨌든 총코드 입니다.

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    //데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password,$dbname);

    session_start(); //세션 시작
    //오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }
    //POST로 전달된 정보 받기
    $login_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['id']),FILTER_SANITIZE_SPECIAL_CHARS));
    $login_pw = mysqli_real_escape_string($conn,$_POST['pw']);

    //ID 찾는 쿼리문
    $sql = "SELECT * FROM LOGIN_INFO WHERE id='$login_id'";

    //쿼리 실행
    $result = mysqli_query($conn, $sql);

    //쿼리 실행 결과 확인
    if(mysqli_num_rows($result) > 0 ) { 
        //ID있으니 비밀번호 검증
        $row = mysqli_fetch_array($result)  ;
        $hashed_pw = $row['pw']; //결과 배열 중 pw을 가져온다
        if(password_verify($login_pw,$hashed_pw)) {
            //로그인 성공
            session_regenerate_id(); //ID 자동 갱신
            $_SESSION['login_id'] = $row['id'];
            header("Location: only_login.php");
        } else {
            //로그인 실패
            $_SESSION['login_error'] = "비밀번호가 일치하지 않습니다.";
            header("Location: login.php");
        }
    }
    else {
        //로그인 실패
        $_SESSION['login_error'] = '아이디 또는 비밀번호가 일치 하지 않습니다.';
        header("Location: login.php");
    }
?>

 

 

 

 

 

-process_join.php

주소는 어차피 입력 불가하죠? 비밀번호는 해쉬로 저장하니 상관없고요

역시 ID만 손보겠습니다

 

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    session_start(); //세션 시작

    //데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password,$dbname);

    //데이터베이스 오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }
    
    
    //POST로 전달된 정보 받기
    $login_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['id']),FILTER_SANITIZE_SPECIAL_CHARS));
    $login_pw = mysqli_real_escape_string($conn,$_POST['pw']);
    $adr = trim(mysqli_real_escape_string($conn,$_POST['address']));
    
    //비밀번호는 해싱
    $hashed_pw = password_hash($login_pw, PASSWORD_DEFAULT);

    //암호화 키
    $encryption_key = 'my_secret_key';
    
    //주소는 암호화
    $encrypted_address = openssl_encrypt($adr, 'aes-256-cbc', $encryption_key, OPENSSL_ZERO_PADDING, '1234567890123456');

    //ID 중복 검사용 sql문
    $check_id = "SELECT id FROM LOGIN_INFO WHERE id='$login_id' ";

    //중복 검사 sql문 실행
    $result_id = mysqli_query($conn, $check_id);

    if(mysqli_num_rows($result_id) > 0 ) { 
        //중복이 있을때
        $_SESSION['join_error'] = '아이디가 중복입니다!';
        header("Location: join.php");
    } else {
        //중복이 아닐때
        //새로운 사용자를 데이터베이스에 삽입문
        $sql = "INSERT INTO $dbname (id, pw, adr)
        VALUES ('$login_id','$hashed_pw','$encrypted_address')";

        //sql문 실행
        if(mysqli_query($conn,$sql)) {
            $_SESSION['join_error'] = '회원가입이 완료되었습니다!';
            header("Location: join.php");
        } else {
            $_SESSION['join_error'] = '회원가입 오류가 발생하였습니다.';
            header("Location: join.php");
        }

    }

    //회원가입 후 닫기
    exit();
?>

 

 

-only_login.php

딱히 없네요 패스

 

-process_logout.php

여기도 없네요! 바로 header로 이동하고 session 종료시키니 문제가 없을 겁니다.

 

-board.php

page랑 search는 그대로 하는데 날짜가 말썽이죠! YYYY-MM-DD 이 사이에 있는 - 때문이죠

 

그러니 format 함수를 이용해 날짜 값으로 바꾸겠습니다

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Welcome!</title>
    </head>
    <body>
        <h1>MENU</h1>
        <form method ="GET" action="board.php">
            <input type = "text" name="search" placeholder="검색">
            <input type="submit" value="검색">
            <label for="date">날짜 선택:</label>
            <input type="date" id="date_value" name="date_value">
        </form>
        <p>
        <?php 

            include 'DB_INFO.php'; //데이터 베이스 정보

            //데이터베이스 연결
            $conn = mysqli_connect($host,$username,$password,$db_board);

            //데이터베이스 오류시 종료
            if(mysqli_connect_errno()) {
                die("데이터 베이스 오류: ". mysqli_connect_error());
            }

            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }

            //한 페이지에 보여줄 게시물 수
            $num_per_page = 5;

            //현재 페이지 번호
            if(isset($_GET['page'])) {
                $page = mysqli_real_escape_string($conn,filter_var(strip_tags(intval($_GET['page'])),FILTER_SANITIZE_SPECIAL_CHARS));
            } else {
                $page = 1;
            }

            //전체 게시물 수 조회
            $total_sql = "SELECT count(*) AS cnt FROM $db_board";

            //작성된 게시물들 조회 문
            $sql = "SELECT board_id,id,title,views FROM $db_board";

            
            //검색이 입력된 경우
            if(isset($_GET['search'])) {
                $search = mysqli_real_escape_string($conn, filter_var(strip_tags($_GET['search']),FILTER_SANITIZE_SPECIAL_CHARS));
                $date_value = mysqli_real_escape_string($conn, $_GET['date_value']);
                $date_value = DateTime::createFromFormat('Y-m-d', $_GET['date_value'])->format('Y-m-d'); //날짜 값으로 다시 바꾼다
                
                if(isset($_GET['date_value'])) {
                    //날짜 선택한 경우
                    $sql = "SELECT board_id,id,title,date_value,views FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                }
                else {
                    //날짜가 선택이 안된 경우
                    $sql = "SELECT board_id,id,title,views FROM $db_board WHERE title LIKE '%$search%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%'";
                }
            }
            
            $total_result = mysqli_query($conn,$total_sql); //sql문 실행
            $total_row = mysqli_fetch_assoc($total_result); //결과를 배열로 가져온다
            $total_posts = $total_row['cnt']; //배열 중 cnt의 값을 가져온다

            //전체 페이지 수
            $total_pages = ceil($total_posts / $num_per_page);

            //각 페이지 시작 인덱스
            $start = ($page - 1) * $num_per_page;


            //sql문에 추가로 작성하고 LIMIT 설정하고 DESC 즉 내림차순으로 정렬
            $sql .= " ORDER BY board_id DESC LIMIT $start, $num_per_page";

            //쿼리문 실행
            $result = mysqli_query($conn, $sql);

            if(mysqli_num_rows($result) > 0) {
                //게시물 출력
                while($row = mysqli_fetch_assoc($result)) {
                    $index = $row['board_id'];
                    $id = $row['id'];
                    $title = $row['title'];
                    echo '<p><a href="view_board.php?index='.$index.'">'.$title.'</a> : '.$id.'작성함 조회수: '.$row['views'].'</p>';
                }
            } else {
                echo "게시물이 없습니다!";
            }
            
            echo '<p> [ ';
            
            // 현재 페이지를 10으로 나눈 몫에 10을 곱한 값에서 1을 빼면
            // 현재 페이지가 몇 번째 페이지 블록에 있는지 알 수 있습니다.
            $block_start = floor(($page-1)/10)*10;

            if ($block_start > 1) {
                // 이전 페이지 블록이 있다면, 다음 버튼을 출력합니다.
                $next_block_start = $block_start - 10;
                if($next_block_start == 0) {
                    $next_block_start += 1; //0페이지는 존재 하지 않기에 1더한다
                }
                echo '<a href="?page=' . $next_block_start .'"><< </a>';
            }

            // 이전 버튼 출력
            if ($page > 1) {
                echo '<a href="?page=' . ($page-1) .'">< </a> ';
            }
            
            // 페이지 링크 출력
            for ($i = $block_start+1; $i <= min($block_start+10, $total_pages); $i++) {
                if($i == $page) {
                    echo '<strong>'.$i.' </strong>';
                } else {
                    echo '<a href="?page=' . $i .'">'.$i. '</a> ';
                }
                echo ' ';
            }

            // 다음 버튼 출력
            if ($page < $total_pages) {
                echo '<a href="?page=' . ($page+1) .'">> </a> ';
            }
            
            if ($block_start+10 < $total_pages) {
                // 다음 페이지 블록이 있다면, 다음 버튼을 출력합니다.
                $next_block_start = $block_start + 11;
                echo '<a href="?page=' . $next_block_start .'">>></a>';
            }

            echo ' ]</p>';
            
        ?>
        </p>
        <p></p>
        <form action="write_board.php" method="POST"> 
            <p><input type="submit" name="write" value="게시판 작성"></p>
        </form>
        <form action="only_login.php"> 
            <p><input type="submit" value="메인페이지로"></p>
        </form>
    </body>
</html>

 

 

 

-view_board.php

GET으로 index을 받고 있네요! 고치겠습니다.

 

그리고 fix_board.php로 갈 때 GET방식으로 주게 하겠습니다.

또한 파일명이 1_1_happy.txt 일 때 1만 나오는 에러를 해결하기 위해

array_slice 이용해 _기준을 나누고 맨 앞이 저희가 중복 이름 피하기 위해 작성한 날짜 값을 제외시키겠습니다.

 

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>게시물 보는중</title>
    </head>
    <body>
        <p>
        <?php 

            include 'DB_INFO.php'; //데이터 베이스 정보

            //데이터베이스 연결
            $conn = mysqli_connect($host,$username,$password,$db_board);

            //데이터베이스 오류시 종료
            if(mysqli_connect_errno()) {
                die("데이터 베이스 오류: ". mysqli_connect_error());
            }

            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }

            //GET방식으로 전달된 index 받는다
            $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_GET['index']),FILTER_SANITIZE_SPECIAL_CHARS));
            //가독성 위해 다른 변수에 저장
            $last_viewed_time_for_board_id = 'last_view_time_'.$board_id;

            // 세션에 마지막 조회 시간 정보가 있는지 확인
            if (!isset($_SESSION[$last_viewed_time_for_board_id])) {
                // 세션에 마지막 조회 시간 정보가 없으면 현재 시간을 저장
                $_SESSION[$last_viewed_time_for_board_id] = time();
            } else {
                // 세션에 마지막 조회 시간 정보가 있으면 일정 시간이 지났는지 확인
                $last_view_time = $_SESSION[$last_viewed_time_for_board_id];
                $current_time = time();
                $time_diff = $current_time - $last_view_time;
                if ($time_diff >= 60) { // 60초(1분) 이상 지났으면 조회수 증가
                //조회수 증가하는 sql문
                $sql_view = "UPDATE $db_board SET views = views + 1 WHERE board_id = '$board_id'";
                //실행
                $result_view = mysqli_query($conn, $sql_view);

                // 세션에 현재 조회 시간을 저장
                $_SESSION[$last_viewed_time_for_board_id] = $current_time;
                }
            }

            //작성된 게시물들 조회 문
            $sql = "SELECT * FROM $db_board WHERE board_id = '$board_id' ";
            //쿼리문 실행
            $result = mysqli_query($conn, $sql);
            
            //게시물 출력
            while($row = mysqli_fetch_assoc($result)) {
                echo '<p>조회수: '.$row['views'].'</p>';
                echo '<p>ID: '.$row['id'].'</p>';
                echo '<p>Title: '.$row['title'].'</p>';
                echo '<p>Detail: '.$row['detail'].'</p>';
                echo '<p>Date: '.$row['date_value'].'</p>';
                if (isset($row['file_name'])) {
                    $file_name = implode('_', array_slice(explode('_', $row['file_name']), 1)); // '_' 문자를 기준으로 분리 후 첫 번째 요소를 제외한 나머지 요소를 모두 합쳐서 파일명을 구성합니다.
                    $download_url = 'download_process.php?filename='.$row['file_name'];
                    echo '<p>FILE: <a href="'.$download_url.'">'.$file_name.'</a></p>';
                }
                
                //로그인한 사용자의 정보 가져온다
                $user_id = $_SESSION['login_id'];

                //게시물 작성자와 로그인한 ID와 일치한 경우
                if($row['id'] == $user_id) {
                    //게시물 수정 버튼을 보여준다
                    echo "<form method='GET' action='fix_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>게시물 수정</button></p>
                          </form>";

                    //게시물 삭제 버튼을 보여준다
                    echo "<form method='POST' action='delete_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>삭제</button></p>
                          </form>";
                }
            }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['write_error'])) {
                    echo $_SESSION['write_error'];
                    unset($_SESSION['write_error']);
                }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['file_error'])) {
                    echo $_SESSION['file_error'];
                    unset($_SESSION['file_error']);
                }
        ?>
        </p>
        <p></p>
        <form action="write_board.php" method="POST"> 
            <p><input type="submit" name="write" value="게시판 작성"></p>
        </form>
        <form action="board.php">
            <p><input type="submit" value="메인페이지로"></p>
        </form>
    </body>
</html>

 

 

 

 

-write_board.php

여기도 딱히 없네요!

 

 

-write_process_board.php

POST로 board_id,title,detail을 받는 것을 수정하겠습니다.

 

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    session_start(); //세션 시작

    if(!isset($_SESSION['login_id'])) {
        //로그인하지 않은 사용자
        header("Location: login.php"); //login 화면으로 바꾼다
        exit(); //이 페이지를 바로 닫는다
    }

    //게시판 데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password, $db_board);

    //데이터베이스 오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }

    //POST로 전달된 정보 받기
    if(isset($_POST['board_id'])){
        $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['board_id']),FILTER_SANITIZE_SPECIAL_CHARS));
    } else {
        $board_id = null;
    }
    $title = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['title']),FILTER_SANITIZE_SPECIAL_CHARS));
    $detail = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['detail']),FILTER_SANITIZE_SPECIAL_CHARS));

    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file'])) {
        // 업로드된 파일 정보 가져오기
        $file_name = $_FILES['file']['name'];
        $timestamp = time(); // 현재 시간을 초로 반환
        $new_file_name = $timestamp . '_' . $file_name; // 현재 시간과 원래 파일 이름을 합쳐 새로운 파일 이름 생성
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif','text/plain','application/zip','application/x-hwp','application/msword','application/vnd.ms-excel','application/pdf']; //MIME 허락 된 것
        //확장자 허락 된 것
        $allowed_extensions = array("jpg","png","gif","txt","zip","hwp","word","xls","xlsx","pdf");

        //sql공격 방지용
        $new_file_name = mysqli_real_escape_string($conn,$new_file_name);

        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {
            // 파일 MIME 타입 확인, 조작됬는지 확인
            $file_mime_type = mime_content_type($file_tmp_name);
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $file_mime_type = finfo_file($finfo, $file_tmp_name);
            finfo_close($finfo);
            
            if (in_array($file_mime_type, $allowed_mime_types)) {

                //파일 확장자 확인
                $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
                if(in_array($file_ext, $allowed_extensions)) {
                // 파일 저장 경로
                $upload_path = '/path/to/upload/directory/' . $new_file_name;

                    // 파일 이동 및 저장
                    if (move_uploaded_file($file_tmp_name, $upload_path)) {
                        $_SESSION['file_error'] =  '파일 업로드 성공';
                    } else {
                        $_SESSION['file_error'] =  '파일 업로드 실패';
                    }
                } else {
                    $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
                }
            } else {
                $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
            }
        }
    }
    //session으로 유저 이름 받기
    $user_id = $_SESSION['login_id'];
    echo $board_id;
    if(isset($board_id)) {
        //board_id가 있다는 것은 수정을 의미
        $sql = "UPDATE $db_board SET title = '$title', detail='$detail', file_name='$new_file_name' WHERE board_id='$board_id' ";
    }
    else {
        //board_id가 없으니 새로 만드는 sql문
        $sql = "INSERT INTO $db_board (id, title, detail,file_name) VALUES ('$user_id', '$title', '$detail','$new_file_name')";
    }

    //sql문 실행
    if(mysqli_query($conn,$sql)) {
        //수정인 경우 바로 게시판 보이게 함
        if(isset($board_id)) {
            $_SESSION['write_error'] = '수정되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
        else {
            $board_id = mysqli_insert_id($conn); // 새로 생성한 게시물의 id를 가져옴
            $_SESSION['write_error'] = '작성되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
    } else {
        $_SESSION['write_error'] = '작성 중 오류가 발생하였습니다.';
        header("Location: view_board.php");
    }

    exit();
?>

 

 

 

-delete_board.php

여기서도 POST로 받는 board_id을 수정하겠습니다.

 

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    session_start(); //세션 시작
    
    if(!isset($_SESSION['login_id'])) {
        //로그인하지 않은 사용자
        header("Location: login.php"); //login 화면으로 바꾼다
        exit(); //이 페이지를 바로 닫는다
    }
    
    //게시판 데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password, $db_board);
    
    //데이터베이스 오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }

    //POST로 전달된 정보 받기
    $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['board_id']),FILTER_SANITIZE_SPECIAL_CHARS));

    //session으로 유저 이름 받기
    $user_id = $_SESSION['login_id'];
    
    //삭제용 sql문 작성
    $sql = "DELETE FROM $db_board WHERE board_id=$board_id";

    //실행
    if(mysqli_query($conn,$sql)) {
        //실행 후 게시판으로 이동
        header("Location: board.php");
    } else {
        //오류 발생 시 메시지 호출
        $_SESSION['write_error'] = '삭제하는데 실패했습니다! 조금 있다 하십시오';
        header("Location: view_board.php");
    }
    

    exit();
?>

 

 

 

-download_process.php

 

filename을 손 보겠습니다

 

<?php
    if(isset($_GET['filename'])) {
        // 파일 이름 및 경로 가져오기
        $filename = filter_var(strip_tags($_GET['filename']),FILTER_SANITIZE_SPECIAL_CHARS);
        $filepath = '/path/to/upload/directory/'.$filename;
    
        // 파일이 존재하는지 확인
        if(file_exists($filepath)) {
            // 다운로드 헤더 설정
            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            readfile($filepath);
            exit;
        } else {
            echo '파일이 존재하지 않습니다.';
        }
    } else {
        echo '파일 이름이 전달되지 않았습니다.';
    }
?>

 

그리고.. zip형식 안에 exe파일이 들어가 있는 상태로 게시물을 올릴 수가 있습니다.

그러니 클라이언트는 exe파일을 함부로 누르면 안 되며 패치가 안된 소프트웨어도 조심해야만 합니다!

 

그러니 서버에는 zip형식이라 서버 측에는 아무런 피해가 없지만

다운로드한 zip안에 바이러스 같은 것은 막을 수가 없습니다.

 

그 형식은 오로지 서버가 아닌 클라이언트를 노리고 하는 것이니깐요.

그러니 이 현상을 피하는 것은 클라이언트들의 숙제이자 조심해야만 하는 겁니다!

 

넘어가겠습니다.
제가 어렸을 때 함부로 블로그에 있는 스타크래프트 파일 받다가 된통 맞았거든요

 

 

-fix_board.php

GET방식으로 오는 board_id 만 수정하면 되겠네요!

 

그리고 업로드된 파일도 수정가능하게 코드 추가 하겠습니다!

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>게시판 수정 중</title>
    </head>
    <body>

        <?php 
            include 'DB_INFO.php'; //데이터 베이스 정보

            //데이터베이스 연결
            $conn = mysqli_connect($host,$username,$password,$db_board);

            //데이터베이스 오류시 종료
            if(mysqli_connect_errno()) {
                die("데이터 베이스 오류: ". mysqli_connect_error());
            }
            
            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }
            //GET방식으로 board_id 받기
            $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_GET['board_id']),FILTER_SANITIZE_SPECIAL_CHARS));

            //작성된 게시물들 조회 문
            $sql = "SELECT * FROM $db_board WHERE board_id = '$board_id' ";

            //쿼리문 실행
            $result = mysqli_query($conn, $sql);

            //결과값 가져오기
            $row = mysqli_fetch_assoc($result);
        ?>
        <form action="write_process_board.php" method="POST" enctype="multipart/form-data"> 
            <p><input type="text" name="title" maxlength="44" value="<?php echo $row['title']; ?>" placeholder="제목 입력, 최대 44자까지 가능합니다"></p>
            <p><textarea name="detail" rows="20" cols="20" maxlength="254" placeholder="내용 작성,최대 254자 가능합니다"><?php echo $row['detail']; ?></textarea></p>
            <p><input type="file" name="file" id="fileToUpload"></p>
            <?php
                if (isset($row['file_name'])) {
                    $file_name = implode('_', array_slice(explode('_', $row['file_name']), 1)); // '_' 문자를 기준으로 분리 후 첫 번째 요소를 제외한 나머지 요소를 모두 합쳐서 파일명을 구성합니다.
                    echo '<p>FILE: '.$file_name.'</p>';
                }
            ?>
            <p><input type="submit" value="수정하기"></p>
            <input type="hidden" name='board_id' value="<?php echo $row['board_id']; ?>">
        </form>

        <p>
        <?php session_start();
                if (isset($_SESSION['write_error'])) {
                    echo $_SESSION['write_error'];
                    unset($_SESSION['write_error']);
                }
        ?>
        </p>
    </body>
</html>

 

 

 

끝입니다!

 

좋아요.. XSS 공격이라 하죠. 이걸 방지하기 위해 보안 함수 2개를 이용했고

 

mysqli escape 함수 이용해 sql인젝션 공격을 방지했습니다!

 

누락된 게 있을 수 있습니다. 

만약 에러나  보안 문제가 생기면 그때 고치도록 하죠!

.

.

.

.

파일 업로드 보안, SQL 인젝션 보안, 스크립트 인젝션 보안 좋아요!

 

이제 나중에는 세션 하이재킹 보안을 해보도록 하겠습니다!