애플리케이션 보안 운영 - 1 (교육 87일차)

SW·2023년 4월 1일
0

실습> CentOS 7에서 APM 환경 구성하기

APM: Apache + Php + MySQL, MariaDB

APM 환경 구성하기 참고 : https://cafe.naver.com/linuxmasternet/1406
그누보드 참고 : https://cafe.naver.com/linuxmasternet/60

WEB Server : Apache 웹서버 (httpd)
WAS : Php 프로그래밍 언어 (php)
DB Server : MariaDB 데이터베이스 서버 (mariadb-server), 클라이언트 (mariadb)
Php 와 MariaDB 연동 : (php-mysql)

Apache 설정 파일: /etc/httpd/conf/httpd.conf
Php 설정 파일: /etc/php.ini
MariaDB 설정 파일: /etc/my.cnf

1. 네트워크 환경
VMname: WebHacking
VMnet8: 200.200.200.0/24
IP주소: 200.200.200.3/24
게이트웨이: 200.200.200.2
DNS 서버: 168.126.63.1
호스트명: webhacking.linuxmaster.net

2. 방화벽 설정
# firewall-cmd --permanent --add-service=http
# firewall-cmd --permanent --add-service=https
# firewall-cmd --reload
# firewall-cmd --list-all

3. APM 설치
서버에 APM 환경을 구성한다.
# yum -y install httpd php php-mysql mariadb mariadb-server

서비스를 활성화 시킨다.
서비스를 활성화 하면 서버가 재부팅해도 서비스가 자동으로 시작된다.
# systemctl enable httpd
# systemctl enable mariadb

GD 라이브러리를 설치한다.
GD 라이브러리는 서버를 설치하는데 있어서 무조건 필수라고 생각하면 된다.
# yum -y install php-gd gd gd-devel libjpeg libjpeg-devel giflib giflib-devel libpng libpng-devel freetype freetype-devel

웹서버 설정파일 수정
파일의 확장자가 .php, .html이면 php로 인식하는 설정을 한다.
# vi /etc/httpd/conf.d/php.conf 
<FilesMatch \.(php|html)$>
    SetHandler application/x-httpd-php
</FilesMatch>

DirectoryIndex index.php index.html

PHP 설정파일 수정
php 설정파일을 수정한다.
date.timezone : 시간대 설정
short_open_tag : <?php -> <?
expose_php : 연동된 정보 출력 여부
display_errors : 개발용 On, 운영용 Off
개발용 On : PHP 코드가 에러가 발생되면 브라우저 화면에 에러가 출력된다.
운영용 Off : /var/log/httpd 로그 디렉터리에 파일로 기록된다.
# vi /etc/php.ini 
[Date]
date.timezone = Asia/Seoul
short_open_tag = On
expose_php = Off
display_errors = On

php 설정이 완료되면 웹서버를  재시작한다.
- 웹서버가 시작되지 않았다면 start 한다.
- 웹서버 설정파일을 수정했다면 restart 한다.
- PHP 설정파일을 수정했다면 restart 한다. 
- ss 명령어를 사용하거나 net-tools(netstat)를 설치해서 웹서버의 열린 포트를 확인한다.
# systemctl restart httpd
# yum -y install net-tools
# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      981/sshd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1142/master
tcp6       0      0 :::22                   :::*                    LISTEN      981/sshd
tcp6       0      0 ::1:25                  :::*                    LISTEN      1142/master
tcp6       0      0 :::80                   :::*                    LISTEN      2233/httpd

4. 웹 문서작성
/var/www/html : 웹 문서가 제공되는 디렉터리
웹 문서를 /var/www/html 디렉터리에 만들어서 연동이 잘 되었는지 확인한다.
# cd /var/www/html
# vi index.html
<!--
파일명 : index.html
프로그램 설명 : 웹 기본 문서
작성자 : 리눅스마스터넷
홈페이지 : http://cafe.linuxmaster.net
-->
<html>
<head>
 <title> test server </title>
 <meta charset="utf-8">
</head>

<body>
<?
phpinfo();
?>
</body>
</html>

5. 웹서버 접속
http://200.200.200.3 으로 접속해서 PHP 연동 페이지가 보이는지 확인한다.
연동 실패: PHP INFO 정보 안보인다.
연동 성공: PHP INFO 정보 보인다.

phpinfo(); 호출의 의미
- APM 연동 내용을 확인
- 아파치 환경변수들을 확인
- PHP 환경변수들을 확인
- PHP 모듈 확인

6. MariaDB 설정
MariaDB의 설정 파일을 수정한다.
MariaDB의 언어셋을 UTF8로 설정한다.
한글 1글자 3byte의 크기를 가진다. DB를 UTF8로 설정하지 않으면 한글이 깨진다.
symbolic-links=0 밑에 4개의 설정을 추가한다.
# cd
# vi /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
collation-server=utf8_general_ci
character-set-server=utf8
skip-character-set-client-handshake
bind-address=127.0.0.1

[mysqld_safe]
  :
  :(생략)


설정 파일을 수정 후 mariadb를 시작한다.
# systemctl start mariadb
# systemctl status mariadb

mariadb가 잘 실행되었는지 열린 포트를 확인한다.
# ss -nltp
# netstat -nltp

mariadb에 접속해서 언어셋이 utf8로 잘 설정되었는지 확인하고 mariadb 접속을 해제한다.
mysql : DBMS(mariadb)에 접속할 수 있는 클라이언트 프로그램 

# mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> \s
--------------
mysql  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:		2
Current database:	
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server:			MariaDB
Server version:		5.5.68-MariaDB MariaDB Server
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8  <-- 언어셋 확인
Db     characterset:	utf8  <-- 언어셋 확인
Client characterset:	utf8  <-- 언어셋 확인
Conn.  characterset:	utf8  <-- 언어셋 확인
UNIX socket:		/var/lib/mysql/mysql.sock
Uptime:			8 min 59 sec

Threads: 1  Questions: 5  Slow queries: 0  Opens: 0  Flush tables: 2  Open tables: 26  Queries per second avg: 0.009
--------------

MariaDB [(none)]> exit

