카테고리 없음

SW 개발보안 2일차 - php

ajh 2026. 5. 11. 09:02

목차

1. 변수
2. 연산자
3. 제어문
4. include / require
5. 함수
6. 배열
7. 클래스
8. 파일 처리
9. DB관련 함수
10. PHP를 이용한 로그인 구현하기
11. 세션/쿠키
12. PHP를 이용한 게시판 만들기

 

PHP는 웹 서버에서 실행되어서 동적인 웹 페이지를 만들어주는 스크립트 언어이다.

 

아파치 웹서버 로그 파일의 의미
  . Apache 웹서버는 클라이언트 접속 기록과 서버 오류 기록을 로그 파일로 저장한다.
  . Rocky Linux, CentOS, RHEL 계열에서는 기본적으로 /var/log/httpd/ 디렉터리에 로그가 저장된다.

  . /var/log/httpd/access_log
    - 클라이언트가 접속하면 웹서버에 누가, 언제, 어떤 페이지에 접속했는지 기록하는 로그 파일이다.
    - 클라이언트에 접속하면 무조건 이 파일에 기록된다

  . /var/log/httpd/error_log
    - Apache 웹서버에서 발생한 오류, 경고, 실행 상태를 기록하는 로그 파일이다.
    - PHP의 코드 에러나 웹서버가 잘못된 설정을 하면 이 파일에 기록한다.

 

PHP 실행방법 3가지
첫 번째 실행 방법 
  . 웹 브라우저를 이용해서 실행하는 방법
    - http://<IP주소 or 도메인주소>/파일명
    - lynx --dump localhost/파일명
    - curl localhost/파일명

두 번째 실행 방법 
  . /usr/bin/php 실행파일로 실행하는 방법
    - php 파일명

세 번째 실행 방법 
  . 파일에 실행권한을 부여하고 실행하는 방법
    - 이 부분으로 실행하는 경우에는 첫 줄에 
      #!/usr/bin/php 를 넣어준다.
    - chmod 755 파일명
    - ./파일명

 

1. 변수

 

변수의 다양한 형태
  . 기본 변수
    - 자료형을 미리 선언하지 않아도 된다.
      값에 따라서 자동으로 타입이 결정된다.
    $name = "홍길동";
    $age =  25;
  . 문자열 변수
    - 위와 동일하게 " 를 이용해서 문자열을 저장한다.
    $name = "홍길동";
  . 숫자형 변수
    - 위와 동일하게 정수를 저장한다.  
    $number = 100;
  . 실수형 변수
    - 소수점을 포함한 숫자를 저장한다.
    $pi = 3.14;
    $height = 175.5;
  . 불리언 변수
    - 참과 거짓을 저장한다.
    $is_true  = true;   // 1로 보인다.
    $is_false = false;  // 0으로 출력되는 것이 아니고 아무것도 출력되지 않는다.
  . NULL 
    - 값이 없음을 의미한다.
    $data = null;
    print($data);     // 아무것도 출력되지 않는다.
    var_dump($data);  // NULL이 출력된다.

  . 배열 변수
    - 여러 개의 값을 하나의 변수에 저장한다.
    - 인덱스 배열: 배열 방 번호가 0 ~ 1 ... 로 되어 있다.
    $fruits = array("apple", "banana", "grape");
    echo $fruits[0];
    echo $fruits[1];
    
    - 연관 배열: 키(key)와 값(value) 형태로 되어 있다.
    $user = array("name" => "홍길동",  "age" => 30, "address" => "Seoul");
    echo $user[0]; // Error: Undefined offset: 0
    echo $user["name"];
    echo $user["age"];
    
    - 다차원 배열
    $students = array(array("홍길동", 90, 100), array("홍길순", 80, 90));
    echo $students[0][0];  // 홍길동
    echo $students[0][1];  // 90

  . 객체 변수
    - 클래스로 만든 객체를 변수에 저장할 수 있다.
    class Person {
        var $name;
        var $age;
    }
    $p = new Person();
    $p->name = "홍길동";
    $p->age = 25;
    echo $p->name . " " . $p->age;  // 홍길동 25

  . 참조 변수
    - 한 변수를 다른 변수와 같은 메모리처럼 연결할 수 있
    $a = 10;
    $b = &$a;
    
    $b = 20;
    echo $a;  // 20
    echo $b;  // 20
    
  . 함수 안의 변수 
    - 지역 변수: 함수 안에서만 사용되는 변수
    - 전역 변수: 함수 밖에서 선언된 변수
    - 정적 변수: 함수가 끝나도 변수의 값이 계속 유지되는 변수
    
    // 지역 변수 테스트
    function local_function() {
        $a = 100;
        echo $a;
    }
    
    local_function();
    // echo $a;  // Undefined variable Error!

    // 전역 변수 테스트
    $a = 100;
    function global_function() {
        global $a; // Undefined variable: a
        echo $a;
    }

    global_function();
    echo "<br>" . $a;

    // 정적 변수 테스트
    function static_function() {
        static $number = 0;
        $number++;
        echo "number: $number <br>";
    }

    static_function();  // 1
    static_function();  // 2
    static_function();  // 3
    static_function();  // 4
    static_function();  // 5

  . 슈퍼 글로벌 변수
    - PHP에서 미리 제공하는 특별한 전역 배열을 의미한다.
    $_GET: URL 주소 뒤의 값을 전달받을 때 사용한다. 변수의 값이 HTTP Header에 붙어서 서버로 전송된다.
    $_POST: 폼(form) 전송 데이터를 받을 때 사용한다. 변수의 값이 HTTP Body에 붙어서 서버로 전송된다.
    $_SERVER: 서버 정보, 실행 환경 정보를 저장한다.
    $_COOKIE: 브라우저에서 저장된 쿠키값을 읽을 때 사용한다.
    $_SESSION: 서버에 저장된 세션값을 다룰 때 사용한다.
    $_FILES: 사용자가 업로드한 파일 정보를 다룰 때 사용한다.
    $_REQUEST: $_GET, $_POST, $_COOKIE 값들을 통합해서 접근할 수 있는 변수
    $_ENV: 서버의 환경변수(Environment Variables) 를 저장한다.


var1.php
/*
 * 파일명: var1.php
 * 프로그램 설명: PHP 변수
변수에 값을 저장하는 방법
  . PHP는 변수 앞에 $가 붙는다.
  . 변수는 자료형이 필요없고 미리 선언할 필요도 없다.
    - 그때 그때 바로 사용하면 된다.
    $변수 = 값;
 */

$a = 10;  // 변수 a에 10을 저장한다.
$b = 20;  // 변수 b에 20을 저장한다.
# 변수에 값을 출력하는 방법
# echo, print, printf를 이용해서 출력한다.
printf("변수a = $a, b = $b");
?>

 

