Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
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
Archives
Today
Total
관리 메뉴

쟝이의 세상

시큐어 코딩이 적용된 게시판 만들기 Window (HTTP, PHP, SQL Server) 본문

자료

시큐어 코딩이 적용된 게시판 만들기 Window (HTTP, PHP, SQL Server)

zyangee 2024. 9. 6. 17:19

"보안을 강화하기 위한 코드 수정" 에서 대부분의 코드가 아래와 같이 수정되었다.

- SQL Injection을 방지하고자 prepare 메서드로 SQL 코드를 정의하고, 입력 값은 바인딩 변수로 받는다.

파라미터 바인딩 후 쿼리를 실행하는 execute 메서드를 사용한다.

- header( ) 함수 사용 후에 코드 실행 방지를 위하여 exit( ); 를 한다.

 

<< 보안 강화한 게시판 소스 리스트 >>
1) dbconn2.php (데이터베이스 연결)
2) register2.php (회원가입)
3) login2.php (로그인 화면)
4) logout2.php (로그아웃)
5) notice_list2.php (게시판 리스트 보는 화면)
6) notice_add_page2.php (게시판 글 추가하는 화면)
7) notice_add2.php (게시판 글 추가하는 부분을 데이터베이스에 저장)
8) notice_edit_page2.php (게시판 글 수정하는 페이지)
9) notice_edit2.php (게시판 글 수정한 부분을 데이터베이스에 반영)
10) delete2.php (게시글 삭제)

 

 

데이터베이스 생성

create database testDB2;
use testDB2;

create table dbo.Users(
	UserID char(10) not null primary key,
	Username nvarchar(50) not null,
	Password nvarchar(255) not null
);

create table dbo.Posts(
	PostID int identity(1,1) not null primary key,
	Title nvarchar(100) null,
	Content nvarchar(max) null,
	CreatedDate datetime null,
	UserID char(10) null foreign key references Users(UserID)
);

 

 

 

데이터베이스 연결하는 PHP

(dbconn2.php)

-> 데이터베이스와 PHP를 연결하는 코드 작성

<?php
	if(session_status() == PHP_SESSION_NONE){
			session_start();
	}

	$current_file = basename($_SERVER['PHP_SELF']);
	if(($current_file != 'login2.php' && $current_file != 'register2.php') && !isset($_SESSION['UserID'])){
		echo "<script>alert('로그인이 필요합니다');</script>";
		echo "<script>location.href='login2.php';</script>";
		exit;
	}
?>

<?php
	$serverName = "(local)";
	$database = "testDB2";

	$uid = "sa";
	$pwd = "p@ssw0rd";

	$table = "Posts";

	try{
		$conn = new PDO("sqlsrv:server=$serverName;Database=$database", $uid, $pwd);
		$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	} catch (PDOException $e) {
		die("Error connecting to SQL Server");
	}
?>

 

<실행화면>
로그인 화면, 회원가입 화면 둘 다 아니고 -> 로그인, 회원가입이 아니면서 세션값이 없으면
alert('로그인이 필요합니다')
메시지 창을 출력

 

 

 

회원가입 PHP

(register2.php)
-> ID, 이름, 비밀번호 입력하여 회원가입을 누르면 DB에 저장되도록 코드를 작성

<?php
	include 'dbconn2.php'; 
	if ($_SERVER["REQUEST_METHOD"] == "POST") {
	    $userid = $_POST['userid'];
	    $username = $_POST['username'];
	    $password = $_POST['password']; // 비밀번호 입력값 (평문으로 저장)

	    // 비밀번호를 해시 처리 (bcrypt 알고리즘 사용)
	    $hashed_password = password_hash($password, PASSWORD_BCRYPT);

	    try {
	    	//SQL 쿼리: prepared statement 사용하여 SQL 인젝션 방지
	    	$sql="INSERT INTO Users(UserID, Username, Password) VALUES(:userid, :username, :password)";
	    	$stmt=$conn->prepare($sql);

	    	//파라미터 바인딩
	    	$stmt->bindParam(':userid', $userid);
	    	$stmt->bindParam(':username', $username);
	    	$stmt->bindParam(':password', $hashed_password);

	    	//쿼리 실행
	    	$stmt->execute();
	    	echo "회원가입이 완료되었습니다.";
	        header('Location: login2.php');
	        exit(); //header 후 코드 실행 방지
	    } catch(PDOException $e) {
	    	die("회원가입 중 오류가 발생했습니다: " . $e->getMessage());
	    }
	}