DB 관리자 비밀번호 설정
mariadb를 설치하면 DB 관리자(root)의 비밀번호가 없다.
그러므로 사용하기 위해서는 반드시 비밀번호를 설정한다.
mysqladmin : 여러가지 역할을 하는 관리자용 프로그램
형식 : Usage: mysqladmin [OPTIONS] command command....
mysqladmin -h 호스트명 -u 사용자 -p비밀번호 password

# mysqladmin --help
mysqladmin  Ver 9.0 Distrib 5.5.68-MariaDB, for Linux on x86_64
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Administration program for the mysqld daemon.
Usage: mysqladmin [OPTIONS] command command....

  :
  :(생략)
-h, --host=name     Connect to host.
-u, --user=name     User for login if not current user.
-p, --password[=name]
password [new-password] Change old password to new-password in current format
  :
  :(생략)

관리자(DB root)의 비밀번호를 P@ssw0rd로 변경한다.
-h localhost : 생략 가능 (클라이언트와 서버가 같은 호스트에 존재하면)
-u root : 생략 가능 (시스템 계정명과 DBMS 로그인 계정명이 동일하면)
# mysqladmin -h localhost -u root -p password <-- mysqladmin -p password
Enter password:        <-- 현재 비밀번호 (엔터) 
New password:          <-- 새로운 비밀번호 (P@ssw0rd)
Confirm new password:  <-- 새로운 비밀번호 (P@ssw0rd)


mysql 명령어로 DBMS(mariadb)로 접속한다.

Usage: mysql [OPTIONS] [database]

-h localhost : 생략 가능 (클라이언트와 서버가 같은 호스트에 존재하면)
-u root : 생략 가능 (시스템 계정명과 DBMS 로그인 계정명이 동일하면)

비밀번호 설정이 완료되면 root 권한으로 DBMS에 접속한다.
# mysql -p
Enter password:  <-- P@ssw0rd 입력
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 6
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> quit


DB서버 자동 접속 설정하기
자신의 홈디렉터리에 .my.cnf 파일을 아래처럼 만들어 놓으면 호스트명, 사용자, 비밀번호
입력을 생략하고 DBMS(mariadb) 서버에 자동으로 접속할 수 있다.
-- $HOME/.my.cnf --
[client]
host = localhost     <-- -h 
user = root          <-- -u
password = P@ssw0rd  <-- -p
-- $HOME/.my.cnf --

# pwd
/root

# vi .my.cnf
[client]
host = localhost
user = root
password = P@ssw0rd

설정파일이 저장되면 허가권(Permission)을 변경한다.
# chmod 600 .my.cnf
# ls -l .my.cnf
-rw-------. 1 root root 58  6월 24 20:08 .my.cnf

# mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 10
Server version: 5.5.68-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> select user();
+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

MariaDB [(none)]> exit


DBMS(mariadb) mysql DB로 접속해서 사용자/DB에 대한 불필요한 설정들을 삭제한다.
# mysql mysql

DB의 목록을 확인한다.
MariaDB [mysql]> show databases;

mysql DB에 있는 Table의 목록을 확인한다. 
MariaDB [mysql]> show tables;

user 테이블 : 사용자 목록이 저장된 테이블
db 테이블 : 사용자 DB에 권한이 저장된 테이블

user 테이블에서 host,user,password 컬럼만 출력한다.
MariaDB [mysql]> select host,user,password from user;
+-----------------------+------+-------------------------------------------+
| host                  | user | password                                  |
+-----------------------+------+-------------------------------------------+
| localhost             | root | *8232A1298A49F710DBEE0B330C42EEC825D4190A |
| localhost.localdomain | root |                                           | <-- 삭제
| 127.0.0.1             | root |                                           | <-- 삭제
| ::1                   | root |                                           | <-- 삭제
| localhost             |      |                                           | <-- 삭제
| localhost.localdomain |      |                                           | <-- 삭제
+-----------------------+------+-------------------------------------------+
6 rows in set (0.00 sec)

db 테이블에서 host,user,db 컬럼만 출력한다.
MariaDB [mysql]> select host,user,db from db;
+------+------+---------+
| host | user | db      |
+------+------+---------+
| %    |      | test    |  <-- 삭제
| %    |      | test\_% |  <-- 삭제
+------+------+---------+
2 rows in set (0.00 sec)


user 테이블의 비밀번호가 없는 레코드를 삭제한다.
MariaDB [mysql]> delete from user where password = '';
Query OK, 5 rows affected (0.00 sec)

MariaDB [mysql]> select host,user,password from user;
+-----------+------+-------------------------------------------+
| host      | user | password                                  |
+-----------+------+-------------------------------------------+
| localhost | root | *8232A1298A49F710DBEE0B330C42EEC825D4190A |
+-----------+------+-------------------------------------------+
1 row in set (0.00 sec)

db 테이블의 모든 레코드를 삭제한다.
MariaDB [mysql]> delete from db;
Query OK, 2 rows affected (0.00 sec)

MariaDB [mysql]> select host,user,db from db;
Empty set (0.00 sec)

권한을 다시 읽어들인다.
MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit

실습> 웹사이트 설정하기

1. 파일 업로드
webhacking1.tar.gz 웹서버에 업로드해서 웹해킹을 실습한다.

2. 웹 파일 설정 
[root@webhacking ~]# mv webhacking1.tar.gz /var/www/html/
[root@webhacking ~]# cd /var/www/html
[root@webhacking html]# rm -f index.html
[root@webhacking html]# tar xzf webhacking1.tar.gz 
[root@webhacking html]# mv public_html/* .
[root@webhacking html]# rm -rf public_html

3. DB 설정
[root@webhacking html]# mysql

CREATE DATABASE WebTest;
CREATE USER webadmin@localhost IDENTIFIED BY 'P@ssw0rd';
GRANT ALL PRIVILEGES ON WebTest.* TO webadmin@localhost;   
USE WebTest
CREATE TABLE member (
    no       int NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '번호',
    u_id     varchar(20) NOT NULL UNIQUE COMMENT '회원아이디',
    u_pass   varchar(50) NOT NULL COMMENT '회원비밀번호',
    u_name   varchar(20) NOT NULL COMMENT '회원명',
    nickname char(20) COMMENT '닉네임', 
    age      int COMMENT '나이',
    email    char(50) COMMENT '이메일',
    reg_date datetime NOT NULL COMMENT '가입날짜'
);