-- var2.php --
<?php
$data["a"] = 10;
$data["b"] = 20;
echo($data["a"] . " " . $data["b"]);  <= . 은 +와 같은 역할
?>
-- var3.php --
<?php
echo $_GET['userid'] . "<br>";
echo $_SERVER['REMOTE_ADDR'];
?>

http://192.168.100.23/php/var3.php?userid=admin <= userid 입력

2. 연산자
산술 연산자
  . 연산 결과: 연산한 결과값


연산자     사용 예     의미 설명       
+         a + b      더하기 (Addition)
-         a - b      빼기   (Subtraction)
*         a * b      곱하기 (Multiplication)
/         a / b      나누기 (몫) (Division) 
%         a % b      나머지 (Modulus) 
++        ++a, a++   1증가 (Increment)  a += 1; a = a + 1; 
--        --a, a--   1감소 (Decrement)  a -= 1; a = a - 1;

대입 연산자
  . 연산 결과: 변수에 값을 저장
연산자  사용 예     의미 설명
=       a = b    우변의 값을 좌변에 대입
+=      a += b   좌변에 우변 값을 더한 결과를 대입 (a = a + b 동일)
-=      a -= b   좌변에 우변 값을 뺀 결과를 대입 (a = a - b 동일)
*=      a *= b   좌변에 우변 값을 곱한 결과를 대입 (a = a * b 동일)
/=      a /= b   좌변에 우변 값을 나눈 결과를 대입 (a = a / b 동일)
%=      a %= b   좌변에 우변 값을 나눈 나머지를 대입 (a = a % b 동일)

관계(비교) 연산자
  . 연산 결과: true, false
연산자    사용 예     의미 설명
==        a == b    같다 (동등) (Equal)
!=        a != b    같지 않다   (Not equal)
>         a > b     크다 (초과) (Greater than)
<         a < b     작다 (미만) (Less than)
>=        a >= b    크거나 같다 (이상) (Greater than or equal to)
<=        a <= b    작거나 같다 (이하) (Less than or equal to)


논리 연산자
  . 연산 결과: true, false
연산자    사용 예      의미 설명
&&       a && b     a와 b를 and 연산 (logical and)
||       a || b     a와 b를 or 연산 (logical or)
!        !a         a의 논리 부정 연산 (logical not)

논리곱(and) : 두 조건이 모두 참이면 참
논리곱 진리표           
a       b      a && b
false  false   false
false  true    false
true   false   false
true   true    true   

논리합(or)  : 두 조건 중 하나만 참이면 참
논리합 진리표
a       b      a || b
false  false   false
false  true    true
true   false   true
true   true    true

논리부정(not) : 피연산자의 값이 참이면 거짓, 거짓이면 참으로 바꾼다.
논리부정(not)은 true->false, false->true 로 뒤바꾼다.

논리부정 진리표
a      !a
true   false
false  true


숏컷(Shortcut)은 말 그대로 짧게 줄여서 쓰는 표현을 의미한다.
프로그래밍에서는 자주 사용하는 코드를 더 간단하게 표현할 때 숏컷 표현 또는 단축 표현이라고 한다.
  . $a = $a + 1; => $a++;
  . $a = $a + 5; => $a++ (X), $a += 5;

논리 연산에서 숏컷
  . 논리 연산에서 말하는 숏컷은 보통 단락 평가 또는 Short-circuit Evaluation이라고 한다.
  . 논리 연산을 할 때 결과가 이미 결정되면 뒤의 조건은 검사하지 않는 것을 의미한다.
  
AND 연산자의 숏컷
  . AND 연산자는 두 조건이 모두 참이어야 참이다.
  . e.g.) 
    $a && $b
    AND 연산에서 앞의 조건 $a가 false이면 뒤의 조건 $b를 검사할 필요가 없다.
    왜냐하면 AND는 하나라도 false이면 전체 결과가 무조건 false이기 때문이다.
    false && true   // false
    false && false  // false
    라서 다음과 같은 코드에서 첫 번째 조건이 거짓이면 두 번째 조건은 실행되지 않는다.


-- op1.php --

<?php
$a = 10;
$b = 3;
echo $a + $b  . "<br>";  // 13
echo $a - $b  . "<br>";  // 7
echo $a * $b  . "<br>";  // 30
echo $a / $b  . "<br>";  // 3.3333...
echo $a % $b;  // 1
?>
-- op2.php --
<?php
$a = 10;
echo ++$a . "<br>" ;   // 11
echo $a--;;   // 10
?>
-- op3.php --
<?php
$a = 10;
$a += 5;   // $a = $a + 5
echo $a . "<br>";   // 15
$a -= 3;   // $a = $a - 3
echo $a . "<br>";   // 12
$a *= 2;   // $a = $a * 2
echo $a . "<br>";   // 24
$a /= 4;   // $a = $a / 4
echo $a . "<br>";   // 6
$a %= 4;   // $a = $a % 4
echo $a . "<br>";   // 2
?>
-- op4.php --
<?php
$a = 10;
$b = 20;
var_dump($a == $b);   // false
var_dump($a != $b);   // true
var_dump($a > $b);    // false
var_dump($a < $b);    // true
var_dump($a >= 10);   // true
var_dump($b <= 20);   // true
?>
-- op5.php --
<?php
$userid = "admin";
$userpw = "1234";
var_dump($userid == "admin" && $userpw == "1234");
?>
-- op6.php --
<?php
$role = "admin";
var_dump($role == "admin" || $role == "manager");  // true
?>
-- op7.php --
<?php
$isLogin = false;
var_dump(!$isLogin);  // true
?>
<?php
// logic_shortcut1.php
$a = 10;
if($a > 20 && $a < 100) 
    echo "조건 만족"; 
else 
    echo "조건 불만족";  // 출력
?>
<?php
// logic_shortcut2.php
$a = 10;
if($a == 10 || $a == 20) 
    echo "조건 만족";   // 출력
else 
    echo "조건 불만족";
?>
<?php
// logic_shortcut3.php
function test()
{
    echo "test 함수 실행!<br>";
    return true;
}
test();
$a = 10;
if($a > 20 && test()) 
    echo "조건 만족"; 
else 
    echo "조건 불만족";  // 출력
?>
<?php
// logic_shortcut4.php
function test()
{
    echo "test 함수 실행!<br>";
    return true;
}
test();
$a = 10;
// if($a > 20 || test()) 
if($a == 10 || test()) 
    echo "조건 만족"; 
else 
    echo "조건 불만족";  // 출력
?>

3. 제어문
제어문은 프로그램의 실행 흐름을 제어하는 문법이다.
조건에 따라 특정 문장을 실행하거나, 반복해서 실행할 때 사용한다.

제어문의 종류
  . 조건문
    - 조건이 참인지 거짓인지 판단해서 실행 흐름을 결정한다.
  . 반복문
    - 특정 실행문을 반복해서 실행한다.
  . 분기문
    - 실행 흐름을 중간에 변경한다.
    
