일일 정리

게시판 웹브라우저 프로그래밍, PHP 보안 - 스크립트 공격 방지

mysecurity 2025. 4. 3. 23:53

목차

1. 게시판 웹브라우저 프로그래밍

1-1 물리 모델 작성

1-2 sql문 작성

1-3 프로그래밍

 

2. PHP 보안 - 스크립트 공격 방지

2-1 htmlspecialchars()

2-2 strip_tags()

 

 

1. 게시판 웹브라우저 프로그래밍

1-1 물리 모델 작성

지난 시간 작성한 논리 모델을 바탕으로 다음과 같은 물리 모델을 작성할 수 있다.

물리 모델

1-2 sql문 작성

작성한 물리 모델을 바탕으로 다음과 같이 오라클 데이터베이스에 접속하고 실제 sql문을 작성하여 테이블을 생성할 수 있다.

create table id (
ino number,
id varchar2(20),
passwd varchar2(128),
name varchar2(50),
cname varchar2(50),
tel varchar2(15),
addr varchar2(100),
email varchar2(50)
);

alter table id
add constraint id_ino_pk
primary key (ino);

create table board (
bno number,
sub varchar2(200),
bdate date,
hit number,
ino number
);
  .
  .
  .

 

1-3 프로그래밍

이전 시간에 작성한 html 파일들은 정적인 상태로, 실제로 로그인과 게시글 노출과 같은 동적인 기술을 사용하기 위해서는 php 파일을 이용한 프로그래밍이 필요하다.

다음과 같이 웹페이지가 실직적인 기능을 수행하도록 프로그램을 작성할 수 있다.

 

● id.php (회원가입) 수정중

<html>
    <head>
        <title>id_in.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>