?>

<form method="POST">
    사용자 ID: <input type="text" name="userid" maxlength="10" required><br> 
    사용자 이름: <input type="text" name="username" required><br>
    비밀번호: <input type="password" name="password" required><br>
    <input type="submit" value="회원가입">
</form>

보안 강화 수정 부분

- SQL Injection을 방지하고자 prepare 메서드로 SQL 코드를 정의하고, 입력 값은 바인딩 변수로 받는다.

파라미터 바인딩 후 쿼리를 실행하는 execute 메서드를 사용한다.

- 비밀번호는 해시값으로 변환하여 입력한다.

- header( ) 함수 사용 후에 코드 실행 방지를 위하여 exit( ); 를 한다.

<실행 화면>
입력받은 값은 데이터베이스에 입력된다.
입력받은 값이 DB에 입력됨을 확인하고, 비밀번호 값은 해시값으로 변환하여 입력해준다.

 

 

 

로그인 PHP

(login2.php)
-> 회원가입을 하면 로그인 창으로 연결되게 regist.php 작성하였으므로 회원가입 버튼을 누를 시에 로그인 창으로 넘어가는지 확인

<?php
    session_start();
    include "dbconn2.php";

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $userid = $_POST['userid'];
        $password = $_POST['password'];
        try {
            //SQL 쿼리: prepared statement 사용하여 SQL 인젝션 방지
            $sql = "SELECT * FROM Users WHERE UserID = :userid";
            $stmt = $conn->prepare($sql);
            $stmt->bindParam(':userid', $userid);
            $stmt->execute(); //쿼리 실행

            $user = $stmt->fetch(PDO::FETCH_ASSOC); //사용자 정보 조회

            //비밀번호가 해시된 비밀번호와 일치하는지 확인
            if($user && password_verify($password, $user['Password'])) {
                $_SESSION['UserID'] = $user['UserID']; //세션에 사용자 ID 저장
                $_SESSION['Username'] = $user['Username']; //세션에 사용자 이름 저장
                echo "<script>alert('로그인 성공');</script>";
                echo "<script>location.href='notice_list2.php';</script>";
                exit(); // 리다이렉트 후 코드 실행 방지
            }
            else {
                echo "<script>alert('로그인 실패: 사용자 ID 또는 비밀번호가 잘못되었습니다.');</script>";
            }
        } catch (PDOException $e) {
            die("로그인 중 오류가 발생했습니다: " . $e->getMessage());
        }
    }
?>

<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8">
        <title>로그인</title>
    </head>
    <body>
        <h2>로그인</h2>
        <form method="POST" action="login2.php">
            <label>사용자 ID:</label>
            <input type="text" name="userid" required>
            <br>
            <label>비밀번호:</label>
            <input type="password" name="password" required>
            <br>
            <button type="submit">로그인</button>
            <a href="register2.php">회원가입</a>
        </form>
    </body>
</html>

보안 강화 수정 부분

- ID, 비밀번호의 경우 primary key 이므로 <input> 태그에 required 속성을 입력해준다. 

 

<실행 화면>
사용자의 ID와 비밀번호를 행단위로 변수에 가져온 후 아이디와 비밀번호가 일치하면
세션에 해당 아이디와 비밀번호를 저장한다.

입력한 아이디와 비밀번호가 일치하면 로그인 성공 메시지 창이 뜬다.
<보안 강화 실행 부분>
ID와 비밀번호는 <input> 태그의 required 속성으로 입력하지 않으면 로그인이 되지 않는다.

 

 

 

로그아웃 PHP

(logout2.php)

<?php
	session_start();
	session_destroy();

	header('Location: login2.php');
	exit;
?>

로그아웃하면 세션 값이 삭제되고, 로그인 화면으로 이동한다.

 

 

 

게시판 리스트 화면 PHP

