security

웹 애플리케이션 취약점 완전정복 가이드: 인젝션·XSS·IDOR·SSRF부터 비즈니스 로직 결함까지 방어 원리

방화벽도 TLS도 막지 못하는 정문 공격, 애플리케이션 스스로 막는 법

2026년 06월 03일
웹보안 OWASP SQL인젝션 XSS IDOR SSRF 접근제어 비즈니스로직 프레임워크보안 취약점패치
2분 읽기

방화벽을 아무리 촘촘히 세워도, SSH를 아무리 단단히 잠가도, 웹 서비스를 운영하는 한 80번과 443번 포트는 열어 둘 수밖에 없습니다. 그리고 공격은 바로 그 열린 문으로, 정상 요청과 똑같은 얼굴을 하고 들어옵니다.

웹 서비스를 운영한다는 것은, 누구나 두드릴 수 있는 문 하나를 영원히 열어 둔다는 뜻입니다. 인프라 단의 방어 — 닫힌 포트, 강화된 SSH, TLS 암호화, 보안 헤더 — 는 모두 중요하지만, 그 어느 것도 웹 애플리케이션 자체의 결함은 막아 주지 못합니다. 방화벽은 443번 포트로 들어오는 악성 요청과 정상 요청을 구별하지 못하고, TLS는 그 안에 담긴 공격까지 친절히 암호화해 안전하게 전달할 뿐입니다.

이 글은 웹 애플리케이션을 노리는 주요 취약점들을 다룹니다. 단순히 "이런 게 위험하다"를 나열하는 대신, 각 취약점이 왜 발생하고 어떤 원리로 막아야 하는지에 집중합니다. 그리고 자동 스캐너가 절대 잡아내지 못하는 영역 — 비즈니스 로직 결함 — 과, 실제 유명 프레임워크에서 터졌던 사고의 교훈까지 짚어 보겠습니다. 공격 기법이 아니라 방어 설계가 목표입니다.

들어가기 전에: OWASP 세 가지만 알아 두기

본문에서 계속 등장할 참고 자료가 있습니다. OWASP(Open Worldwide Application Security Project)는 웹 보안 지식을 무료로 공개하는 비영리 단체이고, 실무자가 알아 둘 핵심 자료는 세 가지입니다.

자료 한 줄 정리 언제 보나
OWASP Top 10 가장 위험한 웹 취약점 10가지 어디서부터 봐야 할지 모를 때 출발점
OWASP ASVS 항목별 보안 검증 체크리스트 "우리 앱이 안전한가"를 점검할 때
OWASP Cheat Sheet Series 취약점별 "코드를 어떻게 짤까" 치트시트 실제로 코드를 고칠 때

개발자라면 세 번째 치트시트는 지금 바로 북마크해 두길 권합니다. 아래에서 다루는 거의 모든 항목은 결국 Top 10의 한 줄을 풀어 쓴 것입니다.

인젝션: 데이터를 명령으로 착각하게 만드는 고전

웹 취약점의 원조이면서, 수십 년이 지난 지금도 여전히 가장 위험한 축에 드는 것이 인젝션(injection)입니다. 다행히 본질 하나만 붙잡으면 SQL 인젝션, 명령 인젝션 등 여러 변종을 한꺼번에 이해할 수 있습니다.

한 문장으로 보는 본질

인젝션의 핵심은 이것입니다 — 애플리케이션이 사용자가 넣은 데이터를, 데이터가 아니라 명령의 일부로 잘못 읽게 만드는 것입니다. 사용자 입력이 데이터베이스 질의나 시스템 명령에 섞여 들어갈 때, 그 입력이 명령의 구조 자체를 바꿀 수 있다면 끝입니다. 공격자는 입력을 조작해 원래 의도에 없던 명령을 실행시킵니다.

가장 유명한 사례인 SQL 인젝션을 보면, 애플리케이션이 사용자 입력을 질의문에 그대로 끼워 넣을 때 공격자가 질의 구조를 바꾸는 문자열을 입력해 남의 데이터를 빼내거나 조작합니다. 명령 인젝션은 같은 원리를 시스템 명령에 적용한 것으로, 사용자 입력이 셸 명령에 섞여 임의 명령이 실행되는 경우입니다. 표적은 다르지만 메커니즘은 똑같습니다.

근본 방어: 데이터와 명령을 분리하라

인젝션을 원리적으로 막는 길은 단 하나, 데이터와 명령을 엄격하게 분리하는 것입니다. 사용자 입력은 언제나 "값"으로만 다뤄야 하고, 명령의 구조에는 절대 손대지 못하게 해야 합니다.

데이터베이스에서 이를 달성하는 표준 기법이 매개변수화된 질의(parameterized query), 다른 말로 준비된 문(prepared statement)입니다. 비유하자면, 질의문이라는 빈칸 양식을 먼저 고정해 두고, 사용자 입력은 정해진 빈칸에 값으로만 꽂아 넣는 방식입니다. 양식의 구조가 먼저 확정되므로, 입력이 아무리 교묘해도 구조를 흔들 수 없습니다. 그래서 SQL 인젝션이 원리적으로 차단됩니다.