if문
  . if문은 조건식의 결과가 참(true) 인지 거짓(false) 인지 판단하여 실행문을 실행하는 조건문이다.
  . PHP에서 조건식의 결과가 true이면 { } 안의 실행문이 실행되고, false이면 실행되지 않는다.
  . if문의 종류
    - 단일 if문: 참일 때만 실행문을 실행하는 구조
    - if ~ else문: 참/거짓을 판단해서 실행문을 실행문을 실행하는 구조
    - 다중 if문: 비교 대상이 여러 개 있고 참인 경우 실행문을 실행하는 구조
    - 중첩 if문: if 안에 if문 있는 있는 구조  
  
단일 if문
  . 단일 if문은 조건식이 참일 때만 실행문을 실행하는 구조이다.

단일 if문 형식:
if(조건식)
{
   참일 경우 실행할 실행문;
}

 

switch ~ case문
  . 하나의 변수값을 여러 값과 비교하여 일치하는 값에 해당하는 실행문을 처리하는 선택 제어문이다.
  . switch ~ case문 형식: 
    switch(변수)  // 비교할 변수를 지정한다.
    {
        case 값1:  // 변수의 값이 해당 값과 같으면 그 아래 실행문을 수행하고 다르면 다음 case 값2 를 비교한다.
            실행문 1;
            break;  // 생략 가능하고 해당 case를 실행한 뒤 switch문을 빠져나올 때 사용한다.
                    // 생략하면 아래 case값2의 실행문2를 실행한다.
        case 값2:
            실행문 2;
            break;
        case 값3:
            실행문 3;
            break;
        default:  // 생략 가능
            실행문 4;
    }


<?php
// if3.php
$admin_ipaddress = "192.168.100.1";
if($_SERVER['REMOTE_ADDR'] != $admin_ipaddress) {
    print("Access Denied!");
    exit;
}

/*
if(isset($_GET['a']))
    $a = $_GET['a'];
else
    $a = 0;
*/

$a = isset($_GET['a']) ? $_GET['a'] : 0;

if($a == 0)
    print("값을 입력해야 합니다.");
else if($a == 1)
    printf("1");
else if($a == 2)
    printf("2");
else if($a == 3)
    printf("3");
else if($a == 4)
    printf("4");
else if($a == 5)
    printf("5");
else
    printf("1 ~ 5가 아닙니다.");

print("<br> 코드의 마지막 부분!");
?>

반복문
  . 반복문은 특정 실행문을 여러 번 반복해서 실행할 때 사용하는 제어구조이다.
  . PHP에서 사용하는 대표적인 반복문
    - while문
    - do ~ while문
    - for문

 

continue 문
  . 반복문 안에서만 사용할 수 있고 밖에서 사용하면 에러가 발생한다.
  . while문에서 continue문을 만나면 조건식으로 돌아간다.
    - continue문을 사용할 때는 반드시 if문과 함께 사용해야 한다.
  . for문에서 continue문을 만나면 증감식로 돌아간다.
    - continue문을 사용할 때는 반드시 if문과 함께 사용해야 한다.
    continue 형식: 
    if(조건식)
        continue;

break문
  . 반복문 안에서만 사용할 수 있고 밖에서 사용하면 에러가 발생한다.
  . while문에서 break문을 만나면 반복문을 탈출한다. 
    - break문을 사용할 때는 반드시 if문과 함께 사용해야 한다.
  . for문에서 break문을 만나면 반복문을 탈출한다. 
    - break문을 사용할 때는 반드시 if문과 함께 사용해야 한다.
    break 형식: 
    if(조건식)
        break;


while문
  . while문은 조건식이 참(true)인 동안 실행문을 반복하는 제어문이다.
    - 조건을 먼저 검사한 후 조건이 참이면 실행문을 수행한다.
  . while문 형식   
    초깃값;
    while(조건식)
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          :
        증감식;
    }

    while문 형식 설명
    - 초깃값: 반복문에서 사용할 변수의 시작값을 지정한다.
    - 조건식: 조건식이 참이면 반복하고 거짓이면 반복을 종료한다.
    - 실행문: 반복해서 수행할 문장이다. while 안에 있는 문장들은 모두 실행문이지만 증감식(증가, 감소)을 따로 뺀 것이다.
    - 증감식: 반복 변수의 값을 증가 또는 감소한다.
  . 무한 루프 형식
    - 무한 루프란 조건식에 항상 참이 되는 값을 넣어서 while문을 계속적으로 반복해서 실행한다.
      무한 루프로 프로그램을 실행하면 while문을 탈출할 수 없기 때문에 while문을 탈출하는 코드를 넣어야 한다.
      이때 사용하는 것이 break문이다.
       
    while(1)
    {
        실행문;
    }
    while(true)
    {
        실행문;
    }
    - break를 이용한 while문 종료
      무한 루프가 아니라도 while문이면 모두 해당된다.
      무한 루프는 반드시 존재해야 한다.
    while(1)
    {
        실행문;
        ... 
        if(조건식)    // if문을 반드시 사용해야 한다.
            break;  // 단독으로 사용하면 안되고 반드시 if문과 같이 사용해야 한다.
    }

do ~ while문
  . do ~ while문은 조건식이 참(true)인 동안 실행문을 반복하는 제어문이다.
    - 조건을 먼저 검사한 후 조건이 참이면 실행문을 수행하고 거짓이면 while문을 탈출한다.
    - while(조건식); 조건식에서는 ; 이 들어간다.
  . do ~ while문 형식
    
    초깃값;
    do
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          :
        증감식;
    } while(조건식);

    do ~ while문 형식 설명
    - 초깃값: 반복문에서 사용할 변수의 시작값을 지정한다.
    - 실행문: 반복해서 수행할 문장이다. while 안에 있는 문장들은 모두 실행문이지만 증감식(증가, 감소)을 따로 뺀 것이다.
    - 증감식: 반복 변수의 값을 증가 또는 감소한다.
    - 조건식: 조건식이 참이면 반복하고 거짓이면 반복을 종료한다.