(notice_list2.php)
-> 로그인 화면에서 로그인 버튼을 누르면 게시판 리스트 화면으로 이동하도록 코드를 작성하였으므로 로그인 성공 후 게시판 화면으로 이동하는지 확인

더보기
<?php
    session_start();

    if (!isset($_SESSION['UserID']) || !isset($_SESSION['Username'])) {
        echo "<script>alert('로그인이 필요합니다');</script>";
        echo "<script>location.href='login2.php';</script>";
        exit;
    }
?>

<!DOCTYPE html>
<html lang="ko">
    <head>
      <meta charset="UTF-8">
      <title>공지사항 리스트</title>
      <script type="text/javascript" src="https://www.google.com/jsapi"></script>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
      <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
      <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
      <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <style>
           body{
                width: 100%;
                height: 100%;
                margin: 0;
                padding: 0;
            }
            p{
                margin: 0;
            }
            main{
                width: 100%;
                height: 100%;
            }
            .top_header{
                position: fixed;
                top: 0;
                width: 100%;
                background-color: black;
                color: white;
                padding: 20px 0;
                text-align: right;
                z-index: 9999;
            }
            .logout_btn{
                cursor: pointer;
                background-color: white;
                color: red;
                border: none;
                border-radius: 15px;
                outline: none;
                margin: 0 20px;
            }
            .excel_header{
                width: 80%;
                margin-top: calc(5% + 50px);
                margin-left: 10%;
                border: solid gray 1px;
                background-color: white;
                display: flex;
            }
            .excel_header div:nth-child(1){
                width: 40%;
                padding: 30px 20px;
          
            }
            .excel_header h3{
                margin: 0;
              
                font-size: 20px;
            }
            .excel_header div:nth-child(2){
                width: 80%;
                text-align: right;
                margin-right: 50px;
                padding-top: 30px;
                border-radius: 5px;
            }
            .excel_header div:nth-child(2) button{
                width: 100px;
                padding: 10px 0;
                border: solid black 1px;
                color: black;
                cursor:pointer;
            }
            .excel_header div:nth-child(2) button:hover{
                background-color: orange; 
            }

            .excel_wrapper{
                width: 80%;
                margin-left: 10%;
                margin-top: 3%;
            }
            .excel_wrapper table{
                width: 100%;
                border-collapse: separate;
                border-spacing: 0 10px;
            }
            .excel_wrapper thead, .excel_wrapper tbody{
                width: 100%;
            }
            .excel_wrapper th{
                padding: 5px 20px;
                border-bottom: solid black 2px;
                border-radius: 5px 5px 0 0;
                font-size: 20px;
                text-align: left;
            }
            .excel_wrapper td:nth-child(1){
                width: 5%;
            }
            .excel_wrapper td:nth-child(2){
                width: 20%;
            }
            .excel_wrapper td:nth-child(3){
                width: 45%;
                text-align: left;
            }
            .excel_wrapper td:nth-child(4){
                width: 20%;
                text-align: left;
            }
            .excel_wrapper td{
                padding: 7px 5px 17px 20px;
                border-bottom: solid gray 1px;
                font-size: 18px;
            }
            .excel_wrapper td:nth-child(3) button{
                padding: 5px 10px;
                border: solid gray 1px;
                border-radius: 5px;
                background-color: white;
                outline: none;
                cursor: pointer;
            }
            .excel_wrapper td:nth-child(3) button:hover{
                border: solid black 1px;
                background-color: orange;
            }
            /* 엑셀 버튼 */
            .excel_button{
                width: 100%;
                text-align: center;
                margin-top: 2%;
                /* margin-left: -2%; */
            }
            .excel_button button{
                padding: 15px;
                background-color: white;
                color: black;
                font-size: 15px;
                border: solid black 2px;
                cursor: pointer;
            }
            .excel_button button:nth-child(1), .excel_button button:nth-child(5){
                margin: 0 20px;
            }
            .excel_button button:nth-child(2), .excel_button button:nth-child(3), .excel_button button:nth-child(4){
                margin: 0 5px;
            }
            a{
                color: red;
            }
        </style>
    </head>
    <body>
        <div class="top_header">
            <p>
                <?php echo htmlspecialchars($_SESSION['Username'], ENT_QUOTES, 'UTF-8'); ?>님 
                <a href='logout2.php'>
                    <button class="logout_btn">로그아웃</button>
                </a>
            </p>
        </div>
        <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
            <main>
                <div class="excel_header">
                    <div>    
                        <h3>공지사항 관리</h3>
                    </div>
                    <div>
                        <button onclick="location.href='notice_add_page2.php'">추가하기</button>
                    </div>
                </div> <!-- excel_header --> 
                <div class="excel_wrapper">
                    <table>
                        <thead>
                            <tr>
                                <th>번호</th>
                                <th>제목</th>
                                <th>내용</th>
                                <th>업데이트날짜</th>
                                <th>수정/삭제</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                                include "dbconn2.php";
                            
                                $query = "SELECT * FROM Posts ORDER BY PostID DESC";
                                $stmt = $conn->query($query);

                                while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) 
                                    { 
                            ?>
                            <tr>
                                <td><?php echo htmlspecialchars($row['PostID'], ENT_QUOTES, 'UTF-8');?></td> 
                                <td><?php echo htmlspecialchars($row['Title'], ENT_QUOTES, 'UTF-8');?></td>
                                <td><?php echo htmlspecialchars($row['Content'], ENT_QUOTES, 'UTF-8');?></td>
                                <td><?php echo htmlspecialchars($row['CreatedDate'], ENT_QUOTES, 'UTF-8');?></td>
                                <td> 
                                    <?php 
                                        if ($_SESSION['UserID'] == $row['UserID']) { 
                                    ?>
                                    <a href="notice_edit_page2.php?PostID=
                                    <?php 
                                        echo htmlspecialchars($row['PostID'], ENT_QUOTES, 'UTF-8'); 
                                    ?>">수정/삭제</a>
                                    <?php 
                                        } else { 
                                    ?>
                                    <span>수정/삭제 불가</span>
                                    <?php 
                                        } 
                                    ?> 
                                </td>
                            </tr> 
                            <?php } ?>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </body>
