보안 위협과 방어
보호 메커니즘이 완벽하더라도 공격자는 소프트웨어의 취약점을 파고듭니다. OS 수준의 보안 위협과 방어 기법을 이해하면, 안전한 코드를 작성하고 시스템을 견고하게 구성하는 능력이 생깁니다. 보안은 단일 기술이 아니라 계층적 방어(Defense in Depth)입니다.
버퍼 오버플로우
버퍼 오버플로우(Buffer Overflow)는 가장 오래되고 가장 위험한 취약점 중 하나입니다. 1988년 모리스 웜부터 현재까지 계속 발견됩니다.
배열의 범위를 넘어서 데이터를 쓰면, 인접한 메모리 영역(리턴 주소, 함수 포인터 등)을 덮어쓸 수 있습니다.
#include <string.h>
#include <stdio.h>
/* 취약한 코드 — 절대 이렇게 작성하지 마세요 */
void vulnerable(char *input) {
char buffer[64];
strcpy(buffer, input); /* 입력이 64바이트를 초과하면 오버플로우! */
printf("Buffer: %s\n", buffer);
}
/* 안전한 코드 */
void safe(const char *input) {
char buffer[64];
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; /* 널 종단 보장 */
printf("Buffer: %s\n", buffer);
}스택 스매싱 공격
메모리 레이아웃 (높은 주소 → 낮은 주소)
┌──────────────────┐
│ 리턴 주소 │ ← 공격자가 이걸 덮어씀
├──────────────────┤
│ 저장된 EBP │
├──────────────────┤
│ buffer[63] │ ← 오버플로우 방향 (↑)
│ ... │
│ buffer[0] │ ← 버퍼 시작
└──────────────────┘공격자가 리턴 주소를 자신의 코드 주소로 덮어쓰면, 함수 반환 시 공격자의 코드(shellcode)가 실행됩니다.
다계층 방어
#include <stdio.h>
#include <stdlib.h>
int main() {
/* 1. 안전한 함수 사용 */
char buf[32];
/* strcpy → strncpy, sprintf → snprintf, gets → fgets */
if (fgets(buf, sizeof(buf), stdin) == NULL) return 1;
/* 2. 컴파일러 방어 (-fstack-protector) */
/* 스택 카나리: 리턴 주소 앞에 랜덤 값 삽입 */
/* 함수 반환 시 카나리가 변조되었으면 __stack_chk_fail() 호출 */
/* 3. OS 방어 (ASLR) */
printf("스택 변수 주소: %p\n", (void *)buf);
printf("힙 주소: %p\n", (void *)malloc(1));
/* 매 실행마다 주소가 달라짐 → 공격자가 주소 예측 불가 */
return 0;
}# 모든 보안 옵션을 켜고 컴파일
gcc -fstack-protector-all \
-D_FORTIFY_SOURCE=2 \
-Wformat -Wformat-security \
-pie -fPIE \
-Wl,-z,relro,-z,now \
-o program program.c
# -fstack-protector-all: 모든 함수에 스택 카나리
# -D_FORTIFY_SOURCE=2: 버퍼 크기 검사 강화
# -pie -fPIE: 위치 독립 실행 파일 (ASLR 효과 극대화)
# -Wl,-z,relro,-z,now: GOT 보호 (Full RELRO)| 방어 기법 | 대상 | 동작 |
|---|---|---|
| 스택 카나리 | 스택 오버플로우 | 리턴 주소 변조 탐지 |
| ASLR | 주소 예측 | 메모리 레이아웃 무작위화 |
| DEP/NX | 코드 주입 | 데이터 영역 실행 금지 |
| PIE | 코드 영역 예측 | 코드 섹션도 무작위 배치 |
| RELRO | GOT 변조 | GOT 영역 읽기 전용화 |
| CFI | 간접 호출 변조 | 제어 흐름 무결성 검증 |
악성 코드 분류
| 유형 | 전파 방식 | 특징 | 예시 |
|---|---|---|---|
| 바이러스 | 숙주 프로그램에 삽입 | 실행 시 활성화, 자기 복제 | CIH, Melissa |
| 웜 | 네트워크 자동 전파 | 독립 실행, 취약점 이용 | Morris, WannaCry |
| 트로이 목마 | 유용한 프로그램 위장 | 백도어 설치 | Zeus, Emotet |
| 랜섬웨어 | 다양 (이메일, 웹) | 파일 암호화 후 몸값 요구 | WannaCry, Ryuk |
| 루트킷 | 기존 침입 후 설치 | 커널 수준 은닉 | Sony Rootkit |
| 키로거 | 트로이 목마/물리적 | 키 입력 기록 전송 | - |
실무 방어
import hashlib
import os
def file_hash(path):
"""파일의 SHA-256 해시 계산"""
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
# 중요 시스템 파일의 해시를 저장하고 주기적으로 비교
critical_files = ["/usr/bin/sudo", "/usr/bin/ssh", "/usr/sbin/sshd"]
for f in critical_files:
if os.path.exists(f):
print(f"{f}: {file_hash(f)}")
# 해시가 변경되었으면 → 파일이 변조된 것 (루트킷 의심)
# 실무에서는 AIDE, Tripwire, OSSEC 같은 무결성 검사 도구 사용강제 접근 제어 (MAC)
일반적인 Unix 권한(DAC, Discretionary Access Control)은 파일 소유자가 권한을 마음대로 설정할 수 있습니다. root가 탈취되면 모든 보호가 무력화됩니다.
MAC(Mandatory Access Control)은 시스템 관리자가 정의한 정책에 따라 접근을 제어합니다. root조차 정책을 우회할 수 없습니다.
SELinux
NSA가 개발한 MAC 구현입니다. 프로세스마다 보안 컨텍스트(타입)를 부여하고, 정책에 명시적으로 허용된 접근만 가능합니다.
# SELinux 상태 확인
getenforce # Enforcing, Permissive, Disabled
sestatus # 상세 상태
# 파일의 보안 컨텍스트 확인
ls -Z /var/www/html/index.html
# -rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0
# httpd 프로세스는 httpd_sys_content_t 타입의 파일만 읽을 수 있음
# /home/user/ 디렉토리는 user_home_t → httpd가 접근 불가
# 컨텍스트 복원
restorecon -Rv /var/www/html/웹 서버가 해킹되어 root 셸을 획득해도, SELinux 정책에 의해 /etc/shadow 읽기, 네트워크 포트 변경 등이 차단됩니다.
AppArmor
프로그램별로 접근 가능한 파일, 네트워크, 커패빌리티를 프로파일로 정의합니다. SELinux보다 설정이 간단합니다.
# /etc/apparmor.d/usr.sbin.nginx 프로파일 예시
# /usr/sbin/nginx {
# /var/www/** r, # 웹 콘텐츠 읽기만
# /var/log/nginx/** w, # 로그 쓰기만
# /run/nginx.pid rw, # PID 파일
# network inet tcp, # TCP 네트워크 허용
# deny /etc/shadow r, # shadow 파일 읽기 명시적 거부
# }
# AppArmor 상태 확인
aa-status
# 프로파일을 enforce 모드로 설정
aa-enforce /etc/apparmor.d/usr.sbin.nginx네트워크 보안 기초
방화벽 (iptables/nftables)
# iptables 기본 규칙: 필요한 것만 허용, 나머지 차단
iptables -P INPUT DROP # 기본 정책: 모든 입력 차단
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT # 나가는 트래픽은 허용
# 기존 연결 유지
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# SSH (포트 22) 허용
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPS 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 로컬 루프백 허용
iptables -A INPUT -i lo -j ACCEPT보안 모니터링
# 실패한 로그인 시도 확인
grep "Failed password" /var/log/auth.log | tail -20
# 현재 열린 포트 확인
ss -tlnp
# 최근 sudo 사용 기록
grep sudo /var/log/auth.log | tail -10
# 비정상 프로세스 확인
ps aux --sort=-pcpu | head -20보안 계층 정리
| 계층 | 방어 수단 |
|---|---|
| 네트워크 | 방화벽, IDS/IPS, VPN |
| 운영체제 | MAC(SELinux/AppArmor), 패치 관리, 로깅 |
| 애플리케이션 | 입력 검증, 안전한 함수, 코드 분석 |
| 데이터 | 암호화, 해싱, 백업 |
| 물리 | 잠금장치, BIOS 패스워드, TPM |
보안은 OS의 한 기능이 아니라, 물리적 접근부터 애플리케이션 코드까지 전 계층에 걸친 노력입니다. 다음 장에서는 현대 인프라의 핵심인 가상화와 컨테이너를 다루겠습니다.