요즘 프레임워크와 DB 라이브러리는 대부분 이 방식을 기본으로 제공합니다. 따라서 새 기술이 필요한 게 아니라, 이미 있는 안전장치를 우회하지 않고 제대로 쓰는 것이 핵심입니다. 정말 위험한 건 편의나 무지 때문에 사용자 입력을 질의 문자열에 직접 이어 붙이는 코드입니다.

그래서 지금 뭘 하면 되나. 코드에서 SQL 문자열을 + 연산이나 템플릿 문법으로 사용자 입력과 이어 붙이는 곳을 찾으세요. 거의 모든 SQL 인젝션이 바로 그 자리에서 납니다. 남의 코드를 넘겨받아 점검할 때도 가장 먼저 보는 곳이 이 "문자열로 이어 붙인 쿼리"입니다 — ORM이나 쿼리 빌더를 잘 쓰다가도 한두 군데만 급하게 raw 쿼리로 박아 둔 자리가 꼭 나옵니다. 언어·프레임워크별 정확한 작성법은 OWASP SQL Injection Prevention Cheat Sheet에 정리돼 있습니다.

시스템 명령의 경우, 애초에 사용자 입력을 셸 명령에 전달하는 설계 자체를 피하는 것이 최선입니다. 불가피하다면 입력을 엄격히 검증하고, 명령과 인자를 분리해 넘기는 안전한 호출 방식을 사용해야 합니다.

입력 검증은 보조 방어선일 뿐

데이터/명령 분리가 1차 방어라면, 입력 검증(input validation)은 어디까지나 보조선입니다. 사용자 입력이 예상한 형식과 범위에 맞는지 확인해 비정상 입력을 걸러내는 것이죠. 이때 "허용할 것만 통과시키는(allowlist)" 방식이 "알려진 나쁜 것만 막는(denylist)" 방식보다 안전합니다.

다만 검증만으로 인젝션을 완전히 막으려는 시도는 위험합니다. 우회 기법은 늘 새로 나오기 때문입니다. 반드시 데이터와 명령의 분리가 먼저이고, 입력 검증은 그 위에 한 겹 더 두르는 보조 수단이라는 순서를 기억하세요.

XSS: 노리는 건 서버가 아니라 다른 사용자의 브라우저

인젝션이 서버 측 명령을 노린다면, 교차 사이트 스크립팅(XSS)은 표적이 다릅니다. 바로 다른 사용자의 브라우저입니다.

어떻게 동작하나

발상은 이렇습니다. 공격자가 웹 페이지에 악성 스크립트를 심어 두고, 그것이 피해자의 브라우저에서 실행되게 만드는 것입니다.

게시판이 있는 사이트를 떠올려 보세요. 공격자가 글 본문에 스크립트를 숨겨 작성합니다. 사이트가 이 글을 다른 사용자에게 보여 줄 때 스크립트를 걸러내지 않으면, 그 글을 여는 모든 방문자의 브라우저에서 공격자의 코드가 돌아갑니다. 이걸로 세션 탈취, 페이지 변조, 사용자 행동 조작, 정보 유출까지 가능합니다.

결국 XSS의 본질은 인젝션과 쌍둥이입니다. 피해자의 브라우저가 공격자가 주입한 데이터를 실행 가능한 코드로 착각하는 것 — 데이터가 코드로 오인되는 똑같은 패턴입니다.

근본 방어: 출력 인코딩, 그리고 맥락

XSS를 막는 핵심은 출력 인코딩(output encoding)입니다. 사용자 입력을 페이지에 출력할 때, 그것이 코드로 해석되지 않고 그냥 텍스트로만 보이도록 변환하는 것이죠. 스크립트로 읽힐 수 있는 특수문자들을, 화면에는 똑같이 보이되 브라우저가 실행하지는 않는 형태로 바꿔 줍니다. 그러면 공격자가 스크립트를 욱여넣어도 실행되지 않고 그저 글자로 표시될 뿐입니다.

여기서 결정적인 게 맥락(context)입니다. 데이터가 어디에 출력되느냐 — HTML 본문인지, 태그 속성값인지, <script> 내부인지, URL인지 — 에 따라 필요한 인코딩이 전부 다릅니다. 현대 프레임워크는 맥락에 맞는 인코딩을 대부분 자동으로 해 주므로, 이번에도 핵심은 그 자동 방어를 우회하지 않는 것입니다.

위험한 자리는 명확합니다. 프레임워크가 켜 둔 자동 이스케이프를 일부러 끄거나, 원시 HTML을 그대로 꽂는 기능을 부주의하게 쓰는 곳입니다. 구체적으로 이런 것들입니다.

프레임워크 위험한 출력 방식 안전한 기본 방식
React dangerouslySetInnerHTML JSX 중괄호 {value}
Vue v-html {{ value }} (머스태시)
Laravel Blade {!! $value !!} {{ $value }} (자동 이스케이프)