</html>

보안 강화 수정 부분

- htmlspecialchars( ) 함수를 사용하여 문자열에서 특정한 특수 문자를 HTML 엔티티로 변환한다.

데이터베이스에서 가져온 데이터를 HTML로 출력할 때 발생할 수 있는 XSS 공격을 방지

<실행 화면>
로그인 성공 시 해당 게시판 메인 화면으로 이동한다.

로그인화면에서 게시판 메인 화면으로 이동

 

 

 

게시판 글 추가 PHP

(notice_add_page2.php)
-> 게시판 리스트 화면에서 "추가하기" 버튼을 누르면 게시판 글 추가 화면으로 이동하도록 설정하였으므로
버튼 클릭 후 글 추가 화면으로 이동하는지 확인

<?php
    session_start();

    if(!isset($_SESSION['UserID'])) {
        echo "<script>alert ('로그인이 필요합니다.');</script>";
        echo "<script>location.href='login2.php';</script>";
        exit;
    }
?>
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
    <script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta charset="utf-8">
    <style>
        body{
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            background-color: whitesmoke;
        }
        main{
            width: 100%;
            height: 100%;
        }
        .content_wrapper{
            width: 80%;
            margin-left: 20%;
        }
        .content_text{
            margin-top: 10%;
            font-size: 30px;
        }
        .add_title h5, .add_content h5{
            font-size: 30px;
        }
        .add_title input, .add_content input{
            font-size: 20px;
            width: 40%;
            padding: 20px 10px;
        }
        .button_content{
            margin-left: 35%;
            margin-top: 3%;
        }
        .button_content button:hover{
            background-color: orange;
        }
        .button_content button{
            width: 100px;
            padding: 15px 0;
            border: solid black 1px;
            border-radius: 5px;
            background-color: white;
        }
        .orange:focus{
            background-color: orange;
        }
    </style>