CREATE TABLE board(
    strNumber   int NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '번호',
    strName     varchar(20) NOT NULL COMMENT '글쓴이',
    strPassword varchar(20) NOT NULL COMMENT '글비번',
    strEmail    varchar(50) COMMENT '이메일',
    strSubject  varchar(100) COMMENT '글제목',
    strContent  text NOT NULL COMMENT '글내용',
    htmlTag     char(1) NOT NULL COMMENT 'html사용여부',
    viewCount   int NOT NULL DEFAULT 0 COMMENT '조회수',
    filename    varchar(50) COMMENT '파일명', 
    filesize    int COMMENT '파일크기',
    writeDate   datetime COMMENT '글쓴날짜'
);

INSERT INTO member VALUES('', 'tester', 'tester', '테스터', '테스트계정', '','',now());
INSERT INTO member VALUES('', 'admin', 'P@ssw0rd', '관리자', '관리자', '','',now());

SHOW TABLES;
DESC board;
DESC member;
SELECT * FROM member;

4. 사이트 접속
http://200.200.200.3/

tester, admin 사용자로 로그인해서 로그인이 잘되는지 확인한다.






프록시의 역할
Client가 Resquest 하는 웹 데이터를 중간에 가로채서 Server로 전송하는 역할을 한다.
Server가 Response 하는 웹 데이터를 중간에 가로채서 Client로 전송하는 역할을 한다.
이를 통해 Client와 Server간에 지나다니는 웹 데이터를 가로채서 분석, 조작을 통해서 
웹서버에서 동작하는 웹 애플리케이션의 취약점 찾는데 활용할 수 있다.

프록시의 위치
프록시는 Client와 Server의 중간에 위치한 상태에 놓이게 된다.

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

실습> JRE 설치

JRE: Java Runtime Environment
Java 언어로 만들어진 프로그램을 실행하기 위한 환경을 제공하는 프로그램

JRE 설치
https://java.com/ko/download/

참고: https://cafe.naver.com/linuxmasternet/1708

실습> proxy 툴 설치하기

점검툴: burp
공식사이트: https://portswigger.net/burp

Community : 무료 버전
Professional : 유료 버전
Enterprise : 유료 버전

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

2. burp 실행
검색 -> burp 로 찾아서 실행한다.

3. burp 설정
burp proxy 기본 port: 9090
9090: 톰캣, Oracle 웹 접속 포트

포트가 겹치므로 이를 먼저 확인한다.
검색: cmd -> netstat -na | findstr 9090
9090 포트가 없으면 그대로 진행하는 것이고 있으면 burp의 포트를 9090 이 아닌 다른 포트로 수정한다.

메뉴 -> proxy -> Options
Edit -> 127.0.0.1 9090
Proxy Listeners Running     체크 

Intercept Client Requests 체크: Client에서 보낸 웹 데이터를 분석할 때 사용한다.
Intercept Server Responses 체크: Server에서 보낸 웹 데이터를 분석할 때 사용한다.

Burp Suite 한글이 잘 나올 수 있도록 아래 2개를 설정한다.
메뉴 -> User options -> Display 
HTTP Message Display : 한글 폰트 변경 (D2Coding)
Character Sets : UTF-8 인코딩 변경

4. switchyomega 설치하기
크롬에서 proxy On/Off 설정하는 플러그인
https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif

크롬 시크릿창에서 프록시를 사용하는 방법
- 기본값은 Off -> On
크롬브라우저 메뉴 -> 설정 -> 확장 프로그램
세부정보 -> 시크릿 모드에서 허용 

실습> burp 사용하기


             Direct
    +-----------------------+
    |                       |
    V                       |
+-------+      +-------+    |  +-------+
|       |Proxy |       |    +->|       | 
|       |<---->|       |<----->|       | 
|       |      |       |       |       | 
+-------+      +-------+       +-------+
   .1            Proxy             .3
            200.200.200.0/24

switchyomega 메뉴
Direct: Proxy를 통해서 접근하지 않고 직접 접속한다.
System Proxy: 웹 데이터를 가로채지 않는다.
Proxy: 웹 데이터를 가로챈다. (burp와 같이 설정해야 함.)

1. switchyomega 고정시키기
크롬의 오른쪽 위에서 고정핀으로 고정시킨다.

2. 웹페이지 접속
200.200.200.3 서버에 접속한다.
http://200.200.200.3/

3. Proxy 선택
proxy를 선택하면 아이콘 색이 바뀐다.
이 상태가 되면 burp에서 Client와 Server간의 웹 데이터를 가로챌 수 있다.
가로채는 이유는 모의해커가 웹 데이터에서 취약점의 여부를 분석하기 위해서 사용한다.

4. Intercept 체크 
메뉴 -> proxy -> Options
Edit -> 127.0.0.1 9090
Proxy Listeners Running     체크 
Intercept Client Requests 체크: Client에서 보낸 웹 데이터를 분석할 때 사용한다.
Intercept Server Responses 체크: Server에서 보낸 웹 데이터를 분석할 때 사용한다.

5. burp 설정
burp에서 Client와 Server간의 웹 데이터를 가로채는 설정을 한다.
메뉴 -> proxy -> intercept -> intercept is on 버튼을 클릭한다.

Intercept is on  : 
- Request:  Client -> Server 로 전송하는 웹 데이터를 가로챈다. (분석 o, 조작 o)
- Response: Server -> Client 로 전송하는 웹 데이터를 가로챈다. (분석 o, 조작 o) 
Intercept is off : 
- Request:  Client -> Server 로 전송하는 웹 데이터를 가로채지 않고 서버로 전송한다.     (분석 x, 조작 x)
- Response: Server -> Client 로 전송한 웹 데이터를 가로채지 않고 클라이언트로 전송한다. (분석 x, 조작 x)
Forward : 
- Request: Client -> Server 로 전송하는 웹 데이터를 가로채고 분석, 조작을 한 후 서버로 전송한다.
- Response: Server -> Client 로 웹 데이터를 가로채고 분석, 조작을 한 후 클라이언로 전송한다.
Drop : 
- Request:  Client -> Server 로 전송하는 웹 데이터를 가로채고 삭제한다. (서버로 전송하지 않는다.)
- Response: Server -> Client 로 전송하는 웹 데이터를 가로채고 삭제한다. (클라이언트로 전송하지 않는다.)