이름에 굳이 "dangerous"가 붙은 데는 이유가 있습니다. 이런 자리에 사용자 입력이 흘러 들어가면 자동 방어가 통째로 풀립니다. 맥락별 인코딩 규칙과, 정말 HTML을 그대로 받아야 할 때 쓰는 살균(sanitization) 방법은 OWASP Cross Site Scripting Prevention Cheat Sheet에 정리돼 있습니다.

2차 방어선으로서의 CSP

출력 인코딩이 1차 방어라면, CSP(Content Security Policy)는 그것이 뚫렸을 때를 대비한 2차 방어선입니다. 어떤 경로로든 스크립트 주입에 성공하더라도, CSP가 "허용되지 않은 출처의 스크립트는 실행 금지"를 강제하면 공격은 무력화됩니다. 출력 인코딩과 CSP를 함께 두르는 것이 바로 깊이 방어(defense in depth)의 모범 사례입니다. 한 겹이 실패해도 다음 겹이 받아 내는 구조죠.

인증과 인가: "누구인가"와 "무엇을 할 수 있는가"

영향력으로만 따지면 웹 취약점 중 가장 무거운 부류가 여기 있습니다. 그런데 출발은 의외로 단순한 용어 구분에서 시작됩니다.

인증과 인가는 전혀 다른 일이다

두 단어를 또렷이 구분해야 합니다.

  • 인증(authentication) = "당신이 누구인가"를 확인. → 로그인이 인증입니다.
  • 인가(authorization) = "당신이 이 일을 할 자격이 있는가"를 확인. → 특정 자원 접근 권한 확인이 인가입니다.

수많은 취약점이 이 둘을 헷갈리거나, 인증은 했는데 인가를 빠뜨리는 데서 터집니다. 사용자가 로그인은 했지만(인증 OK), 막상 접근하려는 그 자원에 권한이 있는지(인가)를 확인하지 않으면, 한 사용자가 다른 사용자의 자원을 들여다보게 됩니다.

접근 제어 붕괴: 현장에서 제일 자주 만나는 결함

이 부류의 대표 선수가 안전하지 않은 직접 객체 참조(IDOR, Insecure Direct Object Reference)입니다. 예를 들어 /order/1001 주소에서 숫자만 1002로 바꿨더니 남의 주문서가 그대로 떠 버리는 경우입니다. 식별자만 바꾸면 타인의 자원이 노출되는 것이죠.

원인은 단순합니다. 서버가 "이 사용자가 로그인했는가"만 보고, "이 사용자가 바로 이 자원에 접근할 권한이 있는가"는 확인하지 않은 것입니다. 화려한 공격 기법이 아니라, 그냥 서버에서 소유권 한 줄을 빼먹은 자리입니다. 우습게 들리겠지만, 실제 점검 현장에서 가장 흔하게 마주치는 결함이 바로 이 IDOR입니다.

더 심각한 건 권한 수준 자체가 무너지는 경우입니다. 일반 사용자가 관리자 기능에 접근하거나, 자기 권한을 임의로 끌어올릴 수 있는 상황이죠. 멀리 갈 것도 없습니다. 협업 도구 Confluence에서 터진 CVE-2023-22515(2023년)가 정확히 이 권한 상승 결함이었고, 인증되지 않은 외부인이 관리자 계정을 만들 수 있었습니다. 수많은 기업이 쓰는 거대 상용 제품조차 이렇게 뚫립니다.

기억해 둘 사실 하나. 접근 제어 붕괴(Broken Access Control)는 OWASP Top 10에서 수년째 1위를 지키고 있는, 가장 흔하고 가장 영향이 큰 부류입니다.

근본 방어: 모든 접근에, 매번, 서버에서

원리는 한 줄로 요약됩니다 — 모든 자원 접근에 대해, 매번, 서버 측에서 인가를 확인하라. 다만 실무에서 자주 무너지는 지점이 있어 셋으로 나눠 강조합니다.

1. 인가는 반드시 서버 측에서. 브라우저에서 메뉴를 숨기거나 버튼을 비활성화하는 것은 인가가 아닙니다. 공격자는 그런 클라이언트 측 제약을 가볍게 우회하고 서버에 직접 요청을 던질 수 있습니다. 진짜 통제는 서버에만 존재할 수 있습니다.

2. 기본은 거부(deny by default). 명시적으로 허용한 것만 통과시키고 나머지는 전부 막아야 합니다. 반대로 "기본 허용 + 예외만 차단" 방식을 쓰면, 빠뜨린 한 곳이 그대로 구멍이 됩니다.

3. 최소 권한 원칙. 각 사용자는 자기 역할에 꼭 필요한 최소한의 권한만 가져야 합니다.

세션과 인증 자체의 안전

