HTTP 헤더 인젝션은 공격자가 헤더에 개행문자를 삽입하여 헤더를 추가하여 공격하는 수동적 공격 방식입니다.


이 공격이 허용되는 경우 쿠키를 임의대로 생성하거나 임의로 URL를 리다이렉트 시킬 수 있습니다.


 http://example.com/?id=1001

위과 같은 코드가 있다면 아래 코드로 고쳐서 공격합니다.


 http://example.com/?id=101%0D%0A%0D%0ASet-cookie+SID=a123456789

결국 아래와 같은 결과 값을 얻게 되므로 쿠키를 만들게 됩니다.

 http://example.com/?id=101


 Set-cookie+SID=a123456789


공격자가 입력한 %0D%0A 는 \r\n 을 의미하므로 결과적으로 새로운 헤더가 만들어 지게 되는 것입니다.

다음의 방법도 주의해야할 부분입니다.


 http://example.com/?id=101%0D%0A%0D%0A<html><body><h2>Hello Habony!!</h2></body></html>

위 문자열이 HTTP 인젝션이 가능하게 되면 아래와 같은 결과가 나타나게 됩니다.

 http://example.com/?id=101


 <html>

   <body>

      <h2>Hello Habony!!</h2>

   </body>

 </html>


이 공격에 의해 유저에게 가짜 웹페이지를 표시하여 개인 정보를 입력받거나 크로스 사이트 스크립팅과 같은 공격을 받게 됩니다. 


물론 개행문자를 이용해 메일 헤더 인젝션 공격도 시도할 수 있습니다.


 http://example.com/?email=you@example.com%0D%0Ato@example.com


이렇게 입력받게 되면 공격자는 Bcc에 임의대로 수신자를 추가해 버릴 것입니다.


 <?php
 $header "you@example.com%0D%0Ato@example.com";
 mail($to$subject$content), $header);
 /*
 결과:
 you@example.com
 to@example.com
 */
 ?>


그래서 다음의 방법 등으로 문자열을 취환해서 공격을 회피해야 합니다.


 <?php
 $str "http://example.com/?id=101%0D%0A%0D%0ASet-cookie+SID=a123456789";
 
$str str_replace(array("\r""\n"), ""$str);
 
?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

프록시 서버나 인터넷 익스플로러, 크롬, 파이어 폭스와 같은 브라우저에 비밀번호와 같은 민감한 정보가 저장된다면 원치않게 정보가 노출되어 공격의 대상이 될 수 있습니다. 그래서 다음과 같이 민감한 정보가 저장되지 않도록 헤더를 추가해 줄 수 있습니다.


 <?php
 header
('
    Cache-Control: no-store,
    no-cache,
    private,
    max-age=0,
    must-revalidate,
    post-check=0,
    pre-check=0;
    Pragma: no-cache;
    Expires: Sat, 26 jul 1997 05:00:00 GMT;
 '
);
 ?>
 <html>
 <body>
 <h1>Hello Habony Password = 1234567890</h1>
 </body>
 </html>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요


Ajax 를 사용하는 경우 X-Requested-With 헤더를 추가해 직접 액세스하는 것을 금지하도록 하여야 합니다. 단, 공격자는 HTTP_ 로 시작하는 헤더를 자유자재로 추가할 수도 있기 때문에 완벽한 보증은 할 수는 없습니다.


 <?php
 if( !isset($_SERVER['HTTP_X-REQUESTED_WITH']) || 

 ($_SERVER['HTTP_X-REQUESTED_WITH'] !== 'XMLHttpRequest') )
 {
    echo 
"잘못된 접근입니다.";

    exit;
 }
 ?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

C 에서 NULL 바이트는 문자열의 끝을 나타내는데, 이것을 이용해 공격하는 기법입니다.


 \x00

 \0


아래 URL 의 값을 받는다고 가정해 보겠습니다.


 habony.php?user_id=%00%27%3BSELECT+*+FROM+member_table


다음의 결과처럼 의도치 않게 문제가 발생하게 됩니다.


 <?php
 $_GET