</head>
<body>
    <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
        <main>
            <div class="content_wrapper">
                <div class="content_text">
                    <h2>공지사항 추가</h2>
                </div>
                <form action="notice_add2.php" method="POST" onsubmit="return validateForm()">
                    <div class="add_title">
                        <h5>제목</h5>
                        <input type="text" maxlength="20" placeholder="20자까지 입력 가능~!" class="orange" name="title" required>
                    </div>
                    <div class="add_content">
                        <h5>내용</h5>
                        <input type="text" maxlength="100" placeholder="100자까지 입력 가능~!" class="orange" name="content" required>
                    </div>
                    <div class="button_content">
                        <button type="submit" id="add">추가하기</button>
                    </div>
                </form>
            </div><!--content_wrapper-->
        </main>
    </div>
    <script>
        //클라이언트 측 입력 검증 추가
        function validateForm() {
            const title = document.querySelector('input[name="title"]').value;
            const content = document.querySelector('input[name="content"]').value;

            if(title.trim()==="" || content.trim()==="") {
                alert("모든 필드를 채워주세요.");
                return false;
            }
            if(title.length>20 || content.length>100) {
                alert("입력된 값이 너무 깁니다.");
                return false;
            }
        return true;
        }
    </script>
</body>
</html>
<실행 화면>
<input> 태그에 required 속성이 있으므로 입력하지 않을 때에 추가하기가 되지 않는다.
<input> 태그에 required 속성이 있으므로 입력하지 않을 때에 추가하기가 되지 않는다.
제목과 내용이 입력되지 않은 상태로 폼이 전송되면 자바스크립트 함수를 통해 메시지 창을 띄워준다.

 

 

 

게시판 추가 시 데이터베이스 반영 PHP

(notice_add2.php)
-> 추가하기 버튼을 눌러 받은 데이터 값을 데이터베이스에 INSERT 한다.

<?php
	session_start();

	//로그인 여부 확인
	if(!isset($_SESSION['UserID'])) {
		echo "<script>alert ('로그인이 필요합니다.');</script>";
		echo "<script>location.href='login2.php';</script>";
		exit;
	}

	if($_SERVER['REQUEST_METHOD'] !=='POST'){
		echo "<script>alert('잘못된 접근입니다.');</script>";
		echo "<script>location.href='notice_list2.php';</script>";
		exit;
	}
?>

<?php
	$title = trim($_POST['title']);
	$content = trim($_POST['content']);
	$now = date('Y-m-d H:i:s');
	
	include "dbconn2.php"; //데이터베이스 연결 설정 파일 포함

	if(empty($title) || empty($content)) {
		echo "<script>alert('제목과 내용을 모두 입력해주세요');</script>";
		echo "<script>history.back();</script>";
		exit;
	}

	try {
		//SQL 쿼리 준비
		$query = "INSERT INTO $table(Title, Content, CreatedDate, UserID) VALUES(:Title, :Content, :CreatedDate, :UserID)";
		$stmt = $conn->prepare($query); //쿼리 준비

		//바인드 파라미터 설정
		$stmt->bindParam(':Title', $title, PDO::PARAM_STR);
		$stmt->bindParam(':Content', $content, PDO::PARAM_STR);
		$stmt->bindParam(':CreatedDate', $now, PDO::PARAM_STR);
		$stmt->bindParam(':UserID', $_SESSION['UserID'], PDO::PARAM_STR);

		$stmt->execute();

		echo "<script>alert ('추가되었습니다');</script>";
		echo "<script>location.href='notice_list2.php';</script>";
	} catch (PDOException $e) {
		//오류 메시지 출력
		echo "<script>alert('데이터베이스 오류가 발생했습니다.');</script>";
		error_log("Database errpr: ".$e->getMessage()); //서버의 오류 로그에 기록
		echo "<script>history.back();</script>";
	}
?>

보안 강화 수정 부분

- 받은 <form> 태그의 method가 POST가 아닐 경우 "잘못된 접근입니다" 메시지 창을 띄우고, 게시판 화면으로 이동한다.

- 양쪽의 공백을 제거한 제목이나 내용이 비어있다면 이전 페이지로 이동한다.

 

<실행 화면>
값이 정상적으로 입력되면 "추가되었습니다." 메시지 창이 뜬다.
입력한 값이 Posts 테이블에 삽입되는 것을 확인할 수 있고,
게시판 글 추가가 완료되면 Posts 테이블에서 각각의 내용을 불러온다.

 

 

 