인가뿐 아니라 인증 자체의 안전도 챙겨야 합니다. 로그인 이후 사용자를 식별하는 세션이 안전하게 관리돼야 합니다. 세션 식별자가 예측 가능하거나, 쉽게 탈취되거나, 제때 만료되지 않으면 공격자가 남의 세션을 가로챌 수 있습니다. 또한 다단계 인증(MFA)은 인증의 안전을 크게 끌어올립니다. 비밀번호가 유출되어도 두 번째 요소가 마지막 빗장을 걸어 주기 때문입니다.

SSRF: 서버를 속여서 내부를 치게 만들기

비교적 최근에 주목받기 시작했지만 파괴력은 만만치 않은 취약점이 SSRF(Server-Side Request Forgery, 서버 측 요청 위조)입니다. 특히 클라우드 환경에서는 작은 결함 하나가 전체 침해로 번질 수 있습니다.

왜 위험한가

많은 웹 애플리케이션에는 사용자 요청에 따라 서버가 다른 곳으로 요청을 보내는 기능이 있습니다. 사용자가 입력한 URL의 내용을 가져오거나, 외부 서비스를 호출하는 식이죠. SSRF는 이 기능을 악용해 공격자가 서버로 하여금 의도하지 않은 목적지로 요청을 보내게 만드는 것입니다.

핵심은 서버의 위치입니다. 서버는 외부 사용자가 직접 닿을 수 없는 내부 자원에 접근할 수 있는 경우가 많습니다. 공격자가 서버를 속여 내부 네트워크의 시스템, 내부 전용 서비스, 또는 클라우드의 민감한 내부 주소로 요청을 던지게 하면, 외부에서는 절대 닿지 못하던 내부를 서버를 우회 경로 삼아 간접 공격할 수 있습니다. 인터넷을 향한 포트를 전부 닫아 두었어도, SSRF는 서버 자신을 발판으로 삼기 때문에 그 방어를 통째로 우회합니다.

클라우드 메타데이터: SSRF가 치명적이 되는 지점

SSRF가 클라우드에서 특히 무서운 이유가 있습니다. 많은 클라우드 환경에는 인스턴스가 자기 정보를 조회할 수 있는 내부 전용 메타데이터 주소가 있고, 이 메타데이터에는 종종 그 인스턴스의 자격 증명 — 클라우드 자원에 접근하는 강력한 열쇠 — 이 들어 있습니다.

공격자가 SSRF로 서버를 속여 이 메타데이터 주소에 접근하게 만들면, 인스턴스의 클라우드 자격 증명을 통째로 탈취할 수 있습니다. 그리고 그 열쇠로 클라우드 환경 전체를 장악할 수 있습니다. 사소해 보이는 SSRF 하나가 클라우드 전면 침해로 직결되는 것입니다.

방어는 여러 겹으로

SSRF 방어는 한 가지로 끝나지 않고 여러 겹을 쌓아야 합니다.

  1. 가능하면 사용자 입력으로 서버가 임의 주소에 요청을 보내는 기능 자체를 없애거나 제한합니다.
  2. 불가피하다면 서버가 요청할 수 있는 목적지를 허용 목록으로 엄격히 제한합니다. 내부 주소와 메타데이터 주소로의 요청은 차단합니다.
  3. 클라우드에서는 메타데이터 접근에 강화된 보호 방식을 적용하고, 인스턴스 권한을 최소화해 둡니다. 그러면 설령 자격 증명이 새어도 피해 범위가 제한됩니다.

그래서 지금 뭘 하면 되나. "URL을 입력하면 미리보기를 보여 준다", "외부 이미지 주소를 가져와 저장한다" 같은 기능이 있다면 SSRF 후보입니다. 그 기능이 보낼 수 있는 주소를 꼭 필요한 외부 도메인으로만 좁히고(허용 목록), 사설망 대역과 클라우드 메타데이터 주소로는 절대 나가지 못하게 막으세요. AWS를 쓴다면 인스턴스 메타데이터를 IMDSv2로 전환하는 것이 메타데이터 탈취형 SSRF에 대한 표준 완화책입니다. 검증 우회 기법까지 포함한 방어 체크리스트는 OWASP Server Side Request Forgery Prevention Cheat Sheet를 참고하세요.

역직렬화와 API: 신뢰할 수 없는 데이터를 함부로 풀지 말 것

안전하지 않은 역직렬화

다소 기술적이지만 무게가 만만치 않은 취약점이 안전하지 않은 역직렬화(insecure deserialization)입니다. 먼저 용어부터. 직렬화는 객체(프로그램의 데이터 구조)를 저장하거나 전송할 수 있는 형태로 변환하는 것이고, 역직렬화는 그 반대 과정입니다.

문제는 애플리케이션이 신뢰할 수 없는 출처(사용자 입력 등)의 직렬화된 데이터를 역직렬화할 때 생깁니다. 그 처리 과정이 안전하지 않으면, 공격자가 조작된 데이터를 흘려보내 의도하지 않은 동작 — 심한 경우 임의 코드 실행 — 을 유발할 수 있습니다.