for문
  . for문도 while문과 동일하게 조건식이 참(true)인 동안 실행문을 반복하는 제어문이다.
    - 조건을 먼저 검사한 후 조건이 참이면 실행문을 수행한다.
  . for문 형식
  
    초깃값;
    while(조건식)
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          :
        증감식;
    }
    
    for문의 기본 문법
    for(초깃값; 조건식; 증감식)
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          : 
    }
    
    for문 형식 설명
    - 초깃값: 반복문에서 사용할 변수의 시작값을 지정한다.
    - 조건식: 조건식이 참이면 반복하고 거짓이면 반복을 종료한다.
    - 실행문: 반복해서 수행할 문장이다. for 안에 있는 문장들은 모두 실행문이지만 증감식(증가, 감소)을 위로 따로 뺀 것이다.
    - 증감식: 반복 변수의 값을 증가 또는 감소한다.
    
    초깃값; 
    for(; 조건식; 증감식)  // 초깃값이 생략된 for문의 문법
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          : 
    }   
    
    초깃값;
    for(; ; 증감식)  // 조건식이 생략된 for문의 문법 (조건식이 생략되면 참)
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          : 
    }        

    for문의 무한루프 형식
    - 무한 루프란 조건식에 항상 참이 되는 값을 넣어서 for문을 계속적으로 반복해서 실행한다.
      무한 루프로 프로그램을 실행하면 for문을 탈출할 수 없기 때문에 for문을 탈출하는 코드를 넣어야 한다.
      이때 사용하는 것이 break문이다.    
    - break를 이용한 for문 종료
      무한 루프가 아니라도 for문이면 모두 해당된다.
      무한 루프는 반드시 존재해야 한다.  
      
    초깃값;
    for(;;)  // 증감식이 생략된 for문의 문법
    {
        실행문;  // 조건식이 참일 때 실행할 문장
          : 
        증감식;
        if(조건식)    // if문을 반드시 사용해야 한다.
            break;  // 단독으로 사용하면 안되고 반드시 if문과 같이 사용해야 한다.        
    }        


include / require
다른 PHP 파일을 현재 파일에 포함 시키는 제어구조이다.
PHP에서 include와 require는 외부 파일을 불러오는 데 사용되지만 둘은 차이점이 존재한다.

PHP 파일을 포함하는 종류
  . include(), include_once(), require(), require_once()
  
include 형식:
include "포함할 PHP파일명";
include_once 형식:
include_once "포함할 PHP파일명";

include vs include_once
include
  . 여러 개 include 해도 동일하게 include 한다. (중복을 허용함)
include_once
  . 여러 개 include 하면 하나만 include 한다. (중복을 허용 안함)

require vs require_once
require 형식
  . 여러 개 require 해도 동일하게 require 한다. (중복을 허용함)
  . require "포함할 PHP파일명";

require_once 형식
  . 여러 개 require 하면 하나만 require 한다. (중복을 허용 안함)
  . require_once "포함할 PHP파일명";


쿠키 에디터 설치하기

 

. 웹 브라우저에서 생성되고 저장된 쿠키(cookie)를 관리하는 도구이다.
  . 쿠키 에디터의 주요 기능
    - 쿠키 보기 (View) 
      - 현재 브라우저에 저장된 쿠키 정보를 확인할 수 있다.
      - 쿠키의 이름, 값, 도메인, 경로, 만료 시간, 보안 플래그 등을 확인할 수 있다.
    - 쿠키 수정 (Edit)
      - 쿠키의 값을 변경하거나, 만료 시간을 연장/단축하는 등 수정할 수 있다.
    - 쿠키 삭제 (Delete)
      - 특정 쿠키 또는 도메인에 속한 모든 쿠키를 삭제할 수 있다.
    - 쿠키 추가 (Add)
      - 새로운 쿠키를 만들어 브라우저에 추가할 수 있다.
    - 쿠키 내보내기/가져오기 (Export/Import)
      - 쿠키 데이터를 파일로 저장하거나 다른 브라우저로 가져오는 기능을 제공한다.
    - 개발 및 디버깅 지원
      - 웹 보안 담당자, 웹 개발자가 테스트 목적으로 쿠키를 조작하거나 특정 상태를 변경하는 데 유용하게 사용할 수 있다.

. 쿠키 에디터 사용 방법
    - 브라우저 확장 프로그램을 이용한다.
      - Google Chrome, Firefox 등 브라우저에 설치 가능한 쿠키 관리 확장 프로그램을 설치한다.
      - EditThisCookie(현재는 안됨), Cookie-Editor
      - 여기서는 Cookie-Editor를 설치해서 실습을 진행한다.
      - Cookie-Editor: https://chromewebstore.google.com/search/Cookie-Editor?hl=ko
    - 브라우저 개발자 도구
      - 대부분의 브라우저는 개발자 도구(DevTools)를 통해서 쿠키를 관리할 수 있다.
      - 브라우저에서 F12 > Application 탭 > Storage > Cookies 항목을 선택해서 관리한다.
      - 브라우저에서 F12 > 애플리케이션 탭 > 저장용량 > 쿠키 항목을 선택해서 관리한다.
    - 확장 프로그램 관리 > Cookie-Editor > 세부 정보 > 시크릿 모드에서 허용 > 활성화
   

proxy 툴 설치하기

점검툴: burp

Burp Suite 프록시(Proxy) 툴
  . 웹 애플리케이션 보안 테스트 시 클라이언트(브라우저)와 서버의 HHTTP(S) 요청과 응답을 가로채고 조작할 수 있는 프로그램
  . 종류
    - Community: 무료 버전
    - Professional: 유료 버전
    - Enterprise DAST(dynamic web vulnerability scanner): 유료 버전
  
  . 공식사이트 
    - https://portswigger.net/burp
    - 참고: https://cafe.naver.com/linuxmasternet/32043
    

1. burp 설치
다운로드 받고 설치한다.

2. burp 실행
검색 -> burp 로 찾아서 실행한다.
    
3. burp 설정
burp proxy 기본 port: 8080
8080: 톰캣, Oracle 웹 접속 포트
Proxy Port: 2030

포트가 겹치므로 이를 먼저 확인할 때 아래처럼 8080 포트가 사용중이면 이 포트는 사용할 수 없다.
C:\>netstat -na | findstr 8080
   
cmd 창에서 아래 명령어를 이용해서 2030 포트가 떠있는지 확인한다.
C:\> netstat -na | findstr 2030

 

[메뉴] > [proxy] > [Proxy settings]
[Proxy Listners]
127.0.0.1:8080 클릭 > [Edit] 
Bind to port: 2030  <-- 8080을 2030 포트로 변경한다.
Bind to address: Loopback only
Proxy Listeners Running 체크   
  
cmd 창에서 아래 명령어를 이용해서 2030 포트가 떠있는지 확인한다.
C:\> netstat -na | findstr 2030
  TCP    127.0.0.1:2030         0.0.0.0:0              LISTENING
  
Request/Response interception rules 체크

           Request interception rules 체크
           Request (웹데이터를 분석하거나 변조)
         ---------->     ---------->
[Client]           [Proxy]         [Server]
         <----------     <----------
           Response(웹데이터를 분석하거나 변조)
           Response interception rules 체크

Request interception rules: 
  . Client에서 Server로 보내는 웹 데이터를 분석할 때 사용한다. 
  . Intercept requests based on the following rules 부분을 체크한다.
    - 기본값은 체크가 되어 있다.

