강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

hsoing00님의 프로필 이미지
hsoing00

작성한 질문수

[실습 6-4, 5] 정상적인 접근이 아닙니다

작성

·

26

0

action.php

<?
	@session_start();
	header("Content-Type: text/html; charset=UTF-8");
	include ( './common.php' );

	# CSRF Token 검증 로직
	$csrf_token_session = $_SESSION["csrf_token"];
	$csrf_token_param = $_REQUEST["csrf_token"];

	if ($_SERVER['REQUEST_METHOD'] === 'POST') {
		$csrf_token_session = $_SESSION["csrf_token"];
		$csrf_token_param = $_REQUEST["csrf_token"];
		
		// CSRF 토큰 검증
		if (empty($csrf_token_session) && empty($csrf_token_param) || $csrf_token_param != $csrf_token_session) {
			echo "<script>alert('정상적인 접근이 아닙니다.');history.back(-1);</script>";
			exit();
		}
	}
	
		$mode = $_REQUEST["mode"];
		$db_conn = mysql_conn();
	
	if($mode == "write") {
		$title = xss_html_entity($db_conn->real_escape_string($_POST["title"]));
		$id = $db_conn->real_escape_string($_SESSION["id"]);
		$writer = xss_html_entity($db_conn->real_escape_string($_SESSION["name"]));
		$password = $db_conn->real_escape_string($_POST["password"]);
		$content = $db_conn->real_escape_string($_POST["content"]);
		$secret = $_POST["secret"];
		$uploadFile = "";

		if(empty($title) || empty($password) || empty($content)) {
			echo "<script>alert('빈칸이 존재합니다.');history.back(-1);</script>";
			exit();
		}

		if(!empty($_FILES["userfile"]["name"])) {
			$uploadFile = $_FILES["userfile"]["name"];
			$uploadPath = "{$upload_path}/{$uploadFile}";
			
			if(!(@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadPath))) {
				echo("<script>alert('파일 업로드를 실패 하셨습니다.');history.back(-1);</script>");
				exit;
			}
		}   
		
		if($secret == "on") {
			$secret = "y";
		} else {
			$secret = "n";
		}
		$uploadfile = $db_conn->real_escape_string($uploadFile);
		$content = str_replace("\\r\\n", "<br>", $content);
		
		$query = "insert into {$tb_name}(title, id, writer, password, content, file, secret, regdate) values('{$title}', '{$id}', '{$writer}', '{$password}', '{$content}', '{$uploadFile}', '{$secret}', now())";
		$db_conn->query($query);
	} else if($mode == "modify") {
		$idx = $_POST["idx"];
		$title = xss_html_entity($db_conn->real_escape_string($_POST["title"]));
		$password = $db_conn->real_escape_string($_POST["password"]);
		$content = xss_html($db_conn->real_escape_string($_POST["content"]));
		$secret = $_POST["secret"];
		$uploadFile = xss_html_entity($_POST["oldfile"]);
	

		if(!is_numeric($idx)){
			echo "<script>alert('숫자 값만 가능합니당.');history.back(-1);</script>";
			exit();
		}

		if(empty($idx) || empty($title) || empty($password) || empty($content)) {
			echo "<script>alert('빈칸이 존재합니다.');history.back(-1);</script>";
			exit();
		}

		# Password Check Logic
		#$query = "select * from {$tb_name} where idx={$idx} and password='{$password}'";
		$query = "select * from {$tb_name} where idx={$idx} and id='{$_SESSION["id"]}'";
		$result = $db_conn->query($query);
		$num = $result->num_rows;

		if($num == 0) {
			#echo "<script>alert('패스워드가 일치하지 않습니다.');history.back(-1);</script>";
			echo "<script>alert('잘못된 요청입니다.');history.back(-1);</script>";
			exit();
		}

		if(!empty($_FILES["userfile"]["name"])) {
			$uploadFile = $_FILES["userfile"]["name"];
			$uploadPath = "{$upload_path}/{$uploadFile}";
			
			if(!(@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadPath))) {
				echo("<script>alert('파일 업로드를 실패 하셨습니다.');history.back(-1);</script>");
				exit;
			}
		}
		
		if($secret == "on") {
			$secret = "y";
		} else {
			$secret = "n";
		}


		
		$content = str_replace("\\r\\n", "<br>", $content);
		$uploadFile = $db_conn->real_escape_string($uploadFile);
		
		$query = "update {$tb_name} set title='{$title}', content='{$content}', file='{$uploadFile}', secret='{$secret}', regdate=now() where idx={$idx}";
		$db_conn->query($query);
	} else if($mode == "delete") {
		$idx = $_GET["idx"];
		$password = $db_conn->real_escape_string($_POST["password"]);
		
		if(!is_numeric($idx)){
			echo "<script>alert('숫자 값만 가능합니당.');history.back(-1);</script>";
			exit();
		}

		# Password Check Logic
		#$query = "select * from {$tb_name} where idx={$idx} and password='{$password}'";
		$query = "select * from {$tb_name} where idx={$idx} and id='{$_SESSION["id"]}'";
		$result = $db_conn->query($query);
		$num = $result->num_rows;

		if($num == 0) {
			#echo "<script>alert('패스워드가 일치하지 않습니다.');history.back(-1);</script>";
			echo "<script>alert('잘못된 요청입니다.');history.back(-1);</script>";
			exit();
		}
		
		$query = "delete from {$tb_name} where idx={$idx}";
		$db_conn->query($query);
	}

	echo "<script>location.href='index.php';</script>";
	$db_conn->close();