방어 원리는 깔끔합니다. 신뢰할 수 없는 데이터의 역직렬화는 피하고, 불가피하다면 무결성 검증과 안전한 방식을 쓰며, 역직렬화 과정에서 위험한 동작이 일어나지 않게 제한합니다. 실무에서 가장 안전한 길은, 객체를 통째로 직렬화/역직렬화하는 위험한 포맷 대신 JSON처럼 단순한 데이터 형식만 주고받는 것입니다. 언어별 안전한 처리법은 OWASP Deserialization Cheat Sheet에 정리돼 있습니다.

API 보안: 화면에서 가렸다고 숨겨진 게 아니다

요즘 웹은 API를 중심으로 돌아가는 경우가 많습니다. 그런데 API도 엄연히 웹 애플리케이션의 일부이고, 앞에서 다룬 모든 취약점(인젝션, 인가 누락, SSRF 등)에 똑같이 노출됩니다.

특히 API는 화면(UI) 없이 데이터를 직접 주고받는 경우가 많아 두 가지 함정에 빠지기 쉽습니다. 인가 확인이 누락되기 쉽고, 과도한 데이터를 그대로 노출하기 쉽습니다. 가장 흔한 착각이 "프론트엔드가 화면에 안 보여 주니 안전하다"는 것입니다. 천만에요 — API 응답은 브라우저 개발자 도구로 누구나 그대로 들여다볼 수 있습니다. 화면에서 가렸다고 데이터가 사라진 게 아닙니다.

따라서 API의 모든 엔드포인트에도 앞의 인가 원칙(매번, 서버에서, 기본 거부)을 철저히 적용해야 합니다. 그리고 응답에 필요 이상의 데이터를 담지 않도록 주의해야 합니다. 특히 비밀번호 해시, 내부 식별자, 권한 플래그 같은 필드가 응답에 슬쩍 새어 나가지 않는지 반드시 확인하세요. REST API의 보안 점검 항목은 OWASP REST Security Cheat Sheet에 정리돼 있습니다.

비즈니스 로직 결함: 스캐너가 절대 못 잡는 영역

이제 가장 미묘하고 가장 자주 간과되는 부류로 넘어갑니다. 비즈니스 로직 결함(business logic flaw)입니다.

무엇이 근본적으로 다른가

앞에서 다룬 취약점들 — 인젝션, XSS, SSRF 등 — 은 모두 기술적 결함입니다. 코드의 특정 패턴이 문제이므로 자동화 도구가 어느 정도 탐지할 수 있습니다. 웹 취약점 스캐너가 잡아내는 것이 대부분 이런 부류입니다.

비즈니스 로직 결함은 성격이 다릅니다. 이건 코드의 기술적 버그가 아니라 애플리케이션의 논리 자체에 난 구멍입니다. 코드는 설계된 그대로 정확히 동작하는데, 그 설계 자체에 허점이 있는 경우죠. 그래서 스캐너가 절대 못 잡습니다. 스캐너는 "이 코드가 안전한가"는 점검해도, "이 비즈니스 규칙이 악용될 수 있는가"는 이해하지 못하기 때문입니다.

흔한 패턴들

말로 설명하기보다 구체적 형태로 보는 게 빠릅니다. 비즈니스 로직 결함은 대략 이런 모습으로 나타납니다.

  • 단계 건너뛰기 — 정해진 순서대로 진행돼야 할 절차에서 중간 단계를 건너뛰어 의도하지 않은 상태에 도달. 예: 결제를 완료하지 않고도 "결제 완료" 상태에 이르는 경로.
  • 한쪽만 한 검증 — 수량이나 금액 검증이 한 방향으로만 되어 있어 음수나 비정상 값으로 엉뚱한 결과를 얻음. 예: 음수 수량을 넣었더니 잔액이 늘어남.
  • 반복 사용·한도 우회 — 한 번만 써야 할 것을 여러 번 쓰거나 정해진 한도를 우회. 예: 일회용 할인을 반복 적용하는 경로.
  • 소유권 가정 붕괴 — 자기 것만 수정할 수 있어야 하는데 논리적 허점으로 남의 것을 수정. (앞서 본 접근 제어 문제와도 맞닿습니다.)

이 모든 사례의 공통점은, 각 단계의 코드는 저마다 "정상"이지만, 그것들이 예상치 못한 순서로 조합될 때 논리가 무너진다는 것입니다.

방어: 결국 사람의 사고가 필요하다

비즈니스 로직 결함 방어는 자동화할 수 없습니다. 사람의 사고를 요구합니다.

핵심은 기능을 설계하고 검토할 때 "악의적인 사용자라면 이걸 어떻게 악용할까"를 상상하는 것입니다. 정상 흐름만 머릿속에 그리지 말고, 비정상 입력, 예상 밖의 순서, 경계값, 동시 실행 같은 공격자의 관점에서 그 논리를 시험해 봐야 합니다. 모든 중요한 동작에 대해, 그것이 일어나야 할 조건이 실제로 충족됐는지를 서버에서 엄격히 확인해야 합니다.