Response interception rules: 
  . Server에서 Client로 보내는 웹 데이터를 분석할 때 사용한다.  
  . Intercept responses based on following rules 부분을 체크한다.
    - 기본값은 체크가 안되어 있기 때문에 체크한다.
    
Burp Suite 한글이 잘 나올 수 있도록 아래 2개를 설정한다.
User interface
  . Message editor
    - HTTP message display
      Font: 굴림체 18pt <-- 한글폰트로 선택한다.
    - character sets

 

FoxyProxy 플러그인 설치하기

FoxyProxy 플러그인
  . 웹 브라우저(크롬, 파이어폭스 등)에서 사용하는 프록시 서버 전환 및 관리용 확장 프로그램이다.
  . 이 프로그램을 이용하면 브라우저에서 여러 프록시 서버를 관리하고 손쉽게 전환할 수 있다.
    - 브라우저에서 프록시 서버 설정을 반복해서 바꾸지 않아도 된다.
    - 특정 URL 패턴에 따라 자동으로 프록시를 사용할 수도 있다.
    - Burp Suite 같은 보안 분석 도구와 연동해 웹 트래픽을 빠르게 캡처한다.
  . 주요 기능
    - 프록시 프로파일 관리: 여러 개의 프록시 서버(IP, 포트, 프로토콜 등)를 등록이 가능하다.
    - 자동 프록시 전환: URL 또는 도메인 패턴별로 자동으로 프록시 설정이 적용된다.
    - 빠른 On/Off 스위치: 버튼 클릭 한 번으로 프록시 사용/비사용의 전환이 가능하다.
    - 다양한 프록시 지원: HTTP, HTTPS, SOCKS4, SOCKS5 등을 지원한다.
    - 색상 아이콘 및 상태표시: 현재 어떤 프록시가 사용 중인지 직관적으로 확인이 가능하다.

  . 설치 방법
    - 아래 크롬 웹 스토어에서 설치한다.
    - https://chromewebstore.google.com/detail/foxyproxy/gcknhkkoolaabfmlnjonogaaifnjlfnp


Proxy by patterns 
  . Burp Proxy가 설치, 세팅이 완료되어야 한다.
  . 특정 사이트를 등록하고 분석을 하고 싶을 때 사용한다.
  . 미리 등록을 해야 한다.
    - [브라우저] > [확장 프로그램] > [확장 프로그램 관리] > [세부 정보] > 
      [툴바에 고정] 선택, [시크릿 모드에서 허용] 을 선택한다.
    - [메뉴] > [Options] > [Proxies] > [Add] 클릭 > 
      Hostname: 127.0.0.1
      Port: 3128(Default Port) -> 2030 변경
      
      [+] 클릭 > [Pattern] 에 아래 사이트 주소를 등록한다.
      http://192.168.100.23/
      
      [Save] 버튼을 클릭한다. 
      브라우저 창을 닫는다. 
    - 브라우저에서 Proxy by patterns 를 클릭해서 활성화 한다.    

 

burp proxy 설명

Proxy 가 없는 상태
  . [Proxy] > [Intercept] > [Intercept off]
    - Request 에서 Server로 전송하는 데이터를 Proxy를 거치지 않고 보낸다.
    
           Request 
           웹데이터를 서버로 전송한다.
         -------------------------->
[Client]                           [Server]
         <--------------------------
           Response 
           서버에서 웹데이터가 클라이언트로 전송한다.

Proxy 가 있는 상태
  . [Proxy] > [Intercept] > [Intercept on]
    - Request 에서 Server로 전송하는 데이터를 Proxy를 거쳐서 보낸다.
    
           Request 
           웹데이터를 서버로 전송할 때 Proxy에 전달하고 Proxy는 받은 데이터를 서버로 전송한다.
         ---------->     ---------->
[Client]           [Proxy]         [Server]
         <----------     <----------
           Response
           서버에서 웹데이터가 Proxy로 전송하고 Proxy는 받은 데이터를 클라이언트로 전송한다.

쿠키와 세션의 개념
웹 개발에서 쿠키(Cookie) 와 세션(Session) 은 사용자의 상태와 정보를 유지하고 관리하기 위해 사용된다.
쿠키와 세션은 모두 웹 애플리케이션에서 사용자 식별과 상태 유지를 지원하지만 정보를 저장하는 위치와 동작 방식에 차이가 있다.

쿠키 
  . 쿠키 정보는 클라이언트에 정보가 저장된다.
    - 세션이 없었던 시절에는 쿠키를 사용했다.
    - 인증에 성공하면 쿠키로 관련 정보를 저장했다. 
    - 정보가 클라이언트에 저장되기 때문에 보안에 문제가 생기게 되었다.

  . 예전에는 로그인 아이디 정보가 저장되어 있지만 현재는 저장되지 않는다.
  . 쿠키는 클라이언트 측에서 저장되는 작은 데이터 조각으로 주로 사용자 식별 및 상태 유지를 위해 사용된다. 
  . 웹 서버는 쿠키를 생성하고 응답 헤더를 통해 클라이언트에게 전달하면 클라이언트는 해당 쿠키를 자신의 쿠키가 저장      되는 디렉터리에 저장한다. 
    - Set-Cookie: PHPSESSID=3i0q2q10esidbmq82bkukkb1h2; path=/
    - 서버 -> 클라이언트에게 전송하는 헤더 정보
      Set-Cookie: 쿠키를 세팅해! 라는 의미이다.
  . 나중에 동일한 도메인에 대한 요청을 할 때마다 해당 쿠키값(세션ID 저장)을 함께 HTTP 헤더에 붙여서 서버로 전송한다.
  
쿠키의 특징:
  . 클라이언트 측에 저장되므로 클라이언트에서 수정 가능하므로 보안에 주의를 해야한다.
  . 유효 기간을 설정하여 일정 기간 동안만 유지할 수 있다. 
  . 주로 팝업창이나 장바구니에서 활용된다.
  . 로그인에 필요한 중요한 정보는 쿠키에 저장하면 안된다.