여기까지 설정하면 실습을 하기 위한 설정이 완료된 것이다.

실습> 배너그래빙


burp 에서 서버의 버전을 획득하는 공격

1. expose_php On 일 경우
/etc/php.ini
expose_php = On

Response 의 내용
Server: Apache/2.4.6 (CentOS) PHP/5.4.16
X-Powered-By: PHP/5.4.16

2. expose_php Off 일 경우
/etc/php.ini
expose_php = Off

Response 의 내용
Server: Apache/2.4.6 (CentOS)

3. Servertokens 설정이 없는 경우

Response 의 내용

HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 03:21:18 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT


4. Servertokens prod 로 설정한 경우
# vi /etc/httpd/conf/httpd.conf 
Servertokens prod

# systemctl restart httpd

Response 의 내용

HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 03:18:29 GMT
Server: Apache
Expires: Thu, 19 Nov 1981 08:52:00 GMT

실습> GET / POST 방식의 전송 분석하기


버프에서 분석
웹 디렉터리: /var/www/html
파일1: get.html  -> get.php
파일2: post.html -> post.php

-- get.html --
<!--
파일명: get.html
프로그램 설명: GET 방식으로 서버로 데이터를 전송한다.
작성자: 리눅스마스터넷
get.html -> get.php
//-->
<!doctype html>
<html>
  <head>
    <meta charset=utf-8>
    <title> ::: get.html ::: </title>
  </head>

<body>

<form method="get" action="get.php">
<table align=center bgcolor=#000000 border=0
       cellpadding=4 cellspacing=1 width=300 >
<tr bgcolor=#ffffff>
  <td align=center width=100> 이름 </td>
  <td width=240> <input type=text name=userid> </td>
</tr>
<tr bgcolor=#ffffff>
  <td align=center width=60> 비밀번호 </td>
  <td width=240> <input type=password name=userpw> </td>
</tr>
<tr bgcolor=#ffffff>
  <td align=center colspan=2>
  <input type=submit value='저장'> </td>
</tr>
</table>
</form>

</body>
</html>
-- get.html --

-- get.php --
<?
/*
파일명: get.php
프로그램 설명: GET 방식으로 데이터를 클라이언트로 전송한다.
작성자: 리눅스마스터넷
get.html -> get.php
*/
print_r($_GET);
?>
-- get.php --


-- post.html --
<!--
파일명: post.html
프로그램 설명: POST 방식으로 서버로 데이터를 전송한다.
작성자: 리눅스마스터넷
post.html -> post.php
//-->
<!doctype html>
<html>
  <head>
    <meta charset=utf-8>
    <title> ::: post.html ::: </title>
  </head>

<body>

<form method="post" action=post.php>
<table align=center bgcolor=#000000 border=0
       cellpadding=4 cellspacing=1 width=300 >
<tr bgcolor=#ffffff>
  <td align=center width=100> 이름 </td>
  <td width=240> <input type=text name=userid> </td>
</tr>
<tr bgcolor=#ffffff>
  <td align=center width=60> 비밀번호 </td>
  <td width=240> <input type=password name=userpw> </td>
</tr>
<tr bgcolor=#ffffff>
  <td align=center colspan=2>
  <input type=submit value='저장'> </td>
</tr>
</table>
</form>

</body>
</html>
-- post.html --

-- post.php --
<?
/*
파일명: post.php
프로그램 설명: POST 방식으로 데이터를 클라이언트로 전송한다.
작성자: 리눅스마스터넷
post.html -> post.php
*/
print_r($_POST);
?>
-- post.php --

실습> 로그인 프로그램

login.php loginok.php logout.php 파일을 생성한다.

login.php    : 로그인 페이지
loginok.php  : 로그인 인증 페이지 (DB 연동 부분)
logout.php   : 로그아웃 페이지


               로그아웃 흐름도
   +-----------------------------------+
   |                                   |
   |                                   |
   |                                   v
로그인 페이지     로그인 인증        로그아웃 페이지
login.php -----> loginok.php ----> logout.php 
                    |                  |
로그인 흐름도       |                  |
                    |                  |
   ^                |                  |
   |                |                  |
   +----------------+                  |
                                       |
   ^                                   |
   |                                   |
   +-----------------------------------+

1. APM 연동
APM을 연동한다.
위에서 설정했기 때문에 설정할 필요가 없지만 아래 명령어로 확인할 필요는 있다.
# php -r "mysqli_connect();"
PHP Warning:  mysqli_connect(): (28000/1045): Access denied for user 'root'@'localhost ... (생략)

2. DB/Table 생성

DB명 : newproject
TB명 : member
MariaDB [newproject]> desc member;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| no       | int(11)     | NO   | PRI | NULL    | auto_increment |
| userid   | varchar(20) | NO   | UNI | NULL    |                |
| userpw   | varchar(41) | NO   |     | NULL    |                |
| username | varchar(20) | NO   |     | NULL    |                |
| regdate  | datetime    | NO   |     | NULL    |                |
| phone    | varchar(20) | YES  |     | NULL    |                |
| email    | varchar(50) | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

CREATE DATABASE newproject;
USE newproject
CREATE TABLE member (
    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 '이메일'
);

INSERT INTO member VALUES ('', 'admin',  password('111111'), '관리자', now(), '010-1111-2222', 'admin@linuxmaster.net');
INSERT INTO member VALUES ('', 'mrhong', password('222222'), '홍길동', now(), '010-2222-3333', 'mrhong@linuxmaster.net');

SELECT * FROM member;

MariaDB [newproject]> quit


3. 웹 프로그램 생성
/var/www/html 디렉터리에 웹애플리케이션 파일을 생성한다.
:set noai , :set paste 를 설정하고 복사/붙여넣기 한다.

# cd /var/www/html
# mkdir LOGINTEST; cd LOGINTEST

# vi login.php
-- login.php --
<?
/*
 * 파일명 : login.php
 * 프로그램 설명 : 로그인 메인 프로그램
 * 작성자 : 리눅스마스터넷
 */

session_start(); // 세션 시작
?>
<html>
<head>
  <title> :::로그인 테스트::: </title>
  <meta charset="utf-8"/>