이것은 위협 모델링의 정신을 기능 단위로 적용하는 일입니다. 또한 비즈니스 로직 결함은 종종 다른 약점과 엮여 큰 침해로 번집니다. AI 코드 검토 같은 도구가 기술적 취약점을 찾는 데는 도움이 되지만, 비즈니스 로직 결함만큼은 그 애플리케이션의 의도를 깊이 이해하는 사람의 검토가 특히 중요합니다.

프레임워크와 의존성: "최신이라 안전하다"는 착각

마지막으로, 공급망 위험이 웹 애플리케이션에서 어떻게 구체화되는지 짚습니다.

프레임워크 자체도 취약점을 가진다

현대 웹 애플리케이션은 거의 다 프레임워크 위에서 만들어집니다. 프레임워크는 매개변수화된 질의, 출력 인코딩, 인증 체계 같은 보안 기능을 기본으로 제공해 개발자를 돕습니다. 이건 분명한 장점입니다.

그러나 프레임워크도 결국 소프트웨어이고, 따라서 취약점을 가질 수 있습니다. 게다가 널리 쓰이는 프레임워크일수록 더 많은 연구자와 공격자의 눈길을 받기 때문에, 취약점이 발견되고 공개되는 일이 주기적으로 일어납니다. 인기 프레임워크에서 심각한 취약점이 공개되면 그것을 쓰는 수많은 사이트가 동시에 위험에 노출됩니다. 공격자는 공개된 취약점을 빠르게 무기화해, 해당 프레임워크를 쓰는 사이트들을 대량으로 훑습니다.

패치를 따라가지 못하면 무너진다

여기서 핵심 교훈. 최신의 현대적 프레임워크를 쓴다고 안전이 보장되는 게 아닙니다. 어떤 프레임워크에도 취약점은 발견되며, 정말 중요한 건 최신 패치 상태를 유지하는 것입니다. 보안 패치가 나오면 신속히 적용해야 합니다. 오히려 활발히 개발되는 프레임워크일수록 패치도 자주 나오므로, 그 패치를 부지런히 따라가는 성실함이 필요합니다.

실제로 널리 쓰이는 프레임워크에서 원격 코드 실행(RCE) 같은 치명적 취약점이 공개된 사례는 반복돼 왔습니다. 최근 몇 해의 사례만 봐도 충분합니다.

  • Spring4Shell(CVE-2022-22965) — 자바 진영에서 가장 널리 쓰이는 Spring 프레임워크의 RCE. 공개되자마자 공격자들이 인터넷을 훑으며 대량으로 노렸습니다.
  • Log4Shell(CVE-2021-44228) — 거의 모든 자바 애플리케이션이 의존하는 로깅 라이브러리 Log4j의 RCE. "n-day의 교과서"라 불릴 만큼, 패치가 나온 뒤에도 수년간 악용이 이어졌습니다. 눈에 안 보이는 간접 의존성이 왜 무서운지 보여 준 사례이기도 합니다.
  • Laravel Ignition(CVE-2021-3129) — PHP 프레임워크 Laravel의 디버그 화면(Ignition)에서 비롯된 RCE. 운영 서버를 APP_DEBUG=true로 켜 둔 환경이 특히 위험했습니다. 디버그 모드 노출이 어떻게 실제 침해로 이어지는지의 생생한 예입니다.

이런 취약점이 공개되면 패치하지 않은 사이트는 곧바로 자동화된 대량 공격의 표적이 됩니다. 패치가 나와 있는데도 적용하지 않아 뚫리는 n-day의 위험 — 이것이 프레임워크에서 가장 빈번하게 현실화됩니다. 어떤 취약점이 "이론"이 아니라 "지금 실제로 악용되고 있는지"는 CISA의 KEV(악용 취약점 목록)에서 확인할 수 있습니다. 여기 오른 항목은 최우선으로 패치해야 합니다 — 위에 든 셋은 모두 이 목록에 올랐던 것들입니다.

방어 원리와 자동화

방어의 골자는 이렇습니다. 자신이 쓰는 프레임워크와 버전을 정확히 파악하고(인벤토리), 눈에 안 보이는 간접 의존성까지 점검하며, 보안 패치가 나오면 신속히 적용하고, 이 점검과 패치를 일회성이 아니라 정기 루틴과 자동화로 만드는 것입니다. 그리고 프레임워크가 제공하는 보안 기능을 우회하지 말고 제대로 쓰는 것 — 이것이 이 글 전체를 관통하는 원칙입니다.

내 버전이 취약한지 어떻게 아나. 의존성 목록(예: composer.lock, package-lock.json)을 들고 다음을 조회하면 됩니다. 특정 패키지·버전이 영향받는지는 OSVGitHub Advisory Database에서, 취약점 상세와 심각도(CVSS)는 NVD에서 확인합니다. 더 편한 길은 자동화입니다 — npm audit, composer audit 같은 내장 명령이나 GitHub Dependabot을 켜 두면 취약한 의존성이 들어올 때 알려 줍니다. 한 번 점검하고 끝내지 말고, 이 알림을 상시로 켜 두는 것이 핵심입니다.