세션 
  . 세션 정보는 서버에 정보가 저장된다.
  . 현재 
    - 예전의 쿠키의 문제를 보완해서 세션이 만들어졌다.
    - 인증에 성공하면 세션으로 관련 정보를 서버에 저장한다. 
    - 세션ID를 클라이언트에게 전송해서 클라이언트에서는 서버에서 전송된 세션ID를 쿠키로 저장한다. 
      - 세션ID에 해당하는 랜덤문자열은 로그인 할 때 마다 다르게 설정하는 것이 좋다.
      - 서버에서 헤더에 전송한 쿠키값: Set-Cookie: PHPSESSID=avstckc09gofgf1v9pc3t3khb2; path=/
      - Cookie-Editor를 이용해서 저장된 쿠키값을 확인할 수 있다. 
      - 개발자 도구에서도 저장된 쿠키값을 확인할 수 있다.
        개발자 도구(F12) > 애플리케이션 > 쿠키 > 도메인 or IP주소
  . 클라이언트는 서버에 접속을 할 때 마다 세션ID를 쿠키로 가지고 다니고 서버가 이를 확인해서 로그인한 사용자로 판단한다.
    - Cookie: PHPSESSID=avstckc09gofgf1v9pc3t3khb2
                          |
                         +-- 세션ID를 저장한 변수이며 /etc/php.ini 파일에 session.name = PHPSESSID 로 저장되어 있다.

  . 세션은 서버 측에서 사용자 정보를 관리하기 위한 메커니즘으로 서버의 파일시스템, 메모리, 데이터베이스에 정보를 저장한다. 
    - /etc/httpd/conf.d/php.conf: 
      php_value session.save_handler "files"
      php_value session.save_path  "/var/lib/php/session"
  . 사용자가 웹 애플리케이션에 처음 접속하면 서버는 고유한(랜덤) 세션 ID를 생성하여 클라이언트에게 응답헤더에 전달하고 
    이 세션 ID를 통해 서버는 해당 세션에 대한 정보를 관리한다.
    - php 소스코드에서 세션을 사용하기 위해서는 첫 줄에 session_start(); 함수를 반드시 호출해야 한다.
    - 만약 session_start(); 함수가 없으면 php 에서 제공되는 세션을 사용할 수 없다.

세션의 특징:
  . 세션은 클라이언트 측에 저장되지 않으므로 쿠키보다 보안상 더 안전하다.
    - /var/lib/php/session 디렉터리의 권한
      drwxrwx---. 2 root apache 4096  6월 10 14:14 /var/lib/php/session
            |  +-- 일반유저는 접근 금지
            +-- apache 사용자는 접근 허용
    - 만약 nginx 웹서버를 사용한다면 반드시 디렉터리 권한을 nginx 그룹에 해당하는 사용자에게 rwx 권한을 줘야 한다.

  # grep apache /etc/{passwd,group}
    /etc/passwd:apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
    /etc/group:apache:x:48:

 

 # ps aux |grep [h]ttpd
    root       1035  0.0  0.8 433140 14936 ?        Ss   08:57   0:01 /usr/sbin/httpd -DFOREGROUND
    apache     1322  0.0  0.5 433404  9500 ?        S    08:57   0:00 /usr/sbin/httpd -DFOREGROUND
# rm -f /var/lib/php/session/*

http://192.168.100.23/php/login.php 새로 접속하면 0byte 짜리 세션파일이 생성된다.

 # ll /var/lib/php/session/*
    -rw-------. 1 apache apache 0  6월 10 14:34 /var/lib/php/session/sess_91hkesd37bap2hjnd9jaie77k2
                                |
                                +-- 0byte 라는 것은 세션 파일만 생성하고 내용물은 아직 저장되지 않았다.

  . 서버 측에 저장되므로 더 많은 데이터를 저장할 수 있지만 서버 자원을 사용한다는 점에 유의해야 한다.
  . 세션의 유효 기간은 서버에서 제어 가능하며 일반적으로 클라이언트의 비활동 시간 동안 유지되며 설정에 따라 만료될 수 있다.
    - 프로그래머가 프로그램을 짤 때 만료시간을 설정했을 때를 말한다.
  . 중요한 정보는 세션에 저장하기 때문에 클라이언트에서는 확인할 수 없다.
    - 서버에 저장된 세션파일의 내용은 서버를 해킹하지 않는 한 알 수 없다.
  . 쿠키와 세션은 보통 함께 사용되며 쿠키에 세션 ID를 저장하여 세션을 식별하는 방식으로 사용자 상태를 관리한다. 
    - 서버의 세션파일의 랜덤문자열과 클라이언트의 Cookie Editor의 PHPSESSID의 값이 동일한지 확인한다.
  . 사용자가 로그인하면 세션을 생성하고 세션 ID를 쿠키로 클라이언트에게 전달하여 이후 요청에서 세션 ID를 통해 사용자 정보를 참조한다. 
  . 이를 통해 로그인 상태를 유지하고 사용자를 식별하는 등을 처리할 수 있다.

서버에서 PHP를 이용한 세션 글로별 변수
  . PHP에서 슈퍼 글로벌 변수인 $_SESSION 을 이용해서 세션 데이터에 접근할 수 있다. 
    - $_GET : GET 방식으로 넘어온 변수를 처리하기 위한 슈퍼 글로벌 변수
    - $_POST: POST 방식으로 넘어온 변수를 처리하기 위한 슈퍼 글로벌 변수    
    - $_FILES: 파일 업로드를 처리하기 위한 슈퍼 글로벌 변수    
    - $_SESSION: 세션을 처리하기 위한 슈퍼 글로벌 변수    
  . $_SESSION을 사용하려면 PHP 스크립트 파일 처음에 session_start() 함수를 호출하여 세션을 초기화한다.
    - PHP 파일에  session_start(); 함수가 없으면 세션을 처리할 수 없다.
  . 이후에 $_SESSION 배열을 사용하여 세션 데이터를 읽거나 쓸 수 있다.

서버에서 PHP 세션이 저장된 디렉터리와 파일 형식
  . 세션 디렉터리 위치: /var/lib/php/session/ 
    - 기본값이고 얼마든지 변경이 가능하고 서버의 설정파일, 애플리케이션에서 변경할 수 있다.
                                       |        |
                                       |        +-- 일반 유저는 함수(session_save_path())를 이용해서 변경이 가능하다.
                                       +-- root만 변경이 가능하다.     
      - e.g.) session_save_path('/path/to/session/directory'); // 세션 저장 경로 변경                                       
    - /etc/httpd/conf.d/php.conf: php_value session.save_path  "/var/lib/php/session"

 $ ll -d /var/lib/php/session/
    drwxrwx---. 2 root apache 84 10월 20 15:38 /var/lib/php/session/
            |
            +-- 일반 유저는 접근할 수 없다.

  . php 세션 파일 형식: sess_랜덤문자열
    - 기본값이고 얼마든지 변경이 가능하지만 변경하는 일은 별로 없다.
  . yum, dnf 로 PHP를 설치하면 Default 값이고 이 디렉터리의 위치는 설정파일과 변수에서 변경이 가능하다.
  . APM을 소스로 설치하면 /tmp 디렉터리가 세션 디렉터리로 쓰이기도 한다.
    - 실무에서는 APM을 소스로 설치하기도 한다.
    - 소스로 설치하면 유지보수가 힘들다.
  . 웹애플리케이션들은 자신의 앱 디렉터리 밑에 특정 세션디렉터리를 개별적으로 만들어서 사용하기도 한다.