['user_id'] = '%00%27%3BSELECT+*+FROM+member_table';
 mysql_query("SELECT * FROM member_table WHERE uid='$_SESSION[memb]

                    AND md='$_GET[user_id]'");
 // 결과 : SELECT * FROM member_table WHERE uid='101' 
AND md='';

            SELECT * FROM member_table

 ?> 


 <?php
 
// file : "../../etc/passwd\0image.php"
 
$file $_GET['file'];

 
// file_exists : TRUE
 
if(file_exists('/home/habony/'.$file.'.php'))
 {
    include 
'/home/habony/'.$file.'.php';
 }
 
// 결과 : include '../../etc/passwd';
 
?>


이 문제를 해결하기 위해 다음의 방법으로 해결할 수 있을 것입니다.


 <?php
 $clean str_replace("\x00"''$input); 
 
// OR 

 $clean str_replace("\0"''$input);
 
// OR
 
$clean str_replace(chr(0), ''$input);
 
?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

Click Jacking 기법은 CSRF 기법과 비슷하지만 수동적인 방법으로 공격합니다. 예를 들어 공격자는 어떤 페이지에 투명한 프레임(iframe)으로 표시해두고, 사용자는 문제의 페이지를 열었을 때 무심코 그 투명한 프레임(iframe)을 클릭하도록 유도하는 방법입니다.


무심코 클릭하면 CSRF 기법처럼 게시물을 삭제해 버리는 등 의도치 않는 동작을 하게 합니다.


iframe 의 사용을 제한하는 X-FRAME-OPTIONS 헤더를 이용해 문제를 해결할 수 있으며, 익스플로러 8 부터 사용할 수 있습니다.


헤더의 값을 SAMEORIGIN 로 설정하면 호스트명이 같을 경우에만 iframe을 허용합니다. 예를 들어 example.com/bbs/habony.php 와 example.com/bbs/test.php 파일에는 표시하지만 book.example.com/habony.php 파일은 호스트명이 다르므로 표시가 되지 않습니다.


 X-FRAME-OPTIONS: SAMEORIGIN

DENY 를 지정하면 모든 페이지에 iframe 에 표시되는 것을 제한합니다.


 X-FRAME-OPTIONS: DENY


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

CSRF 공격은 어떤 게시판의 페이지를 열었을 때 의도치 않는 어떤 처리를 실행시켜 버리는 방법입니다.


아래 코드는 언뜻 보면 정상적인 코드로 보일 수 있습니다. 예를 들어 아래 태그가 포함된 페이지가 있고, 관리자만 게시물을 삭제할 수 있게 하여 일반 사용자는 게시물을 삭제할 수 없도록 해두었습니다.


하지만 관리자 세션을 취득한 관리자가 아래 페이지에 접속하는 것만으로도 의도치 않게 손쉽게 페이지를 삭제해 버릴 것입니다. 일반 사용자가 관리자 세션을 취득하지 않아도 말입니다.


 <?php
 if(!empty($_SESSION['admin_session']))
 {
    
mysql_query("DELETE FROM `tbname` WHERE id='101';");
 }
 ?>
 <img src="http://example.com/del.php?id=101">


그래서 요즘은 회원정보 변경 전 처리 페이지 직전에 비밀번호를 요구하거나 토큰을 요청하여 올바른 경우에만 변경하도록 하고 있습니다.


아래와 같이 비밀번호가 일치할 때만 삭제하게 해두면 CSRF 공격에 대응할 수 있습니다.


  <?php
 if(
($_POST['admin_password'] === '123456789') && ($_POST['id'] === '101')) 

 {
    if(!empty(
$_SESSION['admin_session']))
    {
        
mysql_query("DELETE FROM `tbname` WHERE id='101';");
    }
 }
 ?>
 <img src="http://example.com/del.php?id=101">



2013/03/18 - CSRF 웹보안 자료





블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

인터넷 익스플로러 에서는 서버에서 전송되는 Content-Type 헤더을 무시하고, HTML 파일이 아닌 파일을 HTML 로 인식해 버리는 문제가 있습니다.


그럴 경우 XSS 공격에 매우 취약해지므로 아래 헤더를 지정하여 문제를 해결할 수 있습니다. 단 익스플로러 8 이하 버전에서는 동작하지 않습니다.


 X-Content-Type-Options: nosniff 


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

XSS 웹보안 자료입니다.

 

XSS_Cross_Site_Scripting.pdf

출처: http://pscal.tistory.com

 

 

sample_chap5.pdf

출처: http://hanbitbook.co.kr

블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

 CSRF 웹 보안 자료입니다.

 

web2.0-csrf.pdf

출처: http://inetcop.org/

블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

ISP 통신사업자 서버주소입니다.

 

 KT

 기본 DNS

 보조 DNS 

 168.126.63.1
 168.126.63.2
 SK

 기본 DNS

 보조 DNS 

 219.250.36.130
 210.220.163.82
 LG

 기본 DNS

 보조 DNS

 164.124.101.2

 203.248.252.2

 

블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

다음 파일을 다운받아 홈페이지 최상위 디렉토리에 업로드해두기만 하면 검색봇이 긁어 가지 않습니다.

 

robots.txt

 

파일을 열어보면 "User-agent: * Disallow: /" 로 작성되어 있는데, / 는 전체를 긁어가지 않습니다. 일부만 허용하지 않으려면 폴더명을 기입해 주면 됩니다.

 

만약 test폴더와 admin 폴더를 검색봇 접속을 방지하려면 다음과 같이 해두면 됩니다.


 User-agent: * Disallow: /test Disallow: /admin

블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요


mixed
eval ( string $code_str ) (PHP 4, PHP 5)

이 함수는 php코드를 평가하기 위해 사용됩니다. 예를 들면, 다음의 결과로 이해할 수 있을 것입니다.

예제(ex #1
 <?php
 $string 
'cup'
;
 $name 'coffee'
;
 $str 'This is a $string with my $name in it.'
;
 echo 
$str"\n"
;
 eval(
"\$str = \"$str\";"
);
 echo 
$str"\n"
;
 /*
 결과:
 This is a $string with my $name in it.
 This is a cup with my coffee in it.
 */
 ?> 

작은 따옴표에 의한 값은 보통 일반 텍스트 문자로 처리되지만, eval 함수를 사용하게 되면 php 코드로 해석, 실행해 버립니다. 그래서 간혹 개발자는 편의상 디비에 저장해 두었던 php 코드 데이터를 읽어 와서 eval 함수로 php 코드로 사용하곤 합니다.

이는 분명 편리하기는 하지만 문제가 있습니다. 만약, eval 함수가 악의적인 사용자에게 노출되면 매우 위험해 진다는 점을 이해할 필요가 있습니다. 예를 들어, $str 변수가 변질이 되면 공격자는 서버뿐만 아니라 모든 데이터를 회손할 우려가 있습니다.

예제(ex #2
 <?php
 $str 
$_GET['data'
];
 eval(
$str
);
 ?>

예제(ex #3
 <?php
 $str 
mysql_fetch_array(mysql_query("select field tb where id=$id"
));
 eval(
$str
); 
 /* 
 $str 변수의 값:
 eval( '?> foo <?php' ); 
 or
 eval("\$handle = fopen(\"http://domain.com/template.phtml\");");
 */

 ?>

가능하다면 eval 함수를 사용하지 않기를 권하며, 분명 어떤 변수가 변질되지 않는다고 확신하더라도 권장하지 않습니다.
블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

세션 데이터 유출

보안에 있어 sql 이나 소스코드의 문제만은 아닙니다. 세션 데이터 노출 또한 위험성이 매우 높아 그 중요성이 부각되는데, 특히 공유호스팅 사용자라면 더욱 그러 합니다.

세션 데이터는 리눅스의 경우, /tmp 에 세션이 저장되는데, /tmp 폴더는 기본적으로 모든 사용자가 읽고, 쓰기가 가능한 권한을 가지고, 또 아파치도 여기에 세션 데이터 권한을 가지므로 그 위험은 커질 수 밖에 없습니다.

다음은 /tmp 폴더의 세션 데이터를 읽어 들이는 간단한 php 스크립트 예제입니다.

예제(ex #1
 <?php
 session_start
();

 $path ini_get('session.save_path'
); 

 
$handle opendir($path);

 
while(false !== ($entry readdir($handle
))){
    if(
substr($entry05) === 'sess_'
){
        if((
$entry !== '.') && ($entry !== '..'
)) {
            if(
$data file_get_contents("$path/$entry"
)){
                
session_decode($data
); 

                
$session $_SESSION
;
                
$_SESSION 
= array(); 

                echo 
"Session[".substr($entry5)."]\n"
;
                
print_r($session
);
                echo 
"<br />\n"
;
            }
        }
    }
 }
 
closedir($handle
);
 ?>

이 스크립트로 본인의 도메인 세션 뿐 아니라 공유호스팅에 있는 다른 사용자의 모든 세션 데이터를 긁어 옵니다. 만약, 다른 도메인의 사용자가 비밀번호나 아이디를 세션으로 저장하고 관리하고 있다면, 이는 너무도 쉽게 비밀번호를 취득해 버립니다.
 
세션 삽입 공격

세션 데이터 저장소를 기본 임시폴더로 지정하지 않고, 임의 폴더(사용자 폴더)로 별도 관리하여 사용하더라도 그 폴더 또한, 읽기, 쓰기 권한이 있어 위험은 언제나 존재합니다. 세션 삽입 공격의 유형은 스크립트 내에 다른 사용자의 세션을 추가, 수정, 삭제하는 등 세션 변조로 악용할 가능성은 충분히 있습니다.

다음의 스크립트는 사용자의 기존 세션 데이터를 쉽고, 편리하게 수정할 수 있게 작성된 폼입니다.

예제(ex #2
 <?php 
 session_start
(); 
 
$path ini_get('session.save_path');   

 function 
htmlchars($sessname){
     return 
htmlentities($sessnameENT_QUOTES'utf-8'); 
 }


 
// 폼전송시 실행 
 
if(@$_POST['send'] === 'write'){ 
     foreach(
$_POST as $key => $val){ 
         if(@
$_POST[$key] !== 'write'){ 
             
$_SESSION $val
             
$val session_encode(); 
             
file_put_contents("$path/$key"$val); 

             
print_r($val); 
         } 
     } 
     
$_SESSION = array(); 

 
// 파일 변조 폼 
 
} else { 
      
     echo 
"<form action='' method='post'>\n"
     echo 
"<input type='hidden' name='send' value='write'>"
      
     
$handle opendir($path); 
     while(
false !== ($entry readdir($handle))){  
         if(
substr($entry05) === 'sess_'){  
             if((
$entry !== '.') && ($entry !== '..')) {  
                 if(
$data file_get_contents("$path/$entry")){  
                     
session_decode($data);  
                     
$data $_SESSION;  
                     
$_SESSION = array();  

                     
$sessname htmlchars(substr($entry5)); 
                      
                     echo 
"Session[".$sessname."]\n";  
                      
                     foreach(
$data as $key => $val){ 
                          
                         
$key htmlchars($key); 
                         
$val htmlchars($val);  

 echo 
$key.": <input type='text' name='".
       
$sessname."[".$key."]' value='".$val."'>\n"
                     } 
                     echo 
"<br />\n";  
                 }  
             }  
         }  
   }  
   
closedir($handle);  
   echo 
"<input type='submit'>\n"
   echo 
"</form>\n"
 } 
 ?>

이러한 스크립트의 실행 권한을 보통 막아 두어 관리자는 안심한다지만, 문제는 공격자가 파일업로드의 보안 취약점을 이용하여 우회하는 방법을 알게 되어, 업로드된 파일로부터 php 스크립트 사용 권한이 취득된다면, 문제는 달라 집니다. 그래서 가장 안전한 최상이라 할 수 있는 방법으로 디비로 관리하는 방법을 선택하는 것입니다.

session_set_save_handler 함수를 이용

bool session_set_save_handler ( callback $open , callback $close , callback $read , callback $write , callback $destroy , callback $gc )

이 함수를 이용한다면 파일이 아닌 디비로 세션 관리가 가능해지는데, 이 함수를 먼저 정의한 후에 session_start 함수를 뒤에 정의하면 디비로 관리되며, 이 함수를 정의하지 않은 폐이지는 일반 파일로 저장됩니다.

이 함수는 성공할 경우 TRUE를, 실패할 경우 FALSE를 반환합니다.

 인자

설명

 open

열기 함수, 클래스의 생성자처럼 작동하고 세션이 열릴 실행됩니다. 열기 함수는 인수를 받습니다. 첫번째는 저장 경로이고 두번째는 세션 이름입니다.

 close

닫기 함수, 클래스의 소멸자처럼 작동하고 세션 연산이 끝났을 실행됩니다.

 read

읽기 함수는 저장 핸들러가 정상적으로 작동하기 위해 항상 문자열 값을 반환해야 합니다. 읽을 데이터가 없으면 문자열을 반환합니다. 다른 핸들러에서 오는 값은 논리 표현으로 변환하여 반환합니다. 성공시엔 TRUE, 실패시엔 FALSE입니다.

 write

"쓰기" 핸들러는 출력 스트림이 닫힐 때까지 실행되지 않습니다. 그러므로, "쓰기" 핸들러에서 디버그 구문 출력은 브라우저에서 없습니다. 디버그 출력이 필요하면, 디버그 출력을 파일로 써야 합니다.

 destroy

 session_destroy() 세션이 파괴될 실행되며, 세션 id 인수로 받습니다.

 gc

쓰레기 수거자, 세션 쓰레기 수거가 실행될 실행되며, 최대 세션 수명을 인수로 받습니다.


다음 예제는 파일로 설정한 스크립트입니다.

예제(ex #3
 <?php
 function open($save_path$session_name
){
  global 
$sess_save_path
;

  
$sess_save_path $save_path
;
  return(
true
);
 }

 function 
close
(){
  return(
true
);
 }

 function 
read($id
){
  global 
$sess_save_path
;

  
$sess_file "$sess_save_path/sess_$id"
;
  return (string) @
file_get_contents($sess_file
);
 }

 function 
write($id$sess_data
){
  global 
$sess_save_path
;

  
$sess_file "$sess_save_path/sess_$id"
;

  if (
$fp = @fopen($sess_file"w"
)) {
    
$return fwrite($fp$sess_data
);
    
fclose($fp
);
    return 
$return
;
  } else {
    return(
false
);
  }
 }

 function 
destroy($id
){
  global 
$sess_save_path
;

  
$sess_file "$sess_save_path/sess_$id"
;
  return(@
unlink($sess_file
));
 }

 function 
gc($maxlifetime
){
  global 
$sess_save_path
;

  foreach (
glob("$sess_save_path/sess_*") as $filename
) {
    if (
filemtime($filename) + $maxlifetime time
()) {
      @
unlink($filename
);
    }
  }
  return 
true
;
 }

 session_set_save_handler("open""close""read""write"
   "destroy""gc"
);

 session_start
();

 ?>

디비로 스크립트를 제작하는데, 우선 테이블을 만들 필요가 있습니다. 테이블을 만들고, 다음 코드로 작성하면 나머지는 php 프로세서가 알아서 처리를 해주기 때문에, 사용자가 따로 작업할 필요가 없이 _SESSION 를 그대로 사용해도 됩니다.

예제(ex #4
 <?php 
 /*
 CREATE TABLE sessions (
    session_id varchar(32) NOT NULL,
    session_expire int(11) UNSIGNED NOT NULL,
    session_data text NOT NULL,
    PRIMARY KEY (session_id)
 );
 */ 

 define("DB_HOST_NAME""localhost");
 define("DB_USER""디비 유저");
 define("DB_PASS""디비 패스워드");
 define("SESSION_TABLE_NAME""sessions"); 


 function _open
(){
    global 
$conn
;

    if(
$conn mysql_connect(DB_HOST_NAMEDB_USERDB_PASS
)){
        return 
mysql_select_db(SESSION_TABLE_NAME$conn
);
    }
    return 
false
;
 }

 function 
_close
(){ 
    return true;
 }

 function 
_read($id
){
    global 
$conn
;

    
$id mysql_real_escape_string($id
);

    
$sql "SELECT session_data FROM " SESSION_TABLE_NAME 
     
" WHERE id='" $id "'"
;

    if(
$result mysql_query($sql$conn
)){
        if(
mysql_num_rows($result) == 0
){
            return 
false
;
        } else {
            if(
$row mysql_fetch_assoc($result
)){
                return 
$row['session_data'
];
            } else {
                return 
false
;
            }
        }
    }
 }

 function 
_write($id$data
){
    global 
$conn
;

    
$expire time
();

    
$id mysql_real_escape_string($id
);
    
$expire mysql_real_escape_string($expire
);
    
$data mysql_real_escape_string($data
);

    
$sql "REPLACE INTO " SESSION_TABLE_NAME 
     
" VALUES ('$id', '$expire', '$data')"
;

    if(
$row mysql_query($sql$conn
){
        return 
$row
;
    } else {
        return 
false
;
    }
 }

 function 
_destroy($id
){
    global 
$conn
;

    
$id mysql_real_escape_string($id
);

    
$sql "DELETE FROM " SESSION_TABLE_NAME " WHERE id='$id'"
;

    if(
$row mysql_query($sql$conn
){
        return 
$row
;
    } else {
        return 
false
;
    }
 }

 function 
_gc($max
){
    global 
$conn
;

    
$old_time time()-$max

    
$old_time mysql_real_escape_string($old_time
);

    
$sql "DELETE FROM " SESSION_TABLE_NAME 
     
" WHERE session_expire < '$old_time'"
;

    if(
$row mysql_query($sql$conn
){
        return 
$row
;
    } else {
        return 
false
;
    }
 } 

 session_set_save_handler('_open''_close''_read''_write',
      
'_destroy''_gc'
);

 session_start
(); 

 // 사용자 코드 작성

 ?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

백도어 URL 은 URL를 통해 직접 리소스에 엑세스할 수 있는 것을 말합니다. 다음 예제  내용이 있다고 가정한다면, 상식적으로 test.php 파일을 접속하도록 되어 있지만, 이 백도어 URL은 cal.php 파일인 다이렉트로 접속해서 파일을 변조해 버립니다.

예제(ex #1
 <?php 
 /* 
 test.php ----
 */

 $auth 
false
;

 $auth = @$_POST['userid'
];

 include 
"cal.php"
;
 /* 
 cal.php
 // --------------------------------------------- 
 $name = $auth; 
 echo $name; 
 --------------------------------------------- //
 */ 

 ?>

백도어 URL를 막기 위해 인쿠르드파일을 루트 바깥에 저장해 두거나 아니면 상수를 사용하는 방법이 있습니다. 이렇게 함으로써 직접적인 파일 접근을 차단할 수 있을 것입니다.

예제(ex #2
 <?php
 $auth 
false

 
$auth = @$_POST['userid'
];

 // 상수 정의
 if(!defined("USER_URL"
)){
    
$salt 'LEFTSALT'
;
    
$uniqid uniqid
();
    
$USER_URL sha1(md5($salt '_' md5($uniqid '_' $salt
)));
    
define("USER_URL"$USER_URL
);
 }

 include 
"cal.php"
;
 /*
 cal.php
 // ---------------------------------------------
 if(!defined("USER_URL")){
    echo "정상적인 방법으로 접근해 주세요.";
    exit();
 } 
 $name = $auth; 
 echo $name; 
 --------------------------------------------- //
 */ 

 ?>
블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

단방향 암호화란? 평문으로 암호화할 수 있어도 암호화된 암호문을 평문으로 복원할 수 없는 비가역적 암호화를 말합니다. 다시 말해 복원이 불가능한 상태입니다.

  평문 암호화  복호화  
 양방향 test 098f6bcd4621d373cade4e832627b4f6  test  가역적
 단방향 test 098f6bcd4621d373cade4e832627b4f6    비가역적

일반적으로 단방향 암호화는 실제 많이 사용하는 방법이며, 특히 php 유저는 md5 알고리즘을 널리 사용합니다. 이 방법은 암호화하고자 하는 문자열을 받아 128비트로 된 값을 만들어 낸다고 합니다.

문제는 md5 알고리즘도 보안의 허점에 노출되는 등 보안 강화가 필요한 현 시점에 진보된 해쉬 알고리즘이 필요합니다. 현재 문제시 되는 단순히 숫자로만 이루어져 있거나 영문, 숫자로 이루어져 있더라도 암호길이가 짧아 보안에 취약합니다.

짧은 암호는 해독에 걸리는 시간이 그리 길지 않습니다. 그 만큼 컴퓨터의 성능 또한 진보했으니까요.

진보된 알고리즘을 이용해 암호화하여 디비에 저장하되, 사용자가 로그인에 사용될 패스워드를 저장하는 것 만큼 중요하게 다뤄야 하는데, 숫자, 영문자를 포함하여 9자 이상 패스워드를 입력하게 하고, md5 에 임의의 값을 더한 값에 sha1 이나 sha512 와 같은 알고리즘으로 한번 더 암호화하여 저장하도록 합니다.

간단한 암호를 사용하는 것은 브루트 포스 공격(brute force attack)의 공격 대상이 되는 만큼 위험합니다.

브루트 포스 공격이란? 무차별 대입공격이란 뜻으로 성공할 때까지 가능한 모든 조합의 경우의 수를 시도해 원하는 공격을 시도하는 것으로 대표적인 예로 크렉 등 소프트웨어를 이용하여 password를 추측하는 방법입니다.

대표적인 공격 프로그램으로는 무식하게 password를 다양하게 대입하는 것과 사전을 통해 공격 시도하는 Brutus 프로그램이 있습니다. 특별한 해시 알고리즘을 사용한다 하더라도 로그인 시도 실패 횟수 초과시 강제적으로 로그인 시도를 제한하는 방법 또한 좋은 예입니다.

string hash ( string $algo , string $data [, bool $raw_output= false ] ) (PHP 5 >= 5.1.2, PECL hash >= 1.1)

요즘은 리눅스나 공인인증서 등 암호화가 필요한 곳에 보다 진보된 해시로 많이 교체되고 있습니다. 대략 해시 종류는 다음 표를 참고하면 됩니다.
 
 해시 자릿수
 md2  32
 md4  32
 md5  32
 sha1  40
 sha224  56
 sha256  64
 sha384  96
 sha512  128
 ripemd128  32
 ripemd160  40
 whirlpool  64
 tiger128,3  80
 tiger160,3  128
 tiger192,3  32
 tiger128,4  40
 tiger160,4  48
 tiger192,4  32
 snefru  40
 gost  48
 adler32  64
 crc32  64
 crc32b  64
 haval128,3  8
 haval160,3  8
 haval192,3  8
 haval224,3  128
 haval256,3  128
 haval128,4  32
 haval160,4  40
 haval192,4  48
 haval224,4  56
 haval256,4  64
 haval128,5  32
 haval160,5  40
 haval192,5  48
 haval224,5  56
 haval256,5  64

문자열 'test'를 md5 로 암호화하고, 한번 더 암호화 해줍니다.

예제(ex #1
 <?php
 echo hash('sha512''passwd_' . hash('md5''test'
));
 ?>

다음 예제로 다양한 해시 알고리즘의 결과를 확인할 수 있습니다.

예제(ex #2
 <?php 
 $data 
"test"
;  
 foreach (
hash_algos() as $v
) { 
        
$r hash($v$datafalse
); 
        
printf("%-12s %3d %s\n"$vstrlen($r), $r
); 
 } 
 ?>
 해시 자릿수 암호화
 md2 32 dd34716876364a02d0195e2fb9ae2d1b
 md4 32 db346d691d7acc4dc2625db19f9e3f52
 md5 32 098f6bcd4621d373cade4e832627b4f6
 sha1 40 a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
 sha224 56 90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53
d4286ade99a809
 sha256 64 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b
822cd15d6c15b0f00a08
 sha384 96 768412320f7b0aa5812fce428dc4706b3cae50e02a64
caa16a782249bfe8efc4b7ef1ccb126255d196047dfe
df17a000a9
 sha512 128 ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772
e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc8
87fd67b143732c304cc5fa9ad8e6f57f50028a8ff
 ripemd128 32 f1abb5083c9ff8a9dbbca9cd2b11fead
 ripemd160 40 5e52fee47e6b070565f74372468cdc699de89107
 ripemd256 64 fe0289110d07daeee9d9500e14c57787d9083f6ba1
0e6bcb256f86bb4fe7b981
 ripemd320 80 3b0a2e841e589cf583634a5dd265d2b5d497c4cc44b
241e34e0f62d03e98c1b9dc72970b9bc20eb5
 whirlpool 128 b913d5bbb8e461c2c5961cbe0edcdadfd29f068225
ceb37da6defcf89849368f8c6c2eb6a4c4ac75775d03
2a0ecfdfe8550573062b653fe92fc7b8fb3b7be8d6
 tiger128,3 32 8d1fd829fc83b37af1e5ba697ce8680d
 tiger160,3 40 8d1fd829fc83b37af1e5ba697ce8680d1d8bc430
 tiger192,3 48 8d1fd829fc83b37af1e5ba697ce8680d1d8bc430d76
682f1
 tiger128,4 32 f5cb297b5c37b5149de5409a190ae7f9
 tiger160,4 40 f5cb297b5c37b5149de5409a190ae7f949528b21
 tiger192,4 48 f5cb297b5c37b5149de5409a190ae7f949528b21dff1
d5d4
 snefru 64 8d25dd0b5715f7e4c799ade3a34b5f6148d0ce41699
2b5c2eaf614d35d5b3d30
 snefru256 64 8d25dd0b5715f7e4c799ade3a34b5f6148d0ce41699
2b5c2eaf614d35d5b3d30
 gost 64 a6e1acdd0cc7e00d02b90bccb2e21892289d1e93f62
2b8760cb0e076def1f42b
 adler32 8 045d01c1
 crc32 8 accf8b33
 crc32b 8 d87f7e0c
 salsa10 128 64f0dae5b7799f3389ef65e8ee02612c8e0f363b497c
6061a4e051952b03be7dc13db5fa56dd3ad9e76eae
7bfe17ba3330492d7d5f59a208e13840eee261240c
 salsa20 128 13a1ede976bd3f48a49aac1cafcf2caf88958aa37b18
440
a48dac7e27d62a54f2a8234b0b9fa9f578df36c2b946
01218510ec9d446a2ea27f73f2b9fd5b2d655
 haval128,3 32 a26075021e24a5bda74794d85e9fdb7f
 haval160,3 40 858c2c8f76afa7dd067d3d94c667c8aec6ac2650
 haval192,3 48 c4b8741917dabc27e2bebf58a6663a05b0d3dc4307
2a64b4
 haval224,3 56 f5b30a47580d8bfa256d6ed7604ffd2bb787abb22b5
3ad9f693e8d31
 haval256,3 64 593c9aed973bb51a3c852fb4e051d7c26686b9468b
4e405350cb6805dc1b99e6
 haval128,4 32 1ba3b2186ad54d024603d61ddb9d2f42
 haval160,4 40 516d3243a12ce3af38a005003c7221bf85299714
 haval192,4 48 16ff6de6751cb654c1f788ee2f14ceddb86eec343ef8
7cd5
 haval224,4 56 deea192a84b5e29ab958202b22a0b604c1df1298ee7
d32
ee5d7e2954
 haval256,4 64 435ded7266cba07f389d6e74c954b184e1ddacc8a7
b8dc022db3ca4450a738cd
 haval128,5 32 f5b480f6965efd5f5e6232925c5eed14
 haval160,5 40 f5e3770031ebc6c46fe78d92890e17b1bef93b87
 haval192,5 48 527383196142f6f3352f8a152dd06c9c0a50efcb83a6
46f0
 haval224,5 56 9666797abc57d096c2a9922e350390437f9c2e378ce
2e43e0d816d90
 haval256,5 64 a4b59d68e0111000856baca9e6573a2adc2b56b6b4
d87f7cf31de24a77b93768
 
단방향 sha1 함수

만약, php 5.1.2 이하 버전이라면, sha1 함수를 사용해도 됩니다. 분명 md5 보다는 진보된 함수입니다.

string sha1 ( string $str [, bool $raw_output ] ) (PHP 4 >= 4.3.0, PHP 5)

선택적인 raw_outputTRUE로 설정하면, 길이 20 인 바이너리 형식의 sha1 해시를 반환합니다. 기본값은 FALSE입니다.

예제(ex #3
 <?php
 $str 
'apple'
;
 if (
sha1($str) === 'd0be2dc421be4fcd0172e5afceea3970e2f3d940'
) {
    echo 
"Would you like a green or red apple?"
;
 }
 ?>

패스워드 뿐 아니라 아이디 역시 영문, 숫자를 강제적으로 포함하게 하여 보안을 강화시켜야 하고, 로그인 시도 실패 횟수를 제한해 둘 필요가 있습니다.

 관련글: 2011/09/30 - [mysql] PASSWORD 함수
            2011/09/30 - [mysql] MD5, SHA1 단방향 암호화 함수
            2011/09/14 - [mysql] 데이터의 암호화, 복호화하는 AES 함수
블로그 이미지

하보니

하보니와 함께하는 phP 초보

Tag 암호화

댓글을 달아 주세요

SQL 질의 공격이 현실적으로 많은 문제점을 가져 온다는 점에 최신 버전(MYSQL, MSSQL..)에서 자체적으로 필터링하고 있지만, 구버전에서 그렇지 못합니다.

SQL 질의를 신뢰할 수 없는 명령으로 인해 SQL 질의에서 접근 제어를 우회할 수 있여, 일반적인 인증과 인증 확인을 무시하고, 종종 SQL 질의가 사용자가 가질 수 없는 권한을 강제 취득하기도 합니다.

SQL 명령 인젝션이란? 공격자가 숨겨진 데이터를 노출하거나, 취약한 부분을 덮어쓰거나, 데이터베이스에 위험한 시스템 단계 명령을 실행하게 하는 SQL 명령을 생성하거나 대체하는 기술를 말합니다.

어플리케이션이 사용자 입력을 받아서, 이를 SQL 질의를 만들 떄 정적 인수로 조합함으로써 일어납니다. 유감스럽게도, 아래 예제들은 실제의 것입니다.

패스워드를 얻는 방법 중 하나는 검색 결과 페이지를 우회하는 것입니다. 공격자에게 필요한 것은 변수 중 하나라도 제대로 다뤄지지 않으면서 SQL 구문에 사용되는 것입니다.

이러한 필터는 일반적으로 SELECT 구문에서 WHERE, ORDER BY, LIMIT, OFFSET에 사용됩니다. 데이터베이스가 UNION 구조를 지원하면, 공격자는 원래 질의에 전체 질의를 덧붙여서 임의의 테이블에서 패스워드를 얻을 수 있습니다. 암호화된 패스워드 필드를 강력히 권합니다.

SQL 질의 공격


검증되지 않는 변수를 전적 사용자의 신뢰를 믿고 필터링하지 않는다면, 문제는 커질 수 밖에 없습니다. 다음 변수에 이 질의('--로..)가 $query 에서 사용하는 변수 중 하나에 할당되면, 문제는 커질 수 밖에 없습니다.

예제 (ex #1

 <?php
 $query  
"SELECT id, name, inserted, size FROM products
                  WHERE size = '
$size
'
                  ORDER BY 
$order LIMIT $limit$offset;"
;
 $result mysql_query($query
);
 ?>

다음 질의로 비밀번호없이 누구나 접속이 가능하게 변질되어 버립니다.

예제 (ex #2
 <?php
 $_POST['username'] = 'aidan';
 $_POST['password'] = "' OR ''='"
;

 $query "SELECT * FROM users WHERE user='{$_POST['username']}'
            AND password='
{$_POST['password']}'"
;
 mysql_query($query
);

 echo $query;
 // 결과: SELECT * FROM users WHERE user='aidan' AND password='' OR ''=''
 ?>

SQL UPDATE도 공격받을 수 있는데, 이런 질의를 완전한 새 질의를 덧붙일 수 있습니다. 또한 공격자가 SET 절을 다룰 수도 있습니다. 이 경우 질의를 성공적으로 변경하기 위하여 일부 스키마 정보를 가지고 있어야 합니다.

예제 (ex #3
 <?php
 $query "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
 ?>
 

악의적인 사용자가 $uid ' or uid like'%admin'; -- 값을 넣어서 관리자 패스워드를 변경하거나, $pwd "hehehe', admin='yes', trusted=100 "(마지막 공백 포함)을 설정하여 권한을 얻을 수도 있습니다.

예제 (ex #4
 <?php
 // $uid == ' or uid like'%admin%'; --
 $query "UPDATE usertable SET pwd='...' WHERE uid='' or uid like 
'%admin%'; --"
;

 // $pwd == "hehehe', admin='yes', trusted=100 "
 $query 
"UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100
 WHERE ...;"
;
 ?>

데이터베이스 호스트의 OS 등급 명령에 접근하는 문제시 될 예제입니다.

예제 (ex #5
 <?php
 $query  
"SELECT * FROM products WHERE id LIKE '%$prod%'"
;
 $result mssql_query($query
);
 ?>

공격자가 $proda%' exec master..xp_cmdshell 'net user test testpass /ADD' -- 값을 제출하면, $query는:

예제 (ex #6
 <?php
 $query  
"SELECT * FROM products
             WHERE id LIKE '%a%'
             exec master..xp_cmdshell 'net user test testpass /ADD'--"
;
 $result mssql_query($query
);
 
?>

이러한 공격은 주로 보안을 염두에 두지 않고, 쓰여진 코드 취약점에서 발생합니다. 어떠한 입력도 믿어서는 안되며, 최신 버전이라도 한번더 필터링해 주어야 합니다.

특히 클라이언트측에서 오는 입력은 믿어서는 안됩니다. select, hidden input 필드, 쿠키도 마찬가지입니다. 첫 번째, 두 번째 예제에서 그러한 질의가 큰 문제를 일으킬 수 있음을 보여주고 있습니다.

SQL 인젝션 회피


다음은 안전한 질의 예제가 됩니다.

예제 (ex #7

 <?php
 settype
($offset'integer'
);
 $query "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET
               
$offset;"
;

 $query sprintf("SELECT id, name FROM products ORDER BY
                    name LIMIT 20 OFFSET %d;"
$offset);
 ?>

이러한 문제를 회피하기 위해 안전한 방법으로 문자열 회피 함수를 사용하는 것인데, mysql_real_escape_string() 가 그것입니다. mysql_real_escape_stringmysql_query 에서 특수 문자열을 이스케이프하기 위해 사용되며, 그러므로 SQL 인젝션 공격이 동작하지 않고 질의가 정확하게 실행될 것입니다.

예제 (ex #8

 <?php
 if (isset($_POST['product_name']) &&
       isset(
$_POST['product_description']) &&
       isset(
$_POST['user_id'
])) {
    
// 접속
    
$link mysql_connect(
     
'mysql_host',
     
'mysql_user',
     
'mysql_password'

    );
    if(!
is_resource($link
)) {
        echo 
"서버 접속 실패\n"
;
        
// ... 오류를 적절히 기록
    
} else {
        
// ON일 경우 magic_quotes_gpc/magic_quotes_sybase 효과 제거
        
if(get_magic_quotes_gpc
()) {
            
$product_name        =
                stripslashes
($_POST['product_name'
]);
            
$product_description =
               
stripslashes($_POST['product_description'
]);
        } else {
            
$product_name        $_POST['product_name'
];
            
$product_description $_POST['product_description'
];
        }
        
// 안전한 질의 만들기
        
$query sprintf("INSERT INTO products (
                          `name`, `description`, `user_id`)
                           VALUES ('%s', '%s', %d)"
,
              
mysql_real_escape_string($product_name$link
),
              
mysql_real_escape_string($product_description$link
),
              
$_POST['user_id'
]);

        
mysql_query($query$link
);

        if (
mysql_affected_rows($link) > 0
) {
            echo 
"Product inserted\n"
;
        }
    }
 } else {
    echo 
"Fill the form property\n"
;
 }
 ?>


아니면 addslashes() str_replace() 함수를 사용할 수도 있습니다. addslashes() 는 데이터베이스 질의 등에서 처리할 필요가 있는 문자 앞에 백슬래시를 붙인 문자열을 반환합니다. 이 문자들은 작은 따옴표('), 큰 따옴표("), 백슬래시(\), NUL(NULL 바이트)입니다.

addslashes()를 사용하는 대표적인 예는 데이터베이스에 데이터를 넣을 때 입니다. 예를 들어, 데이터베이스에 O'reilly 라는 이름을 넣으려고 할때, 이스케이프할 필요가 있습니다. 대부분의 데이터베이스는 \을 사용하기에 O\'reilly가 되어야 합니다. 이 데이터를 데이터베이스에 넣으면 추가한 \은 저장되지 않습니다.

예제 (ex #9
 <?php
 $str 
"Is your name O'reilly?"
;

 // 출력: Is your name O\'reilly?
 echo addslashes($str
);
 ?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

Tag 암호화

댓글을 달아 주세요

크로스 사이트 스크립팅(cross-site scripting, 영문 약어 XSS)은 웹 페이지에 클라이언트 사이드 스크립트를 삽입하여 다른 사용자가 이를 실행하게끔 허용하는 취약점으로, 웹에서 사용되는 대표적 스크립트 언어로 자바스크립트, VB스크립트가 있습니다.

이 취약점은 사용자로부터 입력 받은 값을 제대로된 검사를 하지 않고 그대로 사용할 경우 나타나는데, 주로 사용자의 정보(GET, POST, COOKIE, SESSION 등)를 탈취하기 위하여 특수 문자나 예약어, 스크립트를 나타내는 (, ), !, @, {, }, \n, \t, \0, ', " , > , < , % , $ 등의 문자를 이용합니다.

공격 대상 웹사이트에 삽입한 스크립트를 이용하여 다른 웹사이트로 접근하는 것도 가능하기 때문에 사이트 간 스크립팅이라고 합니다.

매월 크로스 사이트 스크립팅 공격이 상용 사이트에서 발생하고 그러한 위험성을 설명하는 경고문이 발표된다고 합니다. 주의하지 않는 다면 여러분의 웹 사이트나 회사도 이러한 공격의 희생양이 될 것입니다.


웹사이트 공격


크로스 사이트 스크립팅으로 인해 다음과 같은 위험에 빠지게 됩니다.
  • 웹 사이트상의 애플리케이션이 크로스 사이트 스크립팅에 취약하다고 알려지면 공격자는 공격 목표의 권한을 사용하여 실행할 수 있도록 JavaScript, VBScript, ActiveX, HTML, Flash를 투입하는 것입니다. 공격이 활성화 되면 계정 하이재킹, 사용자 설정 변경, 쿠키 훔치기 및 오염, 오류 광고 등이 가능하게 됩니다.

  • 공격자는 사용자 세션, 쿠키가 종료되기 전에 사용자 세션을 인계 받을 수 있으며, 공격자가 제공한 URL로 사용자가 접근할 수 있도록 할 수 있는 침입자는 공격자가 선택한 스크립트나 HTML이 사용자 브라우저에서 실행될 수 있도록 할 수 있고, 이러한 기술을 사용하여 공격자는 URL로 접근했던 사용자의 권한을 사용하여 액션을 취할 수 있습니다. SQL 데이터베이스에 쿼리를 실행하거나 결과를 보고 목표 시스템 구현의 오류를 악용할 수도 있습니다.

공격 샘플


다음과 같이 제대로 된 검사를 하지 않는다면, 공격자는 얼마든지 공격삽입이 가능합니다.

 <?php 
 $search 
"'><script>alert(123)</script><'"
;  
 
?> 
 <input ~~ value='<?=$search?>'> 

위와 같이 필터를 거치지 않는 다면 다음과 같은 결과를 만들어 냅니다.

 <input ~~ value=' '><script>alert(123)</script><' '>

필터를 처리하기 위해 다음과 같이 한줄 코드를 삽입하여 공격을 회피할 수 있습니다. 

 <?php 
 $search 
"'><script>alert(123)</script><'"
;  
 
?> 
 <input ~~ value='<?=htmlspecialchars($search)?>'> 

다음은 필터 처리된 모습니다.

  <input ~~ value=''&gt;&lt;script&gt;alert(123)&lt;/script&gt;&lt;''> 

다음은 대표적 링크삽입 공격입니다.

 <?php 
 $_GET
['link'] = "'<SCRIPT>malicious code</SCRIPT>'"
;  
 
?> 
 <A HREF=http://test.com/list.cgi?clientprofile=<?=$_GET['link']?>>Click here</A> 

이 역시 다음 코드 한줄로 필터가 가능합니다.

 <?php 
 $_GET
['link'] = "'<SCRIPT>malicious code</SCRIPT>'"
;  
 
?> 
 <A HREF=http://test.com/list.cgi?clientprofile=<?=urlencode($_GET['link'])?>>Click here</A>

결과 모습입니다.

 <A HREF=http://test.com/list.cgi?clientprofile=%27%3CSCRIPT%3Emalicious+code%3C%2FSCRIPT%3E%27>Click here</A>

이러한 접근 방식의 기초는 사용자 input 나 link 를 절대 신뢰하지 말고 HTML 스팩에서 정의된 메타문자("특수" 문자)를 언제나 필터링 하여 스크립트를 보호하는 것입니다.

그리고 쿠키나 세션으로 데이터를 구울때 중요 데이터인 경우 암호화하여 혹여나 데이터 유출시 쉽게 알아보지 못하게 해둘 필요가 있어 암호화기법도 중요합니다.
블로그 이미지

하보니

하보니와 함께하는 phP 초보

Tag 암호화

댓글을 달아 주세요

리플레이 공격방식은 프레젠테이션 공격이라 부르는데, 정상적인 사용자가 인증권한을 취득하기 위해 전송한 데이터를 재전송하는 모든 종류의 공격을 말합니다. 리플레이 공격과 마찬가지로 패스워드 스니핑 공격을 차단하려는 노력은 많지만 완전 차단방법은 없습니다.

그렇기 때문에 인증과정 중 폼의 여러 가지 속성을 설치한다 하여 공격자에게는 그다지 중요하지 않다 생각되는 잡다한 것으로 보며, 그래서인지 요즘은 HTTP요청내용과 HTTP응답내용이 노출되지 않게 보호하려는 방법으로 SSL전송방식을 많이 애용하는 편입니다. 허나 누가 뭐라해도 패스워드를 보호하는 일이야 말로 가장 중요한 부분임을 누구도 거부하지 않습니다.

이제는 _POST방식도 인증보호에 기대가 없고, 그렇다고 MD5도 믿음이 있어 보이지 않습니다. 왜냐하면 몇 년전에 이미 MD5가 복호화되어 암호가 뚤려 버렸기 때문입니다.

 <form action=https://mydomain.com/login.php method="POST"><br />
 myid:     <input type="text" name="username"><br />
 mypass:<input type="password" name="userpass"><br />
 </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($host80
)){
      
// mydomain.com 에 HTTP요청하고, HTTP응답을 받습니다.
      
fputs($fp$request
);

      
$response ''
;
      while(!
feof($fp
)){
            
// 1줄씩 응답을 읽어 옵니다.
           
$response .= fgets($fp1024
);
      }
      
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?&#52025;鎬?燕?
 
$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($filenametrue
));
 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']);
 }
 ?>


블로그 이미지

하보니

하보니와 함께하는 phP 초보

댓글을 달아 주세요

  • MD5 2011.09.27 22:30  댓글주소  수정/삭제  댓글쓰기

    MD5가 뚫렸다는게 해시에서 원본을 찾아낼수 있게 뚫렸다는게 아닙니다.
    해시는 기본적으로 강한 충돌저항성을 가지는데 임의로 충돌을 만들어 내는데 성공했다고 합니다.
    이를 이용하면 예를들어 가짜 인증서를 만들수 있겠죠...

  • MD5 2011.09.27 22:31  댓글주소  수정/삭제  댓글쓰기

    일단 간단한 암호의 경우에는 브루트포스나 레이보우테이블을 이용해서 뚫는게 가능합니다.
    다만 충분히 강한 SOLT를 붙이면 훨씬 뚫기 힘들어지겠죠...

    • Favicon of https://blog.habonyphp.com BlogIcon 하보니 2011.09.28 18:17 신고  댓글주소  수정/삭제

      단순히 [a-z 0-9]로 이루어진 문자로는 보안이 약하다는 뜻이네요. 중간에 특수문자를 섞어두면 좀 안심이 되지 않을까요?

      조은 정보 감사합니다.