핵심 정리: 여섯 가지로 압축하기

지금까지의 내용을 여섯 줄로 줄이면 이렇습니다.

  1. 웹 애플리케이션은 인프라 방어를 통과한 공격이 들어오는 마지막이자 가장 넓은 전선이다. 방화벽도 TLS도 닫힌 포트도 웹 취약점을 막지 못한다. 정문으로 들어오는 공격은 애플리케이션 자신이 막아야 한다.
  2. 인젝션과 XSS의 본질은 같다. 데이터가 명령이나 코드로 착각되는 것이다. 방어의 핵심은 데이터와 명령의 분리(매개변수화된 질의)와 출력 인코딩이다. 프레임워크의 기본 보호를 우회하지 말 것.
  3. 인증과 인가는 다르며, 인가 누락이 큰 위험이다. 모든 자원 접근에 대해 매번, 서버 측에서, 기본 거부 원칙으로 인가를 확인하라. 클라이언트 측 제약은 인가가 아니다.
  4. SSRF는 서버를 발판으로 내부를 친다. 클라우드에서는 메타데이터 탈취로 전체가 넘어갈 수 있다. 요청 대상을 제한하고 최소 권한을 적용하라.
  5. 비즈니스 로직 결함은 스캐너가 못 잡는다. 코드는 정상이지만 논리에 허점이 있다. "악의적 사용자라면 어떻게 악용할까"를 상상하는 사람의 사고가 필요하다.
  6. 최신 프레임워크도 안전하지 않다. 모든 프레임워크에 취약점은 발견된다. 중요한 건 신속한 패치다.

바로 실행할 체크리스트

  • 인젝션 방어 — 매개변수화된 질의를 쓰는가? 사용자 입력을 질의문에 직접 잇는 곳은 없는가?
  • XSS 방어 — 출력 인코딩이 적용되는가? 프레임워크의 자동 인코딩을 우회하는 곳({!! !!}, v-html, dangerouslySetInnerHTML)은 없는가? CSP를 더했는가?
  • 접근 제어 — 모든 자원 접근에 서버 측 인가 확인이 있는가? 기본 거부 원칙을 따르는가? IDOR로 식별자만 바꿔 남의 자원이 보이지는 않는가?
  • SSRF — 서버가 임의 주소로 요청을 보낼 수 있는가? 클라우드 메타데이터 접근이 보호되는가(AWS면 IMDSv2)?
  • 비즈니스 로직 — "악의적 사용자라면 어떻게 악용할까"를 기능별로 검토했는가? (스캐너는 이걸 못 잡는다.)
  • 의존성 — 프레임워크와 의존성을 최신 패치 상태로 유지하는가? npm audit / composer audit / Dependabot 알림이 켜져 있는가?
  • 도구 검증 — 자신의 웹앱을 OWASP ZAP으로 점검하고, OWASP ASVS로 빠진 항목을 대조했는가?

마지막 항목을 조금 더 풀어 두겠습니다. 위 항목들을 사람의 눈으로만 점검하기는 벅찹니다. 자신의 웹앱을 대상으로 OWASP ZAP(무료 오픈소스 웹 취약점 스캐너)을 돌려 보고, 더 깊이 보려면 Burp Suite를 씁니다. 둘 다 브라우저와 서버 사이의 요청을 가로채 들여다보는 프록시 도구라, "서버에 직접 요청을 보내면 인가가 뚫리나"를 손수 확인하기에 좋습니다. 단, 반드시 본인 소유이거나 명시적으로 허가받은 대상에만 사용하세요. 그리고 빠진 항목이 없는지 OWASP ASVS 체크리스트로 대조하면 마무리입니다.

자주 묻는 질문 (FAQ)

Q. 방화벽과 TLS를 제대로 설정했는데도 웹 애플리케이션 보안이 따로 필요한가요? 네, 반드시 필요합니다. 방화벽은 포트를 여닫을 뿐, 정당하게 열린 443번 포트로 들어오는 악성 요청과 정상 요청을 구별하지 못합니다. TLS는 통신을 암호화할 뿐, 그 안에 담긴 공격까지 막아 주지는 않습니다 — 오히려 공격도 함께 암호화되어 안전하게 전달됩니다. 인젝션, XSS, 접근 제어 붕괴 같은 웹 취약점은 정상적인 웹 요청의 형태로 정문을 통해 들어오므로, 애플리케이션 자신이 막아야 합니다.

Q. SQL 인젝션을 막는 가장 확실한 방법은 무엇인가요? 입력 검증이 아니라 매개변수화된 질의(prepared statement)입니다. 질의문의 구조를 먼저 고정해 두고 사용자 입력을 정해진 빈칸에 값으로만 꽂아 넣으면, 입력이 아무리 교묘해도 질의 구조를 바꿀 수 없어 인젝션이 원리적으로 차단됩니다. 대부분의 현대 프레임워크가 이 방식을 기본 제공하므로, 사용자 입력을 SQL 문자열에 +나 템플릿 문법으로 직접 이어 붙이는 코드만 없애면 됩니다. 입력 검증(allowlist)은 그 위에 더하는 보조 방어선입니다.