</head>
<body>

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

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

<?
} else {

?>
<form method=post action=loginok.php>
<table align=center border=1 cellpadding=5 cellspacing=0>
<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>

<?
}
?>

</body>
</html>
-- login.php --

# vi loginok.php
<?
/*
 * 파일명 : loginok.php
 * 프로그램 설명 : DB에 사용자를 확인해서 로그인하는 파일
 * 작성자 : 리눅스마스터넷
 */
session_start();  // 세션 시작

// 디버깅 시 사용
//print_r($_POST);
//exit;

// 원래는 root로 DB를 접속하면 보안상 문제가 발생되므로 일반 유저로 접속해야 한다.
// 여기서는 테스트므로 root로 그냥 접속한다.
$DBHOST="localhost";
$DBUSER="root";
$DBPASS="P@ssw0rd";
$DBNAME = "newproject";
$TBNAME = "member";

# DBMS 접속
#resource mysql_connect(서버명,사용자,비번)
$connect = mysqli_connect($DBHOST, $DBUSER, $DBPASS, $DBNAME) or die("DBMS에 접속 실패");

# DB 선택
# mysql_select_db(DB명, 연결정보)
//mysqli_select_db($DBNAME);

# 쿼리 실행
$query = "SELECT * FROM $TBNAME WHERE userid = '$_POST[userid]' and userpw = password('$_POST[userpw]') ";

/* 디버깅 용도
 * echo $query;
 * exit;
 */

# resource mysql_query(쿼리문, 연결정보)
# resource mysqli_query(연결정보, 쿼리문)
$result = mysqli_query($connect, $query);

# 전체 자료 수가 1이면 userid / userpw 가 일치했다는 의미이다.
$num = mysqli_num_rows($result);

if($num)
{  // 세션 생성
    $row = mysqli_fetch_array($result); // $row 변수에 연관 배열로 저장한다.
    // echo "디버깅 : $num <br>";
    // echo "디버깅 : $row[username]";
    // exit;
    $_SESSION['userid'] = $row['userid'];  // 세션변수 userid 를 생성한다.
    $_SESSION['username'] = $row['username']; // 세션변수 username 을 생성한다.
   echo " <script language=JavaScript>
          <!--
              location.href = 'login.php';
          //-->
          </script>
        ";

} else { // userid / userpw 가 틀렸다면 에러메세지를 출력하고 이제 페이지로 돌려보낸다.
   echo " <script language=JavaScript>
          <!--
              alert('아이디/비번을 확인해주세요.');
              history.go(-1);
          //-->
          </script>
        ";
}

?>
-- loginok.php --

# vi logout.php
-- logout.php --
<?
/*
 * 파일명 : logout.php
 * 프로그램 설명 : 로그아웃 프로그램
 * 작성자 : 리눅스마스터넷
 */
session_start();    // 세션 시작
session_destroy();  // 세션 삭제

// login.php 로 다시 돌려 보낸다.
echo " <script language=JavaScript>
       <!--
          location.href = 'login.php';
       //-->
       </script>
     ";
?>
-- logout.php --

실습> 웹 데이터 조작하기


웹 데이터: Request Header 조작
Intercept is off/on 설정을 적절하게 사용한다.

1. 웹 페이지 접속
http://200.200.200.3/LOGINTEST/login.php

2. 페이지 소스 분석
크롬 단축키: Ctrl + U, 탭 닫기: Ctrl + F4

HTML 코드를 먼저 파악하기 위해서 소스코드를 분석한다.
소스코드에는 이전에 사용한 페이지이지만 현재는 사용하지 않는 페이지가 있을 때
소스코드에서 삭제하면 최고이지만 단순히 주석으로 막아놓은 경우도 있다.
이때는 문제가 발생할 수 있다.

예)
<!-- 
개발팀: 홍길동 대리
연락처: 010-1000-2222
<center><h1> 로그인 페이지 </h1></center> 
-->

3. Intercept On 설정
Intercept On 으로 설정한다.

4. 데이터 전송
아이디: admin, 비밀번호: 123 을 입력해서 전송한다.

Request 내용
POST /LOGINTEST/loginok.php HTTP/1.1
Host: 200.200.200.3
Content-Length: 23
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://200.200.200.3
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST/login.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close

userid=admin&userpw=123  <-- Request중에서 POST 방식의 전송은 Body 부분에 담겨져서 Server로 전송된다.

변수명=값&변수명=값

Response 내용
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:06:19 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 183
Connection: close
Content-Type: text/html; charset=UTF-8

 <script language=JavaScript>                <-- Response 내용은 모두 Body에 담겨져서 Client로 전송된다.
          <!--
              alert('아이디/비번을 확인해주세요.');
              history.go(-1);
          //-->
          </script>
        
GET /LOGINTEST/login.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close



HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:07:31 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 508
Connection: close
Content-Type: text/html; charset=UTF-8

<html>
<head>
  <title> :::로그인 테스트::: </title>
  <meta charset="utf-8"/>
</head>
<body>

<form method=post action=loginok.php>
<table align=center border=1 cellpadding=5 cellspacing=0>
<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>


</body>
</html>


POST /LOGINTEST/loginok.php HTTP/1.1
Host: 200.200.200.3
Content-Length: 26
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://200.200.200.3
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST/login.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close

userid=admin&userpw=111111

Response 의 값
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:08:46 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 132
Connection: close
Content-Type: text/html; charset=UTF-8

 <script language=JavaScript>
          <!--
              location.href = 'login.php';
          //-->
          </script>
        

GET /LOGINTEST/login.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST/loginok.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close

HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:09:42 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 356
Connection: close
Content-Type: text/html; charset=UTF-8

<html>
<head>
  <title> :::로그인 테스트::: </title>
  <meta charset="utf-8"/>
</head>
<body>


<table align=center border=1 cellpadding=5 cellspacing=0>
<tr align=center>
  <td align=center> 관리자 님 환영합니다. </td>
</tr>
<tr align=center>
  <td align=center> <a href=logout.php> 로그아웃 </a> </td>
</tr>
</table>


</body>
</html>


Client는 ID/PW 가 맞기 때문에 서버에서 인증을 거쳐서 로그인된 페이지가 출력된다.

관리자 님 환영합니다.
로그아웃