?>

write.php

    <div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
      <h1 class="display-4">Write Page</h1>
      <hr>
    </div>
    
    <div class="container">
		<form action="action.php" method="POST" enctype="multipart/form-data">
		  <div class="form-group">
			<label>Title</label>
			<input type="text" class="form-control" name="title" placeholder="Title Input">
		  </div>
		  <div class="form-group">
			<label for="exampleInputPassword1">Password</label>
			<input type="password" class="form-control" name="password" placeholder="Password Input">
		  </div>
		  <div class="form-group">
			<label for="exampleInputPassword1">Contents</label>
			<textarea class="form-control" name="content" rows="5" placeholder="Contents Input"></textarea>
      </div>
      <div class="form-group">
        <label for="exampleInputPassword1">File</label>
        <input type="file" class="form-control" name="userfile">
		  </div>
      <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" id="customCheck1" name="secret">
        <label class="custom-control-label" for="customCheck1">Secret Post</label>
      </div>
		<div class="text-right">
			<input type="hidden" name="csrf_token" value="<?=$csrf_token?>">
			<input type="hidden" name="mode" value="write">
			<button type="submit" class="btn btn-outline-secondary">Write</button>
			<button type="button" class="btn btn-outline-danger" onclick="history.back(-1);">Back</button>
		</div>
		</form>
    </div>

 

해커 계정으로 게시물 작성하려고 하는데

정상적인 접근이 아니라는 팝업이 뜹니다.

 

강의대로 코드 작성해도 그렇고 위처럼 수정했을 때도 그렇습니다.

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.

hsoing00님께서 올려주신 PHP 코드를 살펴본 결과, “정상적인 접근이 아닙니다”라는 경고 창이 표시되는 이유로는 CSRF 토큰 검증 로직에서 주로 발생할 수 있습니다. 다음과 같은 부분을 확인해보시는 것이 좋을 것 같습니다:

  1. 세션에 저장된 CSRF 토큰: $_SESSION["csrf_token"] 값이 실제로 세션에 잘 저장되고 있는지 확인해주세요. write.php에서 세션에 설정하고, action.php에서 해당 값을 호출하기 때문에 작성 및 실행 환경에서 세션이 잘 설정되었는지를 우선 확인해야 합니다.

  2. CSRF 토큰 전송: <input type="hidden" name="csrf_token" value="<?=$csrf_token?>"> 부분에서 <?php echo $_SESSION["csrf_token"]; ?>로 설정한 CSRF 토큰이 write.php에 정상적으로 표시 및 전송되고 있는지 확인해주세요. 이는 PHP 세션에서 토큰 값을 가져와 출력하는 것이기 때문에 소스 파악이 필요합니다.

  3. POST 요청: action.php에서 CSRFTOKEN 관련 검증은 POST 요청일 경우에만 수행됩니다. 실제로 write.php에서 요청이 POST로 잘 이뤄지는지도 확인해야 합니다.

  4. 세션 시작: @session_start(); 부분이 모든 페이지에 적절하게 적용되고 있는지 확인해주세요. 세션이 효율적으로 초기화되지 않으면 CSRF 토큰 검증에 실패할 수 있습니다.

위와 같은 사항들을 확인 후에도 문제가 지속된다면, 코드 전체 흐름에 맞게 세션 및 요청 처리를 다시 검토할 필요가 있을 것 같습니다.

추가로, CSRF 관련 보다 자세한 보안 정보를 확인하고 싶으시면 Spring Security Reference Guide에서 관련 내용을 찾아보실 수 있습니다.

계속해서 문제가 발생된다면 추가적인 환경 정보나 테스트 결과를 공유해 주시면 보다 상세히 도움을 드리도록 하겠습니다.

hsoing00님의 프로필 이미지
hsoing00

작성한 질문수

질문하기