클라이언트가 서버의 특정 파일(session_start() 함수가 있는 페이지)을 요청할 경우
  . 1. 클라이언트: 쿠키값이 없을 때는 Cookie: PHPSESSID=랜덤문자열 부분이 존재하지 않고 서버로 웹데이터를 전송한다.
  . 2. 서버: 서버는 session_start() 함수에 의해서 세션의 랜덤문자열이 만들어지고 
       세션이 저장되는 디렉터리(/var/lib/php/session)에 sess_랜덤문자열 파일을 생성한다.
  . 3. 서버: 클라이언트에게 랜덤문자열을 아래처럼 HTTP 헤더에 저장하고 클라이언트에게 전송한다.
       Set-Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7; path=/
  . 4. 클라이언트: 클라이언트는 서버가 보내준 랜덤문자열을 받아서 쿠키 디렉터리에 쿠키로 저장한다. 
       Cookie-Editor 로 확인하면 아래처럼 저장된 것을 확인할 수 있다.
       PHPSESSID=3kp8k3vei82mshqtjnogd7nna7; path=/
                     |
                     +-- 랜덤이므로 값이 다르다.
  . 5. 클라이언트: 클라이언트가 서버의 특정 페이지로 다시 접속할 때 서버가 보내준 랜덤문자열(세션파일명의 랜덤문자열)을 쿠키로 
       저장했기 때문에 HTTP Request 헤더에 Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7  필드에 담아서 서버로 보낸다.
  . 6. 서버: 서버는 클라이언트가 보내준 쿠키값을 세션디렉터리에서 랜덤 문자열로 된 파일을 확인하고 그 안에 내용을 확인한다.

PHP에서 세션을 다루기 위해 사용되는 세션 관련 함수  
  . 세션 관련 함수들은 세션 데이터를 저장, 읽기, 수정, 삭제 등을 수행하는 데 사용된다.
  . session_start() 
    - 세션을 시작하고 세션 데이터를 사용할 수 있도록 한다. 세션 데이터를 사용하기 전에 반드시 먼저 호출되어야 한다.
    - 만약 소스코드에 session_start() 함수를 호출하지 않으면 세션을 사용할 수 없다. 
    e.g.) session_start();

  . $_SESSION['변수명']: 
    - 세션 데이터를 다루는데 사용되는 슈퍼 글로벌 변수이다. 연관 배열 형태로 데이터를 저장하고 읽을 수 있다.
    e.g.) 
    $_SESSION['userid'] = 'admin';    // 세션 데이터 쓰기(저장)
    $username = $_SESSION['username']; // 세션 데이터 읽기


로그인/로그아웃 분석하기

전체 파일에 대한 설명
  . 로그인 페이지
    - login.php
    - post 방식으로 userid/userpw 변수에 사용자가 입력한 데이터를 저장하고 loginok.php로 전송한다.
  . 로그인 인증 페이지 (DB 연동 부분, 객체지향 스타일로 작성)
    - loginok.php 
    - 전송받은 userid/userpw 를 DB에 저장되어 있는 데이터와 비교해서 맞는지 확인한다.
    - userid/userpw 가 맞으면 
      세션변수 $_SESSION['userid']/$_SESSION['username'] 변수를 생성해서 세션 변수를 
      세션 파일에 저장한 후 login.php로 다시 돌려보낸다.
      세션이 있으면 사용자 정보를 출력한다.
    - userid/userpw 가 틀리면
      userid/userpw 가 틀렸습니다. 라는 에러메세지를 출력하고 login.php로 다시 돌려보낸다.
  . 로그아웃 페이지
    - logout.php
    - 사용자가 로그아웃을 클릭하면 logout.php 로 가서 세션 파일을 모두 삭제한다.
    - login.php로 돌려보낸다.
    - 세션이 없으면 로그인 페이지를 출력한다.

1. DB 설계
DB: test
TB: mymember

# mysql test
MariaDB [(none)]> create database test;
MariaDB [(none)]> use test

CREATE TABLE mymember (
    no       int         NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '회원번호',
    userid   varchar(20) NOT NULL UNIQUE COMMENT '사용자',
    userpw   varchar(41) NOT NULL COMMENT '비밀번호',
    username varchar(20) NOT NULL COMMENT '회원이름',
    regdate  datetime    NOT NULL COMMENT '가입날짜',
    phone    varchar(20) COMMENT '연락처', 
    email    varchar(50) COMMENT '이메일'
);

MariaDB [test]> desc mymember; 

INSERT INTO mymember VALUES (NULL, 'admin',  password('111111'), 'admin', now(), '010-1111-2222', 'admin@server.com');
INSERT INTO mymember VALUES (NULL, 'mrhong', password('222222'), 'honggd', now(), '010-2222-3333', 'mrhong@naver.com');
SELECT * FROM mymember;

MariaDB [test]> SELECT * FROM mymember;

MariaDB [test]> SELECT * FROM mymember\G

 

<?php
// login.php
// 로그인 메인 파일

