리플레이 공격방식은 프레젠테이션 공격이라 부르는데, 정상적인 사용자가 인증권한을 취득하기 위해 전송한 데이터를 재전송하는 모든 종류의 공격을 말합니다. 

리플레이 공격과 마찬가지로 패스워드 스니핑 공격을 차단하려는 노력은 많지만 완전하게 차단할 수 있는 방법은 없습니다.

그렇기 때문에 인증과정 중 폼의 여러 가지 속성을 설치한다 하더라도 공격자에게는 그다지 중요하지 않게 생각할 수 있으며 그래서인지 요즘은 HTTP요청내용과 HTTP응답내용이 노출되지 않게 보호하려고 SSL전송방식을 많이 이용하는 편입니다.

 <form action=https://mydomain.com/login.php method="POST">
     myid:   <input type="text" name="username">
     mypass: <input type="password" name="userpass">
 </form> 

개인이 호스팅을 임대받는 경우라면 SSL기능을 자유롭게 사용할 수 없으므로 get 전송방식보다는 인증노출이 적은 post 방식을 사용할 것을 권장하며, 비밀번호는 값을 MD5로 암호화하되 복잡한 암호를 사용하도록 사용자에게 유도하거나 개발자가 임의의 문자를 섞여 한번 더 암호화시켜 주어야 합니다.

다음으로 인증요청 무차별 공격 스크립트를 작성할 수 있습니다.

<form action="http://mydomain.com/login.php" method="POST"><br /> 
 myid:     <input type="text" name="username"><br /> 
 mypass:<input type="password" name="userpass"><br /> 
 </form>  

<?php 
 $host = "mydomain.com"; 
 $username = "habony"; 
 $userpass = "1111"; 

 $data = "username=$username&userpass=$userpass"; 
 $len = strlen($data); 


 // 헤더 구분은 \r\n로 해주어야 합니다. 
 $request = ''; 
 $request .= "POST /login.php HTTP/1.1\r\n"; 
 $request .= "Host: ${host}\r\n"; 
 $request .= "Content-Type: application/x-www-form-urlencoded\r\n"; 
 $request .= "Content-Length: ${len}\r\n"; 
 $request .= "Connection: close\r\n"; 

 // 본문시작은 \r\n\r\n로 헤더와 바디로 구분합니다. 
 $request .= "\r\n"; 
 $request .= "$data"; 

 // HTTP요청은 기본 80포트입니다. 
 if($fp = fsockopen($host, 80)){ 
      // mydomain.com 에 HTTP요청하고, HTTP응답을 받습니다. 
      fputs($fp, $request); 

      $response = ''; 
      while(!feof($fp)){ 
            // 1줄씩 응답을 읽어 옵니다. 
           $response .= fgets($fp, 1024); 
      } 
      fclose($fp); 
 } 

 echo "$response<br />\n"; 
 ?>

이와 같은 작업으로 공격자는 인증시도를 하려할 것인데, $response의 결과에 따라 패스워드 취득 실패시 다른 패스워드 재시도 루틴을 요청할 수 있을 것입니다.

만약 사용자가 복잡한 암호를 구성하였다면, 공격자의 성공확률을 낮출 수 있습니다. 사용자 암호보호를 위해 암호화하는 것은 필요한 것이므로 무차별 공격을 어렵게 만들거나 성공확률을 낮추기 위해 30초정도 지연시킬 수 있습니다.

<?php 
 $uniqid = uniqid(rand()); 
 // 아이디에 위험한 문자열이 있을 경우 ... 
 $inputid = mysql_real_escape_string($_POST['inputid']); 
 // 사용자가 단순한 암호를 선택하였다면, 
 // 개발자가 임의 문자를 붙여 한번 더 암호화시켜줍니다. 
 // md5 함수는 16진수 32 문자로 반환합니다. 
 // 16진수 32문자 = md5(문자열, [raw_output]); 
 // raw_output는 PHP 5.0부터 사용 가능하며, 
 // true로 설정하면 길이 16의 바이너리 형식으로 반환합니다. 
 // md5는 문자열 암호화이고, md5_file는 파일 암호화로 동일하게 사용할 수 
있습니다. 
 // echo md5("myid"); // 결과:cdce51bb5b16a770fbe0dd78e6d8a5bb 
 // echo md5("myid", true); // 결과: 誥Q?쬹鎬?燕? 
 $inputpass = md5($uniqid . "_habony_" . md5("habony_" . 
$_POST['inputpass'], true)); 

  

 $data = array(); 
 $sql = array(); 

 $now = time(); 
 $login_conn = $now - 30; 

 if($sql = mysql_query("select logintime, inputpass from $db where 
inputid='${inputid}'")){ 
      if(mysql_num_rows($sql)){ 
           $row = mysql_fetch_assoc($sql); 
           if($row['logintime'] > $login_conn){ 
                exit("로그인 실패하여 30초가 지나야 로그인 가능합니다."); 
           } elseif($row['inputpass'] === $inputpass){ 
                echo "로그인 인증되었습니다."; 
           } else { 
               // 로그인 실패시 처리 부분... 
                mysql_query("update $db set logintime = '$now' where 
inputid = '$inputid' "); 
           } 
      }  else { 
           exit("해당하는 아이디가 없습니다."); 
      } 
 } 
 ?>

파일업로드된 파일접근을 막기 위해 이것 역시 암호화하여 저장할 필요가 있습니다.

<?php 
 $uniqid = uniqid(rand()); 
 // 16진수 32문자 = md5_file(파일명, [raw_output]); 
 // raw_output는 PHP 5.0부터 사용 가능하며, 
 // true로 설정하면 길이 16의 바이너리 형식으로 반환합니다. 
 // md5_file함수는 파일이 실제 존재해야 암호화됩니다. 
 // 실패하면 php오류코드를 표시합니다. 
 // md5_file은 다음과 같은 조건입니다. 
 // if(file_exists($filename)){ 
 //      $md5filename = md5($uniqid . "_habony_". md5($filename, true)); 
 // } 
 $filename = base64_encode($_FILES['userfile']['name']); 
 $md5filename = md5($uniqid . "_habony_". md5_file($filename, true)); 
 if($_FILES['userfile']['error'] === UPLOAD_ERR_OK) { 
      if($_FILES['userfile']['size'] <= 0){ 
          echo "파일 업로드에 실패하였습니다."; 
      } else { 
          // HTTP post로 전송된 것인지 체크합니다. 
          if(!is_uploaded_file($_FILES['userfile']['tmp_name'])) { 
               echo "HTTP로 전송된 파일이 아닙니다."; 
          } else { 
               // move_uploaded_file은 임시 저장되어 있는 파일을 ./uploads 디렉토리로 이동합니다. 
               if (move_uploaded_file($_FILES['userfile']['tmp_name'], $md5filename)) { 
                    echo "성공적으로 업로드 되었습니다.\n"; 
               } else { 
                    echo "파일 업로드 실패입니다.\n"; 
               } 
               mysql_query("insert into $db values  ('','$filename'); 
          } 
      } 
 } else { 
      echo file_errmsg($_FILES['userfile']['error']); 
 } 
 ?>

0 댓글