GET /LOGINTEST/logout.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST/login.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close

HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:11:11 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 116
Connection: close
Content-Type: text/html; charset=UTF-8

 <script language=JavaScript>
       <!--
          location.href = 'login.php';
       //-->
       </script>
     

GET /LOGINTEST/login.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST/logout.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=qr4k2mgekt9121qu3aio3ml685
Connection: close

HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 05:11:33 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 508
Connection: close
Content-Type: text/html; charset=UTF-8

<html>
<head>
  <title> :::로그인 테스트::: </title>
  <meta charset="utf-8"/>
</head>
<body>

<form method=post action=loginok.php>
<table align=center border=1 cellpadding=5 cellspacing=0>
<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>


</body>
</html>


아이디:	
비밀번호:
    로그인

실습> 개발자 도구 사용하기


브라우저: 크롬

F11: 전체화면(자주 눌려진다.)
F12: 개발자 도구

네이버로 접속해서 네이버를 시작페이지로를 찾아보기

실습> Client Side Validation


소스코드를 변경한 후 admin/P@ssw0rd 으로 로그인하기

1. 소스코드 수정
[root@webhacking ~]# cd /var/www/html/
[root@webhacking html]# vi login.php 

<input type="text" name="user_id" style="border: 0;" maxlength="4">

2. 로그인
3가지 방법이 존재한다.
- 개발자 도구를 이용하는 방법
  개발자 도구를 열어서 관련 소스를 수정한다.
- 버프를 이용하는 첫 번째 방법
  Server 에서 웹 데이터가 Response 로 전송되었을 때 버프에서 수정해서 Client로 전송한다.
- 버프를 이용하는 두 번째 방법
  Client 에서 서버로 웹 데이터를 전송할 때 버프에서 값을 수정해서 Server로 전송한다.

실습> Client Side Validation Bypass


http://200.200.200.3/register.php

아래 조건에 맞게 회원을 가입한다.
ID: t12        <-- 원래는 4글자 부터 가입이 가능하지만 우회
이름: 홍길동
비밀번호: 123  <-- 원래는 6글자 부터 가입이 가능하지만 우회
나이: 20
닉네임: MrHong

register_ok.php 파일의 내용에서 패스워드 길이 부분을 주석처리 한다.

Server Side Validation                                                                                    
 12   if(!$nick){
 13     $nick=$name;
 14   }
 15
 16 /*  16번 라인부터 ~ 47번 라인까지 모두 주석으로 처리한다.
 17    if($id == "root" || $id == "admin" || $id == "master" || $id == "administrator"){
 18        echo "<script>alert('사용할 수 없는 계정입니다.');
       :
       : (생략)
 47 */
 48
 49   require("dbconn.php");

Intercept is on > 회원가입 클릭 > Forward

Response 된 웹 데이터를 버프에서 아래처럼 if문을 모두 삭제하면 Client Validation 기능은 없어지게 된다.

    <script type="text/javascript">
      function ck() {
        document.mform.submit();
      }
    </script>

Intercept is off 을 클릭하면 회원이 가입된다. 


#############
## 세션 & 쿠키
#############
웹에서 사용하는 HTTP 는 연결을 유지하지 않는 프로토콜이다.
SSH, Telnet 의 프로토콜은 연결을 유지하는 프로토콜이다.
HTTP 이용한 웹 클라이언트와 웹 서버간에 연결을 유지하는 역할을 하는게 쿠키와 세션을 이용한다.

쿠키: 클라이언트에 정보가 저장된다.
세션: 서버에 정보가 저장된다.

예전: 인증에 성공하면 쿠키로 관련 정보를 저장했다. (정보가 클라이언트에 저장되기 때문에 보안에 문제가 생기게 되었다.)
요즘: 
인증에 성공하면 세션으로 관련 정보를 서버에 저장하고 세션ID를 클라이언트에게 전송해서 클라이언트에서는 세션ID를 
쿠키로 저장한다. 그리고 서버에 접속을 할 때 마다 세션ID를 쿠키로 가지고 다니고 서버가 이를 확인해서 로그인한 사용자로 판단한다.

서버에서 세션이 저장된 디렉터리: 
/var/lib/php/session/ (yum으로 PHP를 설치하면 Default 값이고 이 디렉터리의 위치는 변경이 가능하다.)

소스로 설치하면 /tmp 디렉터리가 세션 디렉터리로 쓰이기도 하고
웹애플리케이션들은 자신의 앱 디렉터리 밑에 특정 세션디렉터리를 개별적으로 만들어서 사용하기도 한다.

실습> 세션 파일을 이용한 로그인 분석


세션파일: 
http 프로토콜은 연결이 유지되지 않는 프로토콜이므로 세션 파일을 이용해서 연결을 유지시켜준다. 
ex) 로그인

php 세션 파일의 형식: sess_랜덤문자열

1. 소스코드 작성
테스트할 소스코드를 작성한다.
[root@webhacking ~]# cd /var/www/html
[root@webhacking html]# vi session1.php
<?
/*
 * 파일명: session1.php
 * 프로그램 설명: 세션 이해하기
 * 작성자: 리눅스마스터넷
 */

// 세션을 사용하기 위해서는 session_start(); 함수를 사용한다.
// 이 함수를 사용하기 전에 HTML 코드가 하나라도 클라이언트로 전송되면 안된다.
session_start()
?>

2. 세션파일 확인
서버에서 세션이 저장된 디렉터리인 /var/lib/php/session/ 디렉터리의 파일들을 확인한다.
[root@webhacking html]# ll /var/lib/php/session/
합계 4
-rw-------. 1 apache apache 70 10월 25 13:10 sess_j3212sndenfbilpoc00927lgl6
-rw-------. 1 apache apache  0 10월 24 14:06 sess_u7t0gjqu4l45iqiiub4277fhp1

3. 세션파일 삭제
[root@webhacking html]# rm -fv /var/lib/php/session/*
[root@webhacking html]# ll /var/lib/php/session/
합계 0

4. EditThisCookie 추가
EditThisCookie PlugIn을 설치한다.
https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg?hl=ko

설치가 완료된 후 
확장 프로그램에서 EditThisCookie를 고정시킨다.
[메뉴] > [설정] > [확장 프로그램] > [EditThisCookie 세부 정보] > 시크릿 모드에서 허용을 활성화 시킨다.