게시판 글 수정 PHP

(notice_edit_page2.php)
-> 게시판 올라온 글의 수정을 누르면 글 수정하는 화면으로 이동

<?php
	session_start();

	if(!isset($_SESSION['UserID'])) {
		echo "<script>alert ('로그인이 필요합니다');</script>";
		echo "<script>location.href='login2.php';</script>";
		exit;
	}
?>

<!DOCTYPE html>
<html>
<head>
	<script type="text/javascript" src="https://www.google.com/jsapi"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
	<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
	<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
	<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>공지사항 수정</title>
	<style>
		body{
			width: 100%;
			height: 100%;
			margin: 0;
			padding: 0;
			background-color: whitesmoke;
		}
		main{
			width: 100%;
			height: 100%;
		}
		.content_wrapper{
			width: 80%;
			margin-left: 20%;
		}
		.content_text{
			margin-top: 10%;
			font-size: 30px;
		}
		.edit_title h5, .edit_content h5{
			font-size: 30px;
		}
		.edit_title input, .edit_content input{
			font-size: 20px;
			width: 40%;
			padding: 20px 10px;
		}
		.orange:focus{
			background-color: orange;
		}
		.button_content{
			margin-left: 35%;
			margin-top: 3%;
		}
		.button_content button{
			width: 100px;
			padding: 15px 0;
			border: solid black 1px;
			border-radius: 5px;
			background-color: white;
		}
		.button_content button:hover{
			background-color: orange;
		}
	</style>
</head>
<body>
	<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
		<main>
			<div class="content_wrapper">
				<div class="content_text">
					<h2>공지사항 수정</h2>
				</div>
				<?php //해당 글 번호의 행 데이터값 읽어오기
					include "dbconn2.php";
					
					//입력값 검증
					if(isset($_GET['PostID']) && is_numeric($_GET['PostID'])){
						$PostID = $_GET['PostID'];

						//Prepared Statement 사용
						$stmt = $conn->prepare("SELECT * FROM $table WHERE PostID=?");
						$stmt->execute([$PostID]);
						if($row=$stmt->fetch(PDO::FETCH_ASSOC)){
							//XSS 방지를 위해 htmlspecialchars 사용
							$title = htmlspecialchars($row['Title'], ENT_QUOTES, 'UTF-8');
							$content = htmlspecialchars($row['Content'], ENT_QUOTES, 'UTF-8');
				?>
				<form action="notice_edit2.php" method="POST" onsubmit="return confirmEdit()">
					<div class="edit_title">
						<h5>제목</h5>
						<input type="text" class="orange" 
						value="<?=$title;?>" name="title">
					</div>
					<div class="edit_content">
						<h5>내용</h5>
						<input type="text" class="orange" 
						value="<?=$content;?>" name="content">
						<input type="hidden" value="<?=$PostID;?>" name="PostID">
						<div class="button_content">
							<button type="submit">수정완료</button>
						</div>
						<a href="delete2.php?PostID=<?=$PostID;?>">삭제</a>
					</div>
				</form>
				<?php 
						} else {
							echo "<p>해당 공지사항을 찾을 수 없습니다.</p>";
						}
					} else {
						echo "<p>잘못된 요청입니다.</p>";
					}
					$conn = null;
				?>
			</div>
		</main>
		<script>
			function confirmEdit(){
				return confirm('수정하시겠습니까?');
			}
		</script>
	</div>
</body>
</html>

보안 강화 수정 부분

- 입력값을 검증한다.
(입력받은 값이 null이 아니고, PostID의 값이 숫자일 경우 해당 PostID 값의 데이터들을 모두 불러온다.)

- <form> 태그를 전송할 때 자바스크립트의 함수 confirmEdit( )가 동작한다.

 

<실행 화면>
수정완료 버튼을 누르면 자바스크립트의 함수가 동작한다.

(onsubmit : 양식 제출 이벤트가 발생할 때의 동작을 지정한다.)
onsubmit이 처리되면 그 다음에 action으로 이동한다.
<보안 강화 수정 부분>
Prepared Statement 사용 전
Prepared Statement 사용 후
기존의 시큐어 코딩을 적용하기 전에는 PostID의 값을 그대로 받아왔지만,
시큐어 코딩을 적용한 후에는 PostID의 값을 그대로 받아오지 않는다.

 

 

 

