
웹취약점 점검에서 개발한 웹에 대하여 점검을 진행하였고, 여러 취약점들을 발견하였다.
발견한 취약점들을 대상으로 시큐어 코딩을 진행하고자 한다.
이번 대상은 주요정보통신기반시설 분석·평가 가이드 17번 항목 - 불충분한 인가 이다.
불충분한 인가 취약점이 발견된 항목은 다음과 같다.
| URL | 파라미터 | 메뉴 |
|---|---|---|
| http://jmseok.com/web/board/freeedit_proc.php | num | 자유게시판 > 게시글 수정 |
| http://jmseok.com/web/board/free_edit.php | num | 자유게시판 > 게시글 삭제 |
| http://jmseok.com/web/board/memedit_proc.php | num | 회원게시판 > 게시글 수정 |
| http://jmseok.com/web/board/mem_edit.php | num | 회원게시판 > 게시글 삭제 |
| http://jmseok.com/web/board/comment_delete.php | comment_id | 회원게시판 > 댓글 삭제 |
서버 측 인가 검증 수행
▷ 접근 제어가 필요한 모든 요청에 대하여, 사용자의 인증 상태 및 권한을 서버 측에서 검증
흐름 정리
1. free_edit.php 에서 게시글 수정 요청 → 해당 게시글의 비밀번호를 함께 전송하도록 로직 추가
2. freeedit_proc.php → 전달받은 비밀번호와, 게시글 작성시 설정된 비밀번호를 비교하는 로직 추가
① 게시글 수정 페이지 - 게시글 비밀번호 전송 추가
// free_edit.php
<form class="edit-form" method="post" action="freeedit_proc.php">
<input type="hidden" id="num" name="num" value="<?php echo $num?>">
<input type="hidden" name="input_pass" value="<?php echo $input_pass?>">
<div class="edit-row">
<label for="input_title">제목 <i class="fa-solid fa-pen-to-square"></i></label>
<input id="input_title" name="input_title" type="text" value="<?php echo $title ?>">
</div>
<div class="edit-row">
<label>작성자명</label>
<input type="text" value="<?php echo $writer ?>" disabled>
</div>
<label for="input_cont">본문 <i class="fa-solid fa-pen-to-square"></i></label>
<textarea id="input_cont" name="input_cont"><?php echo $content ?></textarea>
<div class="edit-actions">
<input type="submit" value="수정 완료">
<input type="button" onclick="location.href='free_view.php?num=<?php echo $num?>'" value="뒤로가기">
</div>
</form>
② 게시글 수정 처리 페이지 - 비밀번호 검증 로직 추가
// freeedit_proc.php (일부)
$num = $_POST["num"];
$input_pass = $_POST["input_pass"];
$input_title = $_POST["input_title"];
$input_cont = $_POST["input_cont"];
$sql = "SELECT password FROM freeboard WHERE num = ?";
$stmt = mysqli_prepare($con, $sql);
mysqli_stmt_bind_param($stmt, "i", $num);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
if (!$row) {
echo "<script>
alert('잘못된 요청입니다.');
history.go(-1);
</script>";
exit;
}
$db_pass = $row["password"];
if ($db_pass != $input_pass) {
echo "<script>
alert('비밀번호가 일치하지 않습니다.');
history.go(-1);
</script>";
exit;
}
"자유게시판 - 게시글 삭제" 에서의 불충분한 인가 취약점은, IDOR + SQL Injection을 통해 수행되었다.
따라서 SQL Injection이 불가능하도록 Prepared Statement를 적용함으로써 불충분한 인가 취약점을 제거하였다.
▷ 해당 코드의 경우, 코드 구성상 SQL Injection을 막는 것만으로도 인가 취약점이 제거되었다.
만약 [자유게시판 - 게시글 수정]과 같이 게시글 삭제 페이지를 별도로 구성한 경우, 바로 앞서 작성한 것처럼 똑같이 비밀번호 검증로직을 추가해주면 된다.
// free_edit.php (일부)
<?php
$num = $_POST["num"];
$input_pass = $_POST["input_pass"];
$type = $_POST["type"];
if (empty($input_pass) or !ctype_digit($input_pass) or strlen($input_pass)!=4) {
echo "<script>
alert('비밀번호 숫자 4자리를 입력해주세요');
history.go(-1);
</script>";
exit;
}
include "../db/db_con.php";
$sql = "select * from freeboard where num= ? and password= ?";
$stmt = mysqli_prepare($con, $sql);
mysqli_stmt_bind_param($stmt, "ii", $num, $input_pass);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
if (!$row) {
echo "<script>
alert('비밀번호가 일치하지 않습니다');
history.go(-1);
</script>";
exit;
}
?>
// memedit_proc.php (일부)
$num = $_POST["num"];
$sql = "delete from memboard where num = ? and user_id = ?";
$stmt = mysqli_prepare($con, $sql);
mysqli_stmt_bind_param($stmt, "is", $num, $_SESSION["user_id"]);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_affected_rows($stmt) === 0) {
echo "<script>
alert('권한이 없습니다.');
history.go(-1);
</script>";
exit;
}
(기존)
// mem_edit.php (일부)
if ($type == "delete") {
if (!empty($file_copy)) {
$filepath = "../uploads/".$file_copy;
unlink($filepath);
}
$sql = "delete from memboard where num = $num";
mysqli_query($con, $sql);
echo "<script>
alert('게시글이 삭제되었습니다');
location.href='./mem_list.php';
</script>";
exit;
}
(수정 후)
if ($type == "delete") {
if (!empty($file_copy)) {
$filepath = "../uploads/".$file_copy;
unlink($filepath);
}
$sql = "delete from memboard where num = ? and user_id = ?";
$stmt = mysqli_prepare($con, $sql);
mysqli_stmt_bind_param($stmt, "is", $num, $_SESSION["user_id"]);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_affected_rows($stmt) === 0) {
echo "<script>
alert('권한이 없습니다.');
history.go(-1);
</script>";
exit;
}
mysqli_stmt_close($stmt);
echo "<script>
alert('게시글이 삭제되었습니다');
location.href='./mem_list.php';
</script>";
exit;
}
(기존)
// comment_delete.php (일부)
$comment_id = $_GET["comment_id"];
$board_num = $_GET["board_num"];
$sql = "delete from comment where id = $comment_id and board_num = $board_num";
mysqli_query($con, $sql);
(수정 후)
$comment_id = $_GET["comment_id"];
$board_num = $_GET["board_num"];
$sql = "select * from comment where id = $comment_id and board_num = $board_num";
$result = mysqli_query($con, $sql);
$row = mysqli_fetch_assoc($result);
$user_id = $row["user_id"];
if ($user_id != $_SESSION["user_id"]) {
echo "<script>
alert('권한이 없습니다.');
history.go(-1);
</script>";
exit;
}
$sql = "delete from comment where id = $comment_id and board_num = $board_num";
mysqli_query($con, $sql);
| 불충분한 인가 |
|---|
![]() |
| ▲ num 파라미터를 변조하여도, 권한 여부를 검사하여 요청 거부 |