Q. IDOR가 정확히 무엇이고 왜 그렇게 흔한가요? IDOR(안전하지 않은 직접 객체 참조)는 /order/1001의 숫자만 1002로 바꿨더니 남의 주문서가 그대로 뜨는 것처럼, 식별자만 변경하면 타인의 자원에 접근되는 결함입니다. 원인은 서버가 "로그인했는가(인증)"만 확인하고 "이 사용자가 바로 이 자원에 접근할 권한이 있는가(인가)"를 빠뜨렸기 때문입니다. 화려한 공격 기법이 아니라 단지 서버에서 소유권 확인 한 줄을 누락한 것이라, 점검 현장에서 가장 자주 마주치는 실제 결함입니다. 접근 제어 붕괴는 OWASP Top 10에서 수년째 1위를 지키고 있습니다.

Q. 최신 프레임워크를 쓰면 보안 걱정을 덜 해도 되나요? 아닙니다. 모든 프레임워크는 소프트웨어이므로 취약점이 발견됩니다. 오히려 널리 쓰이는 프레임워크일수록 더 많은 공격자의 주목을 받습니다. Spring4Shell(CVE-2022-22965), Log4Shell(CVE-2021-44228), Laravel Ignition(CVE-2021-3129)처럼 인기 프레임워크에서 원격 코드 실행 취약점이 반복적으로 공개돼 왔습니다. 핵심은 최신 버전을 쓰는 것보다 최신 패치 상태를 유지하는 것입니다. npm audit, composer audit, Dependabot 같은 자동화 알림을 상시 켜 두세요.

Q. 자동 스캐너만 돌리면 웹 보안 점검이 끝나나요? 아닙니다. 스캐너(OWASP ZAP, Burp Suite 등)는 인젝션·XSS 같은 기술적 취약점은 잘 찾지만, 비즈니스 로직 결함은 절대 잡지 못합니다. 결제 단계 건너뛰기, 음수 수량으로 잔액 증가, 일회용 할인 반복 적용 같은 결함은 코드가 설계대로 정확히 동작하는데도 논리 자체에 허점이 있는 경우라, "악의적 사용자라면 어떻게 악용할까"를 상상하는 사람의 검토가 반드시 필요합니다.


원문 출처 및 더 알아보기

이 글은 (주)뎁팀 웹 보안팀이 운영하는 보안 가이드 사이트 SGAEPS(시그앱스)의 가이드를 바탕으로 재구성했습니다. 더 깊은 원문은 SGAEPS 가이드 원문에서 확인하실 수 있습니다.

웹 보안 점검·긴급 대응이 필요하시면 DevTeam의 웹 긴급지원 또는 개발/보안 문의를 이용해 주세요.

개발 파트너가 필요하신가요?

DevTeam은 MVP·웹·앱·AI 개발을 설계부터 배포·운영까지 한 팀이 책임집니다.

이 글 공유하기
Twitter LinkedIn
최종 수정: 2026년 06월 19일

security 관련 글

더 많은 스타트업 노하우와 비즈니스 인사이트를 확인해보세요

사람과 물리, 기술 너머의 공격면: 피싱·딥페이크·내부자·물리보...

방어는 가장 약한 고리만큼만 강하다 — 소셜 엔지니어링·내부자 위협·물리적 접근·서...

SSL/TLS 인증서 만료 방지 및 자동 갱신 실패 시 대응 방안

SSL/TLS 인증서가 만료되지 않도록 예방하고, 자동 갱신 실패 시 효과적인 해결 방법...

공격면 지도화 실전 가이드: 그림자 자산을 찾는 자가 점검법

nmap·CT 로그·민감 경로 점검으로 공격자보다 먼저 내 노출을 찾아내는 법

TLS 제대로 설정하기: TLS 1.2 이상만 허용하고 SSL Labs A+ 받...

자물쇠 아이콘 뒤를 단단하게 — 낡은 프로토콜 차단, 강한 암호 스위트, 인증서 자동...

인증서 체인 불완전 및 도메인 불일치 문제: 단계별 문제 해결...

인증서 체인 불완전 및 도메인 불일치 문제를 해결하는 간단한 단계별 가이드를 통해...

침해를 가정하라: 로깅·탐지·사고대응부터 랜섬웨어 막는 3-2-1...

완벽한 예방은 없다 — 체류 시간을 줄이는 탐지부터 랜섬웨어를 무력화하는 불변 백업...

전문가 도움이 필요하신가요?

스타트업과 비즈니스 성장을 위한 전문 컨설팅을 받아보세요.
확장 가능하고 비즈니스 성과로 이어지는 솔루션을 구축할 수 있도록 도와드립니다.