심층 방어
고친 사람 github-actions[bot]
심층 방어는 공격자가 안쪽까지 들어오려면 막는 장치(로그인 · 권한 검사 · 데이터베이스 계정의 권한)를 여러 겹 뚫어야 하게 만듭니다. 한 겹이 뚫려도 다음 겹이 공격을 멈춰 세웁니다. 어떤 보안 장치도 완벽하지 않다고 보고 시스템을 짓는 방식입니다.
쉽고 빠른 이해
심층 방어는 막는 장치를 여러 겹 쌓아서 한 겹이 뚫려도 버티게 합니다. 로그인을 통과한 요청도 권한을 한 번 더 검사합니다. 데이터베이스 계정은 필요한 일만 하게 묶어 둡니다.
장치 하나에 모든 것을 걸면 그 장치의 빈틈 하나로 전부가 열립니다. 장치에는 버그도 있고 설정 실수도 있습니다. 그래서 빈틈이 하나쯤 있다고 치고 뒤에 다른 겹을 둡니다.
어떻게 쌓나:
- 바깥에서 막습니다. 쓰지 않는 통로를 닫습니다. 들어오는 값을 검사합니다
- 안쪽에서 다시 막습니다. 요청마다 누구인지, 해도 되는 일인지 확인합니다
- 뚫린 뒤를 대비합니다. 권한을 좁혀 피해를 줄입니다. 기록을 남겨 뚫린 것을 알아챕니다
대가도 있습니다. 겹마다 만들고 관리할 일이 늘어납니다. 요청은 조금씩 느려집니다. 정상 요청이 막혔을 때 어느 겹이 막았는지 찾기도 번거롭습니다.
그래서 잃을 것이 큰 서비스에는 겹을 많이 둡니다. 결제 정보를 다루는 서비스가 그렇습니다. 사내에서 잠깐 쓰는 도구에는 겹을 적게 둡니다.
상세
성을 떠올려 봅니다. 성 밖에는 물을 채운 도랑을 팝니다. 그 안에 성벽을 쌓습니다. 가장 안쪽에는 마지막까지 버틸 탑을 세웁니다. 도랑을 건넌 적도 성벽 앞에서 다시 멈춥니다.
심층 방어(defense in depth)는 이 생각을 시스템에 옮긴 설계 원칙입니다. 서로 다른 보안 장치를 여러 겹 둡니다. 한 겹이 뚫려도 공격이 끝까지 가지 못하게 하려는 것입니다. 백엔드 서비스라면 방화벽, 로그인, 권한 검사, 데이터베이스 계정의 권한이 각각 한 겹이 됩니다.
이 이름은 군사 용어에서 왔습니다. 방어선을 한 줄로 세우지 않고 여러 줄로 깊게 배치해 적의 진격을 늦추는 전술입니다. 원자력 발전소의 안전 설계에서도 같은 이름을 씁니다. 이 문서는 소프트웨어 보안에서 쓰는 뜻을 다룹니다.
한 겹을 믿지 않는 까닭
겹을 왜 여럿 두는지부터 봅니다. 출발점은 어떤 보안 장치에도 빈틈이 있다는 것입니다.
빈틈은 여러 곳에서 생깁니다. 코드에 숨은 버그, 잘못 넣은 설정, 새어 나간 비밀번호가 모두 빈틈이 됩니다.
만든 쪽이 아직 모르는 취약점도 있습니다. 이런 취약점을 제로데이라고 부릅니다. 모르는 빈틈은 미리 고칠 수 없습니다.
겹이 하나뿐이면 빈틈 하나가 곧 전부입니다. 그 겹 뒤에 있는 것이 모두 드러납니다. 겹이 여럿이면 공격자는 겹마다 빈틈을 따로 찾아야 합니다. 모든 겹의 빈틈이 한 줄로 이어져야 끝까지 뚫립니다.
겹끼리 달라야 하는 까닭
겹을 늘리기만 해서는 모자랍니다. 겹과 겹이 서로 어떻게 달라야 하는지가 남습니다.
두 겹이 같은 방식으로 막으면 같은 빈틈을 함께 가집니다. 똑같은 규칙을 넣은 방화벽 두 대를 줄지어 세운 경우가 그렇습니다. 첫 대를 지나간 공격은 둘째 대도 지나갑니다. 겉보기로는 두 겹이지만 막는 힘은 한 겹입니다.
열쇠를 함께 쓰는 겹도 한 겹으로 줄어듭니다. 관리자 비밀번호 하나로 서버 접속과 데이터베이스 접속이 다 열린다고 해 봅니다. 그 비밀번호가 새는 순간 두 겹이 함께 열립니다.
그래서 겹마다 막는 방식을 다르게 고릅니다. 네트워크 주소로 가리는 겹 뒤에는 사용자가 누구인지 확인하는 겹을 둡니다. 그 뒤에는 요청에 담긴 값을 검사하는 겹을 둡니다. 한 방식이 놓친 공격을 다른 방식이 잡을 수 있습니다.
요청 하나가 지나는 겹
이 소절은 웹 서비스 하나에 공격 하나를 넣어 봅니다. 그 공격이 어느 겹에서 멈추는지 따라갑니다. 넣어 볼 공격은 SQL(Structured Query Language, 구조화 질의 언어) 인젝션입니다.
SQL 은 데이터베이스에 일을 시키는 언어입니다. SQL 인젝션은 사용자가 입력 칸에 SQL 조각을 적어 넣는 공격입니다.
로그인 아이디 칸에 ' OR '1'='1 을 적는 것이 흔한 예입니다. 서버가 그 입력을 쿼리 문자열에 이어 붙이면 조건이 WHERE id = '' OR '1'='1' 이 되어 늘 참이 됩니다. 공격자가 쓴 조각이 조건을 바꾸는 명령으로 실행된 것입니다.
바깥 방화벽은 이 공격을 못 막습니다. 웹 요청이 드나드는 통로는 열려 있어야 하기 때문입니다. 공격은 정상 요청과 같은 통로로 들어옵니다. 그래서 안쪽 겹이 필요합니다.
첫 겹은 입력 검증입니다. 서버는 들어온 값이 기대한 꼴인지 봅니다. 나이 칸에 숫자가 아닌 것이 오면 돌려보냅니다. 이름이나 게시글처럼 아무 글자나 받아야 하는 칸에서는 이 겹이 공격을 놓칠 수 있습니다.
둘째 겹은 쿼리를 만드는 방식입니다. 입력을 문자열로 이어 붙이지 않습니다. 값이 들어갈 칸을 따로 비워 둔 쿼리를 씁니다. 이것이 매개변수화 쿼리입니다.
SELECT * FROM users WHERE id = ? 처럼 값이 들어갈 칸을 ? 로 비워 둡니다. 데이터베이스는 그 칸에 들어온 것을 값으로만 읽습니다. 공격자가 ' OR '1'='1 을 적어도 그런 글자로 된 아이디를 찾을 뿐입니다. 조건을 바꾸는 명령은 되지 않습니다.
셋째 겹은 데이터베이스 계정의 권한입니다. 코드 한 곳이 이어 붙이기를 써서 둘째 겹이 비었다고 해 봅니다. 공격 명령이 실행되더라도 그 계정이 할 수 있는 일까지만 됩니다. 서비스 계정에 테이블을 지우는 권한이 없으면 테이블은 안 지워집니다. 일에 필요한 만큼만 권한을 주는 원칙이 최소 권한입니다.
넷째 겹은 기록입니다. 이상한 쿼리와 그 요청이 감사 로그에 남습니다.
이 겹은 공격을 막지는 못합니다. 대신 무슨 일이 있었는지 뒤늦게라도 알아챕니다. 피해 범위도 잴 수 있습니다.
네 겹을 한 그림에 모으면 이렇습니다. 갈림마다 한쪽은 거기서 끝나고 다른 쪽은 다음 겹으로 넘어갑니다.
flowchart TD
F["공격 요청이 방화벽을 지난다 · 웹 통로는 열려 있다"] --> B{"입력 검증이 걸러내나"}
B -->|걸러낸다| S1["여기서 멈춘다"]
B -->|놓친다| C{"쿼리가 입력을 값으로만 읽나"}
C -->|그렇다| S2["명령으로 실행되지 않는다"]
C -->|아니다| D["명령이 실행된다 · 계정 권한만큼만 해를 끼친다"]
D --> E["감사 로그에 남아 뒤늦게라도 알아챈다"]
네 겹 중 하나만 있었다면 그 겹의 빈틈이 곧 사고입니다. 넷이 함께 있으면 입력 검증과 쿼리 방식이 둘 다 뚫려야 공격 명령이 실행됩니다. 실행된 뒤에도 계정 권한이 피해를 좁힙니다. 지나간 공격은 기록에 남아 뒤늦게라도 드러납니다.
막는 겹과 뚫린 뒤에 일하는 겹
앞 소절의 네 겹은 하는 일이 서로 다릅니다. 막는 겹이 있습니다. 뚫린 뒤에 일하는 겹도 있습니다. 아래 표는 겹을 하는 일로 나눈 것입니다.
| 겹이 하는 일 | 무엇을 하나 | 예 |
|---|---|---|
| 막기 | 공격이 들어오지 못하게 한다 | 방화벽 · 로그인 · 입력 검증 · 매개변수화 쿼리 |
| 줄이기 | 뚫린 뒤 피해가 번지는 범위를 좁힌다 | 계정 권한 좁히기 · 서버 구역 나누기 · 저장한 데이터 암호화 |
| 알아채기 | 뚫린 것을 알아낸다 | 감사 로그 · 이상 징후 알림 |
| 되살리기 | 망가진 것을 되돌린다 | 백업 |
표의 네 줄 가운데 세 줄은 뚫린 다음에 일합니다. 막는 겹만 쌓으면 뚫린 다음이 비어 있습니다. 심층 방어는 언젠가 뚫린다고 보고 출발하므로 그 뒤를 맡는 겹도 함께 둡니다.
줄이기 칸의 서버 구역 나누기는 네트워크 분할이라고 부릅니다. 서버들을 여러 구역으로 나눠서 한 구역이 뚫려도 다른 구역으로 쉽게 못 건너가게 합니다.
겹은 소프트웨어 장치만이 아닙니다. 운영 서버에 접속하려면 동료의 승인을 받게 하는 절차도 한 겹입니다. 알려진 취약점을 고친 판으로 제때 바꾸는 패치 작업도 한 겹입니다. 보안에서는 사람과 기술과 운영을 모두 겹으로 셉니다.
겹을 쌓는 기준
겹을 몇 개나 둘지는 두 가지로 정합니다. 지키는 것의 값과 겹 하나에 드는 품입니다.
비밀번호나 결제 정보를 다루는 서비스는 뚫렸을 때 잃는 것이 큽니다. 이런 서비스는 겹을 많이 둡니다. 사내에서 잠깐 쓰는 도구는 겹 몇 개로 그치기도 합니다. 겹마다 만들고 고치고 지켜보는 품이 들기 때문입니다.
새 겹을 더할 때는 그 겹이 기존 겹과 다른 빈틈을 막는지 봅니다. 같은 방식을 한 번 더 두는 겹은 품만 늘립니다. 막는 힘은 거의 안 늘어납니다.
겹이 늘 때 잃는 것
이 소절은 겹이 늘 때 잃는 것 넷을 봅니다.
첫째는 품입니다. 겹마다 설정하고 고치고 지켜봐야 합니다. 규칙이 여러 겹에 흩어지면 한 곳을 바꿀 때 다른 겹도 함께 살펴야 합니다.
둘째는 속도입니다. 요청은 겹마다 검사를 받습니다. 검사 하나하나는 짧아도 쌓이면 응답이 늦어집니다.
셋째는 원인 찾기입니다. 정상 요청이 막히면 어느 겹이 막았는지부터 찾아야 합니다. 겹마다 로그가 따로 쌓이면 이 일이 길어집니다.
넷째는 겹끼리 기대는 버릇입니다. 뒤에 겹이 더 있다며 겹마다 검사를 느슨하게 두면 겹은 많아도 빈틈이 줄지 않습니다. 그래서 각 겹은 혼자 서 있는 것처럼 짓습니다.
이웃한 원칙과 가르는 선
심층 방어와 자주 나란히 불리는 원칙이 셋 있습니다. 이 소절은 각각과 어디서 갈리는지만 짚습니다.
경계 보안은 바깥 벽 한 겹을 튼튼히 쌓고 벽 안쪽은 믿는 설계입니다. 심층 방어는 그 벽이 뚫린 뒤를 따로 대비한다는 점에서 갈립니다.
제로 트러스트는 안쪽 네트워크에서 온 요청도 믿지 않습니다. 요청마다 누구인지 다시 확인합니다. 심층 방어는 겹을 여럿 두라는 원칙입니다. 제로 트러스트는 네트워크 안쪽에 있다는 사실을 믿을 근거로 쓰지 말라는 원칙입니다.
공격 표면은 공격이 들어올 수 있는 지점을 모두 모은 것입니다. 표면을 좁히는 일은 문의 수를 줄입니다. 심층 방어는 남은 문 뒤에 겹을 쌓습니다. 둘은 서로를 대신하지 못합니다.
관련 항목
심층 방어가 겹으로 쌓는 보안 장치
방화벽 · 웹 애플리케이션 방화벽 · 인증 · 인가 · 다중 인증 · 입력 검증 · 매개변수화 쿼리 · 출력 인코딩 · 암호화 · 네트워크 분할 · 샌드박스 · 패치
심층 방어에서 뚫린 뒤를 맡는 장치
최소 권한 · 격리 · 감사 로그 · 침입 탐지 · 백업과 복구 · 사고 대응
심층 방어와 짝을 이루거나 맞서는 보안 원칙
경계 보안 · 제로 트러스트 · 공격 표면 · 권한 분리 · 기본 차단 · 안전한 기본값 · 단일 장애점
심층 방어가 막으려는 공격
SQL 인젝션 · 크로스 사이트 스크립팅 · 권한 상승 · 제로데이 · 자격 증명 탈취 · 측면 이동
심층 방어를 정리하고 퍼뜨린 분야와 기관
다른 이름: defense in depth · defence in depth · 다층 방어 · 종심 방어