5. 세션과 쿠키 삭제
웹사이트로 접속해서 모든 쿠키를 EditThisCookie에서 삭제한다.
http://200.200.200.3/

쿠키가 없습니다!

세션 디렉터리에서 세션 파일을 삭제한다.
[root@webhacking html]# rm -f /var/lib/php/session/*

6. 버프 가로채기 설정
Intercept is on으로 설정한다.

7. 접속
웹사이트로 접속한다.
http://200.200.200.3/session1.php

Request 내용: Request to http://200.200.200.3/session1.php
GET /session1.php HTTP/1.1
Host: 200.200.200.3
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Connection: close

Burp에서 Forward 버튼을 클릭한다.

Response 내용: Response from http://200.200.200.3/session1.php
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 07:12:36 GMT
Server: Apache/2.4.6 (CentOS)
Set-Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7; path=/  /  <-- /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7 생성
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

Intercept is off 로 클릭해서 클라이언트로 웹 데이터를 모두 전송한다.

8. 쿠키 확인
EditThisCookie에서 서버에서 전송된 세션ID가 쿠키로 저장된 것을 확인한다.
http://200.200.200.3/session1.php

200.200.200.3 | PHPSESSID
값
3kp8k3vei82mshqtjnogd7nna7

9. 재접속
Intercept is on으로 설정하고 접속을 한다.
http://200.200.200.3/session1.php

Request 내용: Request to http://200.200.200.3/session1.php
GET /session1.php HTTP/1.1
Host: 200.200.200.3
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7  <-- 클라이언트에 쿠키값이 저장되어 있으므로 서버로 접속할 때 이 값을 가지고 다닌다.
Connection: close

Forward 버튼을 클릭한다.

Response 내용: Response from http://200.200.200.3/session1.php
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 07:20:44 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

Intercept is off 로 클릭해서 클라이언트로 웹 데이터를 모두 전송한다.


>>> 정리: 클라이언트가 서버의 특정 파일(session_start() 함수가 있는 페이지)을 요청할 경우 <<<

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

실습> 세션 변수


https://www.php.net/manual/en/session.configuration.php

1. 세션 확인
/etc/php.ini 설정 파일
[Session]
session.save_handler = files
session.name = PHPSESSID
session.use_cookies = 1

; for mod_php, see /etc/httpd/conf.d/php.conf
; for php-fpm, see /etc/php-fpm.d/*conf
;session.save_path = "/tmp"

기본 APM을 설치하면 /etc/httpd/conf.d/php.conf 에 세션이 저장될 경로가 설정되어 있다.
php_value session.save_handler "files"
php_value session.save_path    "/var/lib/php/session"   

2. 세션 파일 삭제
PHP 세션디렉터리 : /var/lib/php/session
리눅스 서버에서 세션을 관리하는 디렉터리에 세션 파일들을 삭제한다.
[root@webhacking html]# rm -f /var/lib/php/session/*

3. 세션 변수 생성
[root@webhacking html]# vi session2.php 
<?
/*
 * 파일명: session2.php
 * 프로그램 설명: 세션 변수 이해하기
 * 작성자: 리눅스마스터넷
 */

// 1. 세션 파일 생성
// 세션을 사용하기 위해서는 session_start(); 함수를 사용한다.
session_start();  // /var/lib/php/session/sess_랜덤문자열로 세션파일이 생성된다.

// 2. 세션 변수 생성
// 세션 변수 형식: $_SESSION['변수명'] = 값;
// 세션 변수를 생성하는 순간 세션 변수를 세션 파일에 저장한다.
$_SESSION['userid'] = 'LinuxmasterNet';
$_SESSION['age']    = 20;
$_SESSION['name']   = '리눅스마스터넷';
?>

[root@webhacking html]# ll /var/lib/php/session/
합계 0

3. 접속
버프의 Intercept를 On 으로 설정하고 접속한다.
http://200.200.200.3/session2.php

서버에는 세션 파일이 없지만 클라이언트에는 세션ID를 저장하고 있기 때문에 이걸 받은 서버는 
다시 세션 파일을 생성하고 세션변수들을 저장한다.
Request 내용: Request to http://200.200.200.101/session2.php
GET /session2.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7  <-- 세션값을 전송한다.
Connection: close

Forward 버튼을 클릭해서 서버로 웹 데이터를 전송한다.

session_start(); 함수가 실행되면서 세션 디렉터리에 세션 파일이 생성되고 그 안에 변수와 값들이 저장된다. <-- apache 사용자가 한다.
세션 디렉터리에 세션 파일을 확인하면 세션 파일의 크기가 72byte라는 것을 알 수 있다.
[root@webhacking html]# ll /var/lib/php/session/
합계 4
-rw-------. 1 apache apache 72  3월 31 16:49 sess_3kp8k3vei82mshqtjnogd7nna7

Response 내용: Response from http://200.200.200.3/session2.php
HTTP/1.1 200 OK
Date: Tue, 25 Oct 2022 06:10:47 GMT
Server: Apache
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

Intercept is off 로 클릭해서 클라이언트로 웹 데이터를 모두 전송한다.

4. 세션 파일 확인
세션 파일의 형식: 
문자열: 변수명|s:길이:"값";
정수: 변수명|i:값
[root@webhacking html]# cat /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
userid|s:14:"LinuxmasterNet";age|i:20;name|s:21:"리눅스마스터넷";

5. 파일 생성
[root@webhacking html]# vi session3.php 
<?
/*
 * 파일명: session3.php
 * 프로그램 설명: 세션 변수 이해하기
 * 작성자: 리눅스마스터넷
 */

// 1. 세션 파일 생성
// 세션을 사용하기 위해서는 session_start(); 함수를 사용한다.
session_start();  // /var/lib/php/session/sess_랜덤문자열로 세션파일이 생성된다.

// 2. 세션 변수 확인
if(isset($_SESSION['userid']))
{
    echo $_SESSION['userid'] . " 님 환영합니다.";
}
?>

6. 접속
http://200.200.200.3/session3.php
LinuxmasterNet 님 환영합니다.
~~~~~~~~~~~~~~
    세션 파일에 저장된 userid 변수값