<?   
    session_start();

    $ino = strip_tags(htmlspecialchars($_POST['ino'], ENT_QUOTES, 'UTF-8'));
    $id = strip_tags(htmlspecialchars($_POST['id'], ENT_QUOTES, 'UTF-8'));
    $userpasswd = strip_tags(htmlspecialchars($_POST['userpasswd'], ENT_QUOTES, 'UTF-8'));
    $name = strip_tags(htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8'));
    $cname = strip_tags(htmlspecialchars($_POST['cname'], ENT_QUOTES, 'UTF-8'));
    $tel = strip_tags(htmlspecialchars($_POST['tel'], ENT_QUOTES, 'UTF-8'));
    $addr = strip_tags(htmlspecialchars($_POST['addr'], ENT_QUOTES, 'UTF-8'));
    $email = strip_tags(htmlspecialchars($_POST['email'], ENT_QUOTES, 'UTF-8'));

    $_SESSION['ino'] = $ino;

    require('conn.php');    // $conn
    
    $sql="insert into id (ino, id, passwd, name, cname, tel, addr, email)
            values ('$ino', '$id', RAWTOHEX(STANDARD_HASH('$userpasswd', 'SHA256')), '$name', '$cname', '$tel', '$addr', '$email')";

    $result=oci_parse($conn,$sql);     
    $re = oci_execute($result, OCI_NO_AUTO_COMMIT);
    if ($re) {
        // 명시적으로 커밋 수행
        oci_commit($conn);
        echo("정상 입력 되었습니다.");
    } else {
        // 실패 시 롤백도 가능
        oci_rollback($conn);
        echo("입력 실패 - 롤백 처리되었습니다.");
    }                          
    oci_free_statement($result);
    oci_close($conn);
 
    echo(" <meta http-equiv='Refresh' content = '1; URL=view.php'>");
?>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

 

● check.php (로그인) 수정중

<html>
    <head>
        <title>check.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>
<?  
  session_start();
  require('conn.php');  
  
  $id = $_POST["id"];  
  $userpasswd = $_POST["userpasswd"];
  $sql="select * from id 
      where id = '$id'
      and passwd = 'RAWTOHEX(STANDARD_HASH('$userpasswd', 'SHA256'))";
  $result=oci_parse($conn,$sql);
  $re=oci_execute($result);
  $row_num=oci_fetch_all($result,$row); 
  oci_free_statement($result);
  oci_close($conn);
  
  if($row_num == 1){
     $_SESSION["id"] = $id;
     $_SESSION["passwd"] = $userpasswd;
     echo(" <meta http-equiv='Refresh' content = '0; URL=view.php'>");
  }    
  else{
       echo(" <script>
               window.alert('잘못된 계정 정보입니다.');
               history.go(-1);
              </script>
            ");
      exit;
     }
?>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

 

● view.php (게시글 목록) 수정중

<html>
    <head>
        <title>check.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>
<?  
  session_start();
  require('conn.php');  
  
  $id = $_POST["id"];  
  $userpasswd = $_POST["userpasswd"];
  $sql="select * from id 
      where id = '$id'
      and passwd = 'RAWTOHEX(STANDARD_HASH('$userpasswd', 'SHA256'))";
  $result=oci_parse($conn,$sql);
  $re=oci_execute($result);
  $row_num=oci_fetch_all($result,$row); 
  oci_free_statement($result);
  oci_close($conn);
  
  if($row_num == 1){
     $_SESSION["id"] = $id;
     $_SESSION["passwd"] = $userpasswd;
     echo(" <meta http-equiv='Refresh' content = '0; URL=view.php'>");
  }    
  else{
       echo(" <script>
               window.alert('잘못된 계정 정보입니다.');
               history.go(-1);
              </script>
            ");
      exit;
     }
?>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

 

● insert.php (게시글 작성) 수정중

<html>
    <head>
        <title>insert.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>

<?
    session_start();

    $bno = strip_tags(htmlspecialchars($_POST['bno'], ENT_QUOTES, 'UTF-8'));
    $sub = strip_tags(htmlspecialchars($_POST['sub'], ENT_QUOTES, 'UTF-8'));
    $bdate = strip_tags(htmlspecialchars($_POST['bdate'], ENT_QUOTES, 'UTF-8'));
    $hit = strip_tags(htmlspecialchars($_POST['hit'], ENT_QUOTES, 'UTF-8'));
    $content = strip_tags(htmlspecialchars($_POST['content'], ENT_QUOTES, 'UTF-8'));
    $ino = $_SESSION['ino'];
    
    require('conn.php');    // $conn

    $sql="insert into board (bno, sub, bdate, hit, ino)
            values (board_bno_seq.nextval, '$sub', sysdate, null, '$ino')";

    $result1=oci_parse($conn,$sql);     
    $re1 = oci_execute($result1, OCI_NO_AUTO_COMMIT);

    $sql="insert into content (bno, content)
            values (board_bno_seq.currval, '$content')";
    $result2=oci_parse($conn,$sql);     
    $re2 = oci_execute($result2, OCI_NO_AUTO_COMMIT);
    

    if ($re1 && $re2) {
        // 명시적으로 커밋 수행
        oci_commit($conn);
        echo("정상 입력 되었습니다.");
    } else {
        // 실패 시 롤백도 가능
        oci_rollback($conn);
        echo("입력 실패 - 롤백 처리되었습니다.");
    }                          
    oci_free_statement($result1);
    oci_free_statement($result2);
    oci_close($conn);
 
    echo(" <meta http-equiv='Refresh' content = '1; URL=insert.html'>");
?>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

insert.php와 같이 로그인 상태에서만 기능이 작동해야 하는 경우, login.php 파일을 미리 만들어 두고 프로그래밍 최상단에 require (login.php); 를 작성하여 로그인 상태에서만 정상적인 작동을 하도록 해야 한다.

 

2. PHP 보안 - 스크립트 공격 방지

2-1 htmlspecialchars()

스크립트 실행에 사용되는 특수문자나 문자열을 HTML Entities로 바꾼다.

문자 HTML Entities
< &lt;
> &gt;
" &quot
' &#039
& &amp;

 

- ENT_QUOTES 옵션을 사용하면 '와 " 모두 변환 가능하다.

- UTF-8 지정으로 문자셋 오작동을 방지한다.

 

 

<?
  $name1 = $_POST["name"];
  $name2 = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8');   
  echo("\$name :  {$name1}<br>");
  echo("\$name :  {$name2}<br>");
 ?>

 

위와 같이 post 방식으로 값을 전달받은 경우,  name1과 name2의 출력은 동일하나, 전달된 값은 다르다.

 

2-2 strip_tags()

HTML 및 PHP 태그를 제거하는 함수로, 입력값을 정리하거나 XSS 방지에 활용한다.

<?
  $input1 = "<p> Hello <b> World</b> ! </p>";
  $output1 = strip_tags($input1);
  echo ("\$output1 = {$output1} <br>");    
  
  $input2 = "<p>Hello <b>World</b>! <i>Italic</i></p>";
  $output2 = strip_tags($input2, "<b><i>"); // <b>와 <i> 태그는 허용한다.
  echo ("\$output2 = {$output2} <br>");   
  
  $input3 = "Hello <?php echo 'World'; ?>";
  $output3 = strip_tags($input3); 
  echo ("\$output3 = {$output3} <br>");  
 ?>

 

- input1 출력: Hello World

- input2 출력: Hello World Italic

- input3 출력: Hello

 

htmlspecialchars() 함수와 strip_tags() 함수는 공격자가 악의적인 스크립트를 웹 페이지에 삽입하여 사용자의 브라우저에서 실행되도록 유도하는 공격을 방지하는 기능을 수행하므로 php를 작성할 경우 반드시 둘 다 사용하도록 한다.