게시판 글 수정한 데이터를 데이터베이스에 반영 PHP

(notice_edit2.php)
-> "수정하시겠습니까?" 에서 확인 버튼을 누르면 해당 php로 데이터 값을 넘겨준다.
넘겨받은 값이 정상적으로 수정되었는지 확인

<?php
	session_start();

	if(!isset($_SESSION['UserID'])) {
		echo "<script>alert ('로그인이 필요합니다');</script>";
		echo "<script>location.href='login2.php';</script>";
		exit;
	}
?>

<?php
	include "dbconn2.php";

	//입력값 검증
	if(isset($_POST['PostID'], $_POST['title'], $_POST['content']) && is_numeric($_POST['PostID'])){
		$PostID = $_POST['PostID'];
		$title = $_POST['title'];
		$content = $_POST['content'];
		$now = date('Y-m-d H:i:s');

		//데이터 필터링(XSS 방지)
		$title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
		$content = htmlspecialchars($content, ENT_QUOTES, 'UTF-8');

		//prepared statement 사용
		try{
			$stmt = $conn->prepare("UPDATE $table SET Title=?, Content=?, CreatedDate=? WHERE PostID=?");
			$stmt->execute([$title, $content, $now, $PostID]);

			//업데이트 성공 시
			if($stmt->rowCount()>0) {
				echo "<script>alert('변경되었습니다.');</script>";
			} else {
				echo "<script>alert('변경된 내용이 없습니다.');</script>";
			}
		} catch(PDOException $e) {
			//SQL 오류처리
			echo "<sctipt>alert('오류 발생: ".htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8')."');</script>";
		}
		echo "<script>location.href='notice_list2.php';</script>";
	} else {
		echo "<script>alert('잘못된 요청입니다.');</script>";
		echo "<script>location.href='notice_list2.php';</script>";
	}
	$conn = null;
?>

보안 강화 수정 부분

- htmlspecialchars( ) 함수를 사용하여 문자열에서 특정한 특수 문자를 HTML 엔티티로 변환한다.

데이터베이스에서 가져온 데이터를 HTML로 출력할 때 발생할 수 있는 XSS 공격을 방지

 

<실행 결과>
수정된 내용(제목, 내용, 시간, 글 번호)이 있으면 "변경되었습니다." 메시지 창을 띄우고
수정된 내용이 없으면 "변경된 내용이 없습니다." 메시지 창을 띄운다.
수정된 내용을 게시판 메인 화면에서 확인하고, 데이터베이스에서도 확인한다.

 

 

 

게시글 삭제 PHP

(delete2.php)

<?php
	session_start();

	if(!isset($_SESSION['UserID'])) {
		echo "<script>alert ('로그인이 필요합니다');</script>";
		echo "<script>location.href='login2.php';</script>";
		exit;
	}
?>

<?php
	include "dbconn2.php";
	if(isset($_GET['PostID']) && is_numeric($_GET['PostID'])) {
		$PostID = $_GET['PostID'];

		//prepared statement 사용
		try{
			$stmt = $conn->prepare("delete from $table where PostID=?");
			$stmt->execute([$PostID]);

			//삭제 성공 시
			if($stmt->rowCount()>0){
				echo "<script>alert('삭제되었습니다.');</script>";
			} else {
				echo "<script>alert('삭제할 항목이 없습니다.');</script>";
			}
		} catch(PDOException $e) {
			//SQL 오류 처리
			echo "<script>alert('오류 발생: ".htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8')."');</script>";
		}
	} else {
		//입력값이 없거나 유효하지 않은 경우
		echo "<script>alert('잘못된 요청입니다.');</script>";
	}
	echo "<script>location.href='notice_list2.php';</script>";
	$conn = null;
?>

보안 강화 수정 부분

- if문과 try~catch문을 활용하여 오류 처리 작업을 하였다.

 

<실행 결과>

삭제 버튼을 누르면
alert('삭제되었습니다.')
메시지 창이 뜬다.

해당 게시글이 삭제된 것을 확인할 수 있다.