실습> 로그인 프로그램


   +----------------+ <--------+
   |                |          |
   v                |          |
login.php ----> loginok.php    |
   |                           |
   |                           |
   +---------->  logout.php ---+

!!! 세션을 사용하기 위해서는 반드시 session_start() 함수를 사용해야 한다. !!!

login.php
로그인 O: 
userid님 환영합니다. 를 출력한다. 
로그아웃 링크를 출력한다.

로그인 X: 
로그인폼을 출력한다. 
사용자에게 userid와 userpass를 입력 받아서 loginok.php로 POST방식으로 전송한다.

   
loginok.php 
login.php에서 전송된 userid && userpass 비교해서 맞으면 세션변수를 세션파일에 등록하고 login.php로 돌아간다.
원래는 DB에서 전송한 userid/userpass를 인증을 통해서 성공해야 세션변수를 생성하지만
여기서는 간단히 소스코드에 userid와 userpass 를 저장하는 하드코딩 형태로 작성한다.

logout.php
세션파일을 삭제하고 login.php로 돌아간다.

[root@webhacking html]# mkdir LOGINTEST2
[root@webhacking html]# cd LOGINTEST2
[root@webhacking LOGINTEST2]# touch login.php loginok.php logout.php
[root@webhacking LOGINTEST2]# vi login.php
<?php
/*
 * 파일명: login.php
 * 프로그램 설명: 로그인 기능으로 사용자와 비밀번호를 받아서 loginok.php로 전송한다. 
 * 작성자: 리눅스마스터넷
 * 작성일: 2022. 10. 26. (수) 11:30:51 KST
 */
session_start();
?>
<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title> 로그인 </title>

<body>
<?php
if(isset($_SESSION['userid'])) {  // 로그인 O
    echo $_SESSION['userid'];
?>
    님 환영합니다. <br> <a href="logout.php">로그아웃</a>
<?
} else { // 로그인 X 
?>

<form method="POST" action="loginok.php">
사용자: <input type="text" name="userid"> 
비밀번호: <input type="password" name="userpass">
<input type="submit" value="로그인">

</form>
<?
}
?>

</body>
</html>


[root@webhacking LOGINTEST2]# vi loginok.php
<?php
/*
 * 파일명: loginok.php
 * 프로그램 설명: 사용자와 비밀번호를 비교해서 세션변수를 생성한다. 
 * 작성자: 리눅스마스터넷
 * 작성일: 2022. 10. 26. (수) 11:30:51 KST
 */
session_start();
/*
 * GET 방식의 변수명 형식: $_GET['변수명']
 * POST 방식의 변수명 형식: $_POST['변수명']
 * 세션 변수명 형식: $_SESSION['변수명']
 */

$dbUserid = "admin"; 
$dbUserpass = "P@ssw0rd";  

if($_POST['userid'] == "$dbUserid" && $_POST['userpass'] == "$dbUserpass")  // 로그인 성공
{
    $_SESSION['userid'] = $_POST['userid'];
} else {  // 로그인 실패
?>
   <script>
   alert("아이디와 비밀번호를 확인해주세요!");
   </script>
<?
}
?>
<script>
 location.href = 'login.php'; 
</script>


[root@webhacking LOGINTEST2]# vi logout.php
<?php
/*
 * 파일명: logout.php
 * 프로그램 설명: 로그아웃 기능으로 세션파일을 삭제한다. 
 * 작성자: 리눅스마스터넷
 * 작성일: 2022. 10. 26. (수) 11:30:51 KST
 */
session_start();
session_destroy();
header("Location: login.php");
?>

Intercept is On 을 켠 상태에서 접속한다.

Request: http://200.200.200.3/LOGINTEST2/login.php

GET /LOGINTEST2/login.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7
Connection: close

Response: http://200.200.200.3/LOGINTEST2/login.php
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 08:23:12 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 189
Connection: close
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title> 로그인 </title>

<body>
LinuxmasterNet    님 환영합니다. <br> <a href="logout.php">로그아웃</a>

</body>
</html>

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

Request: http://200.200.200.3/LOGINTEST2/logout.php
GET /LOGINTEST2/logout.php HTTP/1.1
Host: 200.200.200.3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://200.200.200.3/LOGINTEST2/login.php
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ko;q=0.8
Cookie: PHPSESSID=3kp8k3vei82mshqtjnogd7nna7
Connection: close

Response: http://200.200.200.3/LOGINTEST2/logout.php
HTTP/1.1 302 Found
Date: Fri, 31 Mar 2023 08:27:46 GMT
Server: Apache/2.4.6 (CentOS)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Location: login.php
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

Intercept is Off 로 변경한다.

사용자:     비밀번호:     로그인

실습> root 에서 로그인 로그아웃 수동 처리하기


root에서 직접 세션파일을 편집해서 로그인/로그아웃에 대한 이해를 숙지한다.

1. 로그인
- 실제는 login.php -> loginok.php 에서 이 역할을 담당한다.
- 여기서는 분석을 위해서 root에서 직접 편집한다.
# echo 'userid|s:5:"admin";' > /var/lib/php/session/세션파일명 

브라우저에서 로그인 처리 
http://200.200.200.3/LOGINTEST2/login.php
  <-- 로그인창에서 로그인한다.

콘솔에서 직접 사용 테스트 
[root@localhost session]#  echo 'userid|s:5:"admin";' > /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
[root@localhost session]# ll /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
-rw-------. 1 apache apache 20  3월 31 17:48 /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
[root@localhost session]# cat /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
userid|s:5:"admin";

2. 로그아웃
- 실제는 logout.php 에서 이 역할을 담당한다.
- 여기서는 분석을 위해서 root에서 직접 편집한다.
# > /var/lib/php/session/세션파일명 

브라우저에서 로그아웃 처리
http://200.200.200.3/LOGINTEST2/login.php
  <-- 로그아웃 

콘솔에서 직접 사용 테스트 
[root@localhost session]# ll /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
-rw-------. 1 apache apache 19  3월 31 17:48 /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
[root@localhost session]# > /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
[root@localhost session]# ll /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7
-rw-------. 1 apache apache 0  3월 31 17:49 /var/lib/php/session/sess_3kp8k3vei82mshqtjnogd7nna7

profile
정보보안 전문가

0개의 댓글