session_start(); // 세션 시작
?>
<!DOCTYPE html>
<html>
<head>
  <title> ::: 로그인 페이지 ::: </title>
  <meta charset="utf-8"/>

    <style>
        table { margin-left:auto; margin-right:auto; width: 300px; border: 1px solid #444444; border-collapse: collapse; }
        th, td { border: 1px solid #444444; padding: 10px; height:30px; font-size:12pt; }
        input[type=text],[type=password] { width: 130px; height:25px ; font-size:12pt; }
        input[type=submit] { width:100px; height:30px; font-size:12pt; }
    </style>

</head>
<body>

<?php
if(isset($_SESSION['userid']))
{
?>

<table>
<tr align="center">
  <td align="center"> <?=$_SESSION['username'] ?> 님 환영합니다. </td>
</tr>
<tr align="center">
  <td align="center"> <a href="logout.php"> 로그아웃 </a> </td>
</tr>
</table>

<?php
} else {
?>

<script>
function loginCheck()
{
    if(document.login.userid.value == '') {
        alert('아이디를 입력해야 합니다.');
        document.login.userid.focus();
        return false;
    }

    if(document.login.userpw.value == '') {
        alert('비밀번호를 입력해야 합니다.');
        document.login.userpw.focus();
        return false;
    }

    return true;
}
</script>

<form name="login" method="post" action="loginok.php" onSubmit="return loginCheck();">
<table>
<tr align="center">
  <td> 아이디 </td> <td> <input type="text" name="userid"> </td>
<tr>
<tr align="center">
  <td> 비밀번호 </td> <td> <input type="password" name="userpw"> </td>
<tr>
<tr align="center">
  <td align="center" colspan=2> <input type="submit" value="로그인"> </td>
<tr>
</table>
</form>

<?php
}
?>

</body>
</html>
<?php
// loginok.php
// DB 사용자 인증 (절차지향 스타일)

session_start();  // 세션 시작

// 디버깅
//var_dump($_POST);
//print_r($_POST);


// DB 접속
define("DBAPP", true);
include "__dbConnect1_inc.php";

$TBNAME = "mymember";
$userid = $_POST['userid'];
$userpw = $_POST['userpw'];
$query = "SELECT * FROM $TBNAME WHERE userid = '$userid' and userpw = password('$userpw') ";
$result = mysqli_query($conn, $query);

// 행의 개수를 구했을 때
// 전체 자료 수
//   . 1이면 userid / userpw 가 일치
//   . 0이면 userid / userpw 가 불일치
$num = mysqli_num_rows($result);

// 디버깅
//echo $num . "<br>";
//echo $query . "<br>";

if($num) {  // if(1)
    // 세션 변수 저장
    // Default Directory: /var/lib/php/session/
    // Session File:
    //   . Random 으로 생성된다.
    //   . sess_랜덤문자열
    //     - sess_mrnfjqcid2j716lno6p293m1b1
    $row = mysqli_fetch_array($result);  // $row 변수에 연관 배열로 저장한다.
    // 세션 파일에 저장하는 형식
    //   . $_SESSION['변수명'] = 값;
    //     - 변수명은 사용자가 마음대로 설정한다.
    //     - 값도 마음대로 설정한다.
    $_SESSION['userid']   = $row['userid'];   // DB에 저장된 값을 세션변수 userid에 저장한다.
    $_SESSION['username'] = $row['username']; // DB에 저장된 값을 세션변수 username에 저장한다.
} else {  // if(0)
    echo "<script>alert('아이디/비밀번호를 확인해주세요.');</script>";
}

echo "<script>location.href='login.php';</script>";

mysqli_free_result($result);
mysqli_close($conn);

?>
<?php
// __dbConnect1_inc.php
// DB 접속 (절차지향 프로그래밍 스타일)
// 직접 실행 금지
if(!defined('DBAPP')) {
    exit('잘못된 접근입니다.');
}

$dbhost = "localhost";
$dbuser = "root";
$dbpass = "1234";
$dbname = "test";

$conn = @mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
if($conn  === false) {
    die("DB 연결실패!");
}
?>

__dbConnect1_inc.php 
  . 파일에 직접 접근하면 
    - 상수 DBAPP이 없기 때문에 exit()함수가 실행된다.
    - 더이상 스크립트가 진행되지 않고 종료한다.

  . 파일에 직접 접근하지 않으면 
    - define("DBAPP", true); 정의되어 있고
    - 상수 DBAPP이 있기 때문에 if문 안으로 들어가지 않으므로 exit()함수가 실행되지 않는다.
    - 스크립트는 끝까지 실행된다.
    
DBA 비밀번호를 1234로 수정한다.
# mysqladmin -p password 1234
Enter password:1234
<?php
// logout.php
// 로그아웃
//   . 세션 파일 삭제
//     - 세션 파일을 삭제한다.
//     - 세션 파일을 삭제하고 놔두면 세션 파일이 다시 생성되는데
//       이때 생성되는 세션 파일은 이전의 파일명과 동일하다.
//       그러므로 세션 파일을 재사용하는 공격에 활용된다.
//   . 세션 변수 삭제
//     - 세션 변수를 삭제하면 세션 파일을 재사용하는 공격에 활용된다.

session_start();    // 세션 시작
session_destroy();  // 세션 삭제

// login.php로 리다이렉션
header("Location: login.php");
?>

 


3. 로그인 하기
login.php로 접속해서 사용자로 로그인한다.

Burp 에서 [Intercept off] 로 변경하고 접속한다.

http://192.168.100.23/php/login.php

아이디: admin, 비밀번호: 111111 로 로그인하기

로그인이 성공되면 아래처럼 로그인 페이지가 출력된다.

      admin님 환영합니다.
          로그아웃

세션 변수를 생성해서 세션파일에 저장하고 login.php 파일로 리다이렉션 한다.


4. 로그아웃 하기
로그아웃을 클릭해서 로그아웃이 잘 되는지 확인한다.

      admin님 환영합니다.
          로그아웃     <-- 클릭
          
세션파일을 삭제하고 login.php 파일로 리다이렉션 한다.    
login.php 파일의 session_start(); 함수에 의해서 세션 파일이 다시 생성된다.
이 부분에서 세션 파일을 재사용하는 취약점이 발생한다.

 

동적 쿼리 확인하기

보안 설계 적용 계획
애플리케이션을 설계할 때 접속하는 DBMS 계정 권한
은 최소한으로 설정해야 하지만 여기서는 root로 설정했다.
여기서는 root로 설정했으므로 취약하다.
외부 입력값이 SQL문으로 구성될 수 있는 동적 쿼리(Dynamic SQL)를 사용하지 않는다.

-- loginok.php --

$query = "SELECT * FROM $TBNAME WHERE userid = '$userid' and userpw = password('$userpw') ";

=> ' or 1=1# =>

SELECT * FROM mymember WHERE userid = ' ' or 1=1#' and userpw = password('1') <br> <= 주석 처리됨

즉 ' or 1=1#의 의미는 앞의 조건이 무엇이든 상관없이(or 1=1), 원래 뒷부분은 무시하고(#), 그 결과 중 가장 첫 번째 행(Row)의 정보로 로그인을 해라 이다

 

데이터 베이스에서 확인해보면 

MariaDB [test]> SELECT * FROM mymember WHERE userid = ' ' or 1=1#' and userpw = password('1');

+----+--------+-------------------------------------------+----------+---------------------+---------------+------------------+
| no | userid | userpw                                    | username | regdate             | phone         | email            |
+----+--------+-------------------------------------------+----------+---------------------+---------------+------------------+
|  1 | admin  | *FD571203974BA9AFE270FE62151AE967ECA5E0AA | admin    | 2026-05-11 17:05:44 | 010-1111-2222 | admin@server.com |
|  2 | mrhong | *A0C1808B1A47CECD5C161FEE647F5427F4EB6F98 | honggd   | 2026-05-11 17:05:44 | 010-2222-3333 | mrhong@naver.com |
+----+--------+-------------------------------------------+----------+---------------------+---------------+------------------+
2 rows in set (0.001 sec)

패스워드가 없어도 조건문의 취약점에 의해 $num = mysqli_num_rows($result);가 실행된다

 

SQLi 으로 mrhong 사용자로 로그인하기

' or 1=1 limit 1,1#

limit : SELECT 쿼리 결과에서 반환할 행의 수를 제한하는 데 사용되며, 주로 대량의 데이터를 페이징 처리하거나

상위 (N)개의 데이터만 조회할 때 사용한다

형식

LIMIT [가져올 개수] 또는 LIMIT [건너뛸 행 수, 가져올 개수]

limit 1,1 <= 1행 건너뛰고 1개를 가져옴 

=> admin 건너뛰고 mrhong 을 가져와서 mrhong 으로 로그인이 된다