정보처리안전확보지원사 2024년 봄 오후 문제1 해설 ── JWT의 alg=none과 API 인가, WAF의 임시 대책

· 업데이트: · · 정보처리안전확보지원사, 등록 지원사, API, API 보안, JWT, 인증, 인가, WAF, Log4Shell, 정보보안, 취약점, IPA

수정 이력(1건, 최종 수정 2026년 09월 03일)

이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.

한국어 전면 재작성에 맞춰 본문 표현을 바로잡았습니다. 기술적인 주장은 일본어판과 같습니다.
최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.22176011)

아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.

Go Komura (2026). 「정보처리안전확보지원사 2024년 봄 오후 문제1 해설 ── JWT의 alg=none과 API 인가, WAF의 임시 대책」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/sc-exam-r6s-pm-q1-api-security/

DOI(등록된 아카이브)
10.5281/zenodo.22176011
DOI(마지막 등록 버전)
10.5281/zenodo.22176012

「JWT 서명을 검증하고 있으니 이용자 ID는 믿어도 된다」

이 말은 절반만 맞습니다.

정보처리안전확보지원사 시험 2024년도 봄 오후 문제1은 스마트폰에서 호출하는 API를 소재로 합니다1. 인증에 성공하면 JWT가 발급되고, 그 JWT를 붙여 이용자 정보 조회·갱신 API를 호출합니다. 언뜻 흔한 구성입니다.

그런데 진단에서는 다음 네 가지가 나옵니다.

  1. JWT 헤더의 algnone으로 바꾸면, 서명 없는 JWT가 통과합니다
  2. 올바른 JWT를 그대로 쓴 채 mid를 다른 이용자 ID로 바꾸면, 타인의 정보를 조회·갱신할 수 있습니다
  3. 사양에 없는 status=paid를 추가하면, 무료 이용자가 유료 이용자로 바뀝니다
  4. 메일로 도착하는 4자리 인증 코드를 제한 없이 무차별 대입할 수 있습니다

네 가지 모두 「인증 쪽 취약점」처럼 보이지만, 원인은 같지 않습니다. 토큰 무결성, 오브젝트 단위 인가, 프로퍼티 단위 인가, 시도 횟수 제한이라는 서로 다른 경계가 깨져 있습니다.

후반에는 또 다른 논점이 더해집니다. 널리 쓰이는 오픈소스 라이브러리에 JNDI Lookup을 악용해 외부에서 코드를 실행할 수 있는 중대한 취약점이 공개됩니다. 수정판도, 완성된 WAF 규칙도 아직 없습니다. 그동안 영향을 어떻게 확인하고, WAF로 어디를 보며, 왜 처음에는 「차단」이 아니라 「탐지」로 두는지를 묻습니다.

이 글에서는 공식 해답 예2와 채점 강평3을 바탕으로, 각 문항의 답뿐 아니라 왜 그 답이 되는지, 실무에서는 설계를 어디까지 강하게 잡아야 하는지까지 정리합니다.

문제 전체 구조인증 코드, JWT, API 인가, 라이브러리 취약점의 각 단계에서 깨지는 신뢰 경계를 나타냅니다시도 횟수 제한 없음alg=none 허용mid를 신뢰status=paidJNDI/LDAP/HTTP이용자 앱4자리 인증 코드JWT 발급이용자 API로그 출력취약한 라이브러리외부 코드 실행타인 데이터 조회/갱신과금 상태 변경

그림1: 문제 전체 구조. 각 단계에서 서로 다른 신뢰 경계가 깨집니다.

1. 먼저 결론

  • RESTful API가 세션을 갖지 않는 성질은 stateless입니다. 다만 서버가 데이터베이스나 이용자 상태를 전혀 갖지 않는다는 뜻은 아닙니다
  • 4자리 인증 코드는 1만 가지입니다. 1초에 10회면 평균 5,000회, 500초 만에 성공합니다. 유효 기간 10분보다 짧으므로 만료 시간만으로는 막을 수 없습니다
  • alg=none에 대한 최소 대책은 JWT 헤더의 algNONE이 아님을 확인하는 것입니다. 다만 실무에서는 허용 알고리즘을 서버 쪽에서 고정합니다
  • JWT가 올바르더라도 요청의 mid를 믿어서는 안 됩니다. JWT 안의 이용자 ID와 mid를 대조하거나, 더 안전하게 mid를 받지 않고 JWT에서 대상을 정합니다
  • status=paid 추가는 사양 밖 프로퍼티까지 내부 오브젝트에 묶는 Mass Assignment 문제입니다. 갱신용 DTO를 허용 목록으로 쓰고, 과금 상태는 이용자가 바꾸지 못하게 합니다
  • 무차별 대입 대책의 해답은 연속 실패 횟수가 임곗값을 넘으면 계정을 잠그는 처리입니다. 실무에서는 단계적 지연이나 발신지 단위 제어도 겹칩니다
  • 새 중대 취약점의 영향 확인에서는 파괴적 명령이 아니라, 테스트 서버의 index.html 접근을 기록해 외부 코드 실행 도달을 확인합니다
  • 공격 문자열은 HTTP 헤더에 들어가므로 WAF 검사 대상은 Header입니다. 대소문자 치환에 대응하는 정규식으로는 예를 들어 \W[jJ][nN][dD][iI]\W를 씁니다
  • WAF를 처음에 「탐지」로 두는 이점은 오탐으로 업무 통신이 차단되는 일을 막을 수 있다는 점입니다. 알림을 받으면 공격인지 정밀히 살피고, 조정 뒤 차단으로 옮깁니다
  • WAF는 임시 대책이며, 근본 대책은 영향받는 라이브러리를 수정판으로 업데이트하는 것입니다

그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 18건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle

2. 소재와 문항의 대응

문제의 무대는 헬스케어 서비스를 새로 제공하려는 G사입니다. 이용자는 스마트폰 앱에서 식사나 체중 등을 입력하고, 건강 위험 판정이나 식단 조언을 받습니다. 시스템은 클라우드 위에 구축되어 있으며, API 게이트웨이, 이벤트 구동 처리, 매니지드 데이터베이스를 조합합니다.

문제문의 제품명이나 서비스명은 추상화되어 있습니다. 이 글도 IPA의 도표나 문장을 전재하지 않고, 문항을 이해하는 데 필요한 구조만 바꿔 말합니다.

문항 주제 이 글의 장
문항1 RESTful API의 성질 4장
문항2(1) 4자리 코드의 무차별 대입 시간 5장
문항2(2) JWT의 alg=none 6장
문항2(3) mid를 이용한 타인 접근 7장
문항2(4) 사양 밖 status를 받아들이는 결함 8장
문항2(5) 무차별 대입 대책 9장
문항3(1) 안전한 방법으로 취약점 존재를 확인하기 11장
문항3(2)(3) WAF가 보는 위치와 정규식 12장
문항3(4) 탐지 모드의 이점과 운용 13장

채점 강평에서는 전체 정답률이 평균적이었다고 합니다. 한편 문항2(2)의 JWT 변조 대책과, 문항3(1)의 검증용 서버에 필요한 장치는 정답률이 다소 낮았다고 지적합니다. 둘 다 용어만 알아서는 풀 수 없습니다. 공격자가 어떤 값을 바꾸고, 그 값이 어떤 처리로 흘러가, 어디서 믿어졌는지를 따라가야 합니다.

3. 이 문제는 「인증 문제」 하나가 아닙니다

문제 전체를 신뢰 경계별로 늘어놓으면 다음과 같습니다.

[이용자 ID·비밀번호]
          |
          v
[4자리 코드 확인] ---- 시도 횟수 제한 없음 ----> 무차별 대입
          |
          v
[JWT 발급]
          |
          v
[JWT 라이브러리] ------- alg=none 허용 ------> 이용자 ID 변조
          |
          v
[이용자 API]
    |             |
    |             +-- status를 통째로 넘김 ----> 프로퍼티 단위 인가 결함
    |
    +-- mid를 신뢰함 -----------------------> 오브젝트 단위 인가 결함

[외부 입력을 로그에 기록]
          |
          v
[취약한 라이브러리] ---- JNDI/LDAP/HTTP ------> 외부 코드 실행

여기서 가장 중요한 것은 다음 구별입니다.

확인 묻는 것 이 문제에서 깨진 예
인증 당신은 누구인가 4자리 코드의 무차별 대입
토큰 검증 그 신원 정보가 변조되지 않았는가 alg=none
오브젝트 단위 인가 그 이용자의 데이터에 접근해도 되는가 mid 바꿔 치기
프로퍼티 단위 인가 그 항목을 바꿔도 되는가 status=paid
입력에서 실행으로의 경계 외부 입력이 명령으로 해석되지 않는가 JNDI Lookup

바로 앞 확인에 성공했다는 사실이, 다음 확인을 생략해도 되는 이유는 되지 않습니다. 올바른 JWT를 가진 이용자라도 타인의 데이터를 읽어도 되는 것은 아닙니다. 자기 데이터를 갱신할 수 있는 이용자라도 과금 상태까지 바꿔도 되는 것은 아닙니다.

이 단계 구분이 되면, 문항별 답이 암기가 아니게 됩니다.

인증과 인가의 차이인증이 주체를 확인하고, 인가가 그 주체에게 허용된 조작을 확인합니다인증누구인가인가무엇을 해도 되는가

그림2: 인증과 인가의 차이. 인증이 먼저이고, 인가는 별개의 확인입니다.

4. 문항1 ── stateless란 무엇인가

문항1은 RESTful API 설계 원칙 가운데 세션 관리를 하지 않는 성질을 묻습니다.

해답은 스테이트리스(stateless) 입니다.

stateless란, 서버가 바로 앞 요청의 대화 상태를 기억하지 않아도 각 요청만으로 처리에 필요한 정보가 갖춰지는 것입니다. 이 문제에서는 스마트폰 앱이 요청마다 JWT를 Authorization 헤더에 붙입니다. 서버는 그 JWT를 검증하고, 그 요청의 이용자를 식별합니다.

오해하기 쉬운 점은 stateless를 「서버는 상태를 전혀 갖지 않는다」고 읽는 것입니다. 실제로는 다음 상태는 보통 갖습니다.

  • 이용자 정보나 건강 데이터를 저장하는 데이터베이스
  • 과금 상태
  • 인증 코드 값, 유효 기간, 실패 횟수
  • JWT 서명 키
  • 폐기 목록을 쓰는 설계라면 그 폐기 정보
  • 로그와 감사 기록

갖지 않는 것은, 대화를 이어 가기 위한 서버 쪽 세션 상태를 각 API 호출의 전제로 두지 않는다는 뜻입니다.

또한 stateless라는 사실이 안전성을 자동으로 높이지는 않습니다. JWT를 매번 보내면 수평 분산은 쉬워지지만, JWT 검증을 잘못하면 그 잘못도 모든 노드에 고르게 퍼집니다. 아키텍처상의 성질과 보안상의 옳음은 별개입니다.

5. 문항2(1) ── 4자리 코드는 평균 500초에 맞습니다

인증 API는 이용자 ID와 비밀번호가 맞으면 메일로 4자리 숫자를 보냅니다. 그다음 이용자 ID와 4자리 코드가 일치하면 JWT를 발급합니다. 코드는 생성부터 10분간 유효합니다.

진단에서는 1초에 10회 시도가 가능했습니다. 평균 몇 초에 뚫리는지를 구합니다.

계산은 「후보 수의 절반」

4자리 숫자는 앞자리 0을 포함해 다음 1만 가지입니다.

0000, 0001, 0002, ... , 9999

정답이 균등하게 골라졌다면, 순서대로 중복 없이 시도하는 공격자가 정답에 도달하기까지의 평균 시도 횟수는 후보 수의 절반입니다.

평균 시도 횟수 = 10,000 ÷ 2 = 5,000회
평균 시간     = 5,000 ÷ 10회/초 = 500초

따라서 빈칸 b는 500 입니다.

최대로는 1,000초가 걸리지만, 문제가 묻는 것은 평균입니다. 그리고 코드 유효 기간은 600초이므로, 평균 돌파 시간 500초보다 깁니다. 이것이 「뚫릴 가능성이 높다」고 판단된 이유입니다.

4자리 인증 코드의 시간 감각1만 가지를 1초 10회로 시도하면 평균 5,000회, 500초 만에 맞으며, 유효 기간 600초보다 짧습니다평균 10,000 / 2 = 5,000회500초 < 600초후보 수 10,000가지평균 돌파 시간 500초유효 기간 600초유효 기간 안에 돌파 가능

그림9: 4자리 인증 코드의 시간 감각. 후보 수의 절반을 평균으로 시도하면 유효 기간 안에 맞습니다.

만료 시간만 줄여도, 후보가 적으면 집니다

인증 코드의 강도는 자릿수만으로도, 유효 기간만으로도 정해지지 않습니다.

유효 기간 동안 시도할 수 있는 횟수
= 1초당 시도 횟수 × 유효 기간
= 10 × 600
= 6,000회

중복 없는 값을 순서대로 시도하면, 1만 가지의 60%를 유효 기간 안에 확인할 수 있습니다. 만료 시간을 두었더라도 시도 횟수를 제한하지 않으면 충분하지 않습니다.

현행 NIST SP 800-63B는 대역외 인증에 쓰는 단기 비밀에 대해 적어도 6자리를 요구하고, 64bit 미만이면 시도 횟수 제한을 필수로 합니다. 또한 전자메일을 대역외 인증에 쓰지 말 것을 요구합니다4. 시험에서는 주어진 4자리·메일 전송이라는 사양 안에서 답하지만, 실무의 신규 설계에서는 그 전제 자체도 다시 봐야 합니다.

6. 문항2(2) ── alg=none은 「공격자에게 검증 방법을 고르게 한」 문제

문제의 JWT는 헤더, 페이로드, 서명의 세 부분으로 이루어집니다.

base64url(header).base64url(payload).base64url(signature)

헤더에는 서명에 쓰는 알고리즘으로 RS256이 기록되어 있었습니다. 페이로드에는 이용자 ID와 발행 시각, 유효 기간이 들어 있습니다.

진단자는 다음 두 가지를 바꿨습니다.

  1. 헤더의 algRS256에서 NONE으로 바꿉니다
  2. 페이로드의 이용자 ID를 다른 이용자로 바꿉니다

그 JWT를 보내면 검증이 성공해 다른 사람으로 위장할 수 있었습니다.

JWT alg=none 공격의 흐름올바른 JWT에서 alg를 none으로 바꾸고, 이용자 ID를 고쳐 써 통과시키는 흐름헤더의 alg를 none으로서명 검증을 건너뜀올바른 JWTalg=RS256user=user01변조 JWTalg=noneuser=user02서버가user02로 받아들임

그림3: JWT alg=none 공격의 흐름. 공격자가 검증 알고리즘을 고르고 있습니다.

none은 문자열 철자 실수가 아닙니다

RFC 7519에는 서명도 암호도 없는 JWT로서 algnone인 「Unsecured JWT」가 정의되어 있습니다5. 따라서 none이라는 값이 사양상 전혀 없는 것은 아닙니다.

문제는 서명 있는 JWT만 받아야 할 API가, 공격자가 지정한 none을 받아들인 것입니다.

취약한 처리를 개념적으로 쓰면 다음과 같습니다.

1. JWT 헤더를 읽는다
2. 헤더에 적힌 alg를 보고 검증 방법을 고른다
3. alg가 none이면 서명 검증을 하지 않는다
4. 페이로드의 이용자 ID를 믿는다

공격자가 제어하는 입력에서 보안 강도 자체를 고르게 하고 있습니다.

시험의 해답

문항은 수정 후 라이브러리 Q가 「어떤 데이터에 대해, 어떤 검증을 하는가」를 각각 20자 이내로 묻습니다.

해답 예는 다음과 같습니다.

항목 해답의 요점
검증 대상 데이터 JWT 헤더 안 alg에 지정된 값
검증 내용 NONE이 아님을 검증한다

문제문의 취약점에 대한 직접 수정으로는 이것으로 정답입니다.

실무에서는 「NONE이 아니면 된다」로 두지 않습니다

여기서는 시험 해답과 실무 권고를 나눌 필요가 있습니다.

RFC 8725는 JWT 라이브러리가 호출 측에 허용 알고리즘 집합을 지정하게 하고, 그 집합 밖을 써서는 안 된다고 합니다6. 즉 다음 사고방식입니다.

나쁜 사고방식:
  token.header.alg != "none" 이면 받아들인다

좋은 사고방식:
  serverConfig.allowedAlgorithms 에 포함된 경우만 받아들인다
  예: allowedAlgorithms = ["RS256"]

none만 거부해도, 다른 약한 알고리즘이나, 공개키 방식과 공통키 방식을 혼동하는 알고리즘 혼동이 남을 수 있습니다. 받아들이는 조건을 부정형으로 늘리는 것이 아니라, 허용 조건을 좁게 고정하는 것이 원칙입니다.

JWT 검증에서는 알고리즘뿐 아니라, 용도에 따라 적어도 다음을 확인합니다.

항목 확인할 것
서명 상정한 키와 알고리즘으로 검증할 수 있는가
iss 신뢰하는 발행자인가
aud 이 API를 위해 발급된 토큰인가
exp 유효 기간 안인가
nbf 이용 시작 시각보다 이전이 아닌가
sub 또는 이용자 ID 애플리케이션상의 유효한 주체인가
토큰 종류 ID 토큰과 액세스 토큰 등을 혼동하지 않았는가

이 문제에서 페이로드 키 이름은 user이지만, 실무에서는 표준 sub를 쓰거나 독자 클레임의 의미를 명확히 정의합니다.

안전한 JWT 검증과 위험한 JWT 검증위험한 검증은 alg에 의존하고, 안전한 검증은 서버 쪽 허용 목록을 씁니다안전한 검증서버 설정의 허용 알고리즘예: RS256JWT 헤더의 alg가허용 목록에 포함되는가 확인서명·iss·aud·exp를 검증위험한 검증JWT 헤더의 alg를 읽는다alg가 none이면 받아들인다

그림4: 안전한 검증과 위험한 검증. 실무에서는 허용 알고리즘을 좁게 고정합니다.

Base64url은 암호화가 아닙니다

JWT에서 흔히 생기는 오해가 하나 더 있습니다. 헤더와 페이로드는 base64url로 표현되지만, 이것은 암호화가 아닙니다. 누구든 디코드해 읽을 수 있습니다.

서명이 보장하는 것은, 올바르게 검증된 경우에 한해, 내용이 발행 뒤에 변조되지 않았다는 것입니다. 비밀로 해야 할 개인정보를 서명 있는 JWT 페이로드에 넣어도 된다는 뜻은 아닙니다.

7. 문항2(3) ── 올바른 JWT라도 mid를 바꾸면 타인을 읽었습니다

다음은 JWT 자체를 변조하지 않는 공격입니다.

이용자 API는 GET 또는 PUT으로 mid라는 이용자 ID를 받습니다. 공통 모듈 P는 그 mid에 묶인 이용자 정보를 데이터베이스에서 조회·갱신합니다.

공격의 구도는 단순합니다.

JWT의 이용자 ID: user01    ← 올바르게 서명된 JWT
요청의 mid: user02  ← 공격자가 변경

JWT 서명은 올바르므로 인증은 성공합니다. 그러나 API는 mid=user02를 그대로 믿고 user02의 정보를 반환합니다.

이것은 OWASP API Security Top 10 2023에서 말하는 Broken Object Level Authorization(BOLA) 에 해당하는 전형입니다. 이용자가 지정한 오브젝트 ID로 데이터에 접근할 때, 매번 그 오브젝트에 대한 인가를 확인해야 합니다7.

BOLA 공격올바른 JWT를 쓰면서 요청의 mid를 다른 이용자 ID로 바꿉니다JWT: user01mid: user02mid를 신뢰공격자이용자 APIDB에서 user02 정보를 반환

그림5: BOLA 공격. 인증은 통과하지만 인가를 확인하지 않습니다.

문항의 해답

표5의 밑줄 ②는 공통 모듈 P의 호출 처리에 추가하는 처리를 40자 이내로 묻습니다.

해답 예는 다음과 같습니다.

JWT에 포함된 이용자 ID가 mid 값과 일치하는지를 검증하는 처리

공통 모듈 P에서 검증하는 이점은 GET과 PUT 양쪽, 그리고 앞으로 P를 쓰는 다른 API에도 같은 인가를 걸기 쉽다는 점입니다. 각 화면이나 각 엔드포인트에 같은 비교 처리를 복사하면, 어딘가 한 곳에서 빠집니다.

더 안전한 설계는 mid를 받지 않는 것입니다

자기 정보만 조회·갱신하는 API라면, 클라이언트에서 이용자 ID를 받을 필요가 없습니다.

GET /users/me
Authorization: Bearer ***

서버 쪽에서는 검증이 끝난 JWT에서 주체를 꺼냅니다.

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

갱신도 같습니다.

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

비교 처리는 쓰면 막을 수 있습니다. 그러나 대상 ID를 외부에서 받지 않는 설계라면, 비교 처리를 빠뜨리는 종류의 버그 자체를 줄일 수 있습니다.

관리자가 다른 이용자 정보를 조작해야 한다면 다음과 같이 나눕니다.

PUT /users/me                  일반 이용자용
PUT /admin/users/{userId}      관리자용

관리자용에는 별도 권한, 감사 로그, 필요하면 재인증을 요구합니다. 「일반 이용자 API에 관리자인 경우만 예외를 더하는」 쪽보다 인가 정책의 경계가 보이기 쉽습니다.

BOLA를 막는 방법요청의 mid를 쓰지 않고, JWT 주체에서 대상을 정하거나 대조합니다GET /users/me + JWTJWT의 sub를 취득일치불일치mid 없음이용자APImid가 있으면sub와 일치하는가자기 데이터를 반환403 거부JWT의 sub로 DB 검색

그림6: BOLA를 막는 방법. mid를 받지 않거나, JWT 주체와 대조합니다.

인증과 인가를 한 문장으로 구별합니다

시험에서도 실무에서도 다음 표현이 도움이 됩니다.

  • 인증: 누구인가
  • 인가: 그 사람이 무엇을 해도 되는가

JWT 서명 검증이 성공한 것은 「이 토큰이 나타내는 주체를 믿어도 된다」는 지점까지입니다. 「그 주체가 user02를 읽어도 된다」는 따로 확인해야 합니다.

8. 문항2(4) ── status=paid는 프로퍼티 단위 인가 결함

이용자 API 사양에는 갱신용 파라미터로 다음이 정의되어 있습니다.

mid   이용자 ID
name  이름
age   나이

그런데 진단자는 사양에 없는 다음 값을 추가했습니다.

status=paid

그러자 무료 이용자의 상태가 유료 이용자로 바뀌었습니다.

문제문에 따르면 서비스 L은 받은 파라미터를 검증하지 않고 모두 공통 모듈 P로 넘기고 있었습니다. P는 그대로 데이터베이스를 갱신할 수 있는 구조였습니다.

빈칸 c의 해답은 공통 모듈 P 입니다.

Mass Assignment사양에 없는 status=paid가 추가되어 내부 오브젝트에 통째로 반영됩니다공격자가 status=paid를 추가자동 바인딩DB 저장API 사양mid / name / age요청 바디공통 모듈 P과금 상태가 paid로 변경

그림7: Mass Assignment. 사양 밖 프로퍼티가 내부 오브젝트에 통째로 반영됩니다.

BOLA와의 차이

앞 장의 mid 바꿔 치기와 이번 status 추가는 비슷하지만, 지키는 단위가 다릅니다.

취약점 공격자가 바꾸는 것 원래 확인해야 할 것
mid 바꿔 치기 대상 오브젝트 이 이용자가 이 이용자 레코드에 접근해도 되는가
status 추가 오브젝트 안의 프로퍼티 이 이용자가 이 항목을 바꿔도 되는가

OWASP API Security Top 10 2023에서는 후자를 Broken Object Property Level Authorization 으로 다루고, 이전의 Mass Assignment를 이 분류에 넣고 있습니다8.

「JSON을 그대로 엔티티에 넣는」 일이 위험합니다

취약한 구현은 개념적으로 다음과 같습니다.

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

화면에 nameage만 입력란이 있어도, 공격자는 HTTP 요청을 직접 만들 수 있습니다. UI에 없는 항목은 보안 경계가 아닙니다.

안전한 구현은 갱신 가능한 항목을 명시합니다.

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

여기서 중요한 것은 두 가지입니다.

  1. 갱신용 입력 타입에 이용자가 바꿔도 되는 항목만 둡니다
  2. 사양 밖 알 수 없는 항목을 무시하는 것이 아니라, 가능하면 오류로 거부합니다

알 수 없는 항목을 조용히 무시하면 공격이 실패한 사실은 감출 수 있지만, 클라이언트 구현 실수나 공격 징후를 놓칩니다. 호환성상의 이유가 없으면 엄격한 스키마로 거부하는 편이 조사하기 쉽습니다.

항목 단위 인가갱신용 DTO는 허용 목록만 갖고, 알 수 없는 프로퍼티는 거부합니다스키마 검증아니요전용 경로갱신용 DTO(허용 목록)nameage요청 바디허용된항목만인가엔티티의 name/age를 갱신오류를 반환결제 서비스검증된 알림status=paid를 갱신

그림8: 항목 단위 인가. 갱신 가능한 항목을 허용 목록으로 제한하고, 과금 상태는 다른 경로로 바꿉니다.

status는 결제 결과에서만 바꿉니다

status=paid는 이용자 프로필의 일부가 아닙니다. 결제가 성공했다는 서버 쪽 사실에서 도출되는 상태입니다.

이용자의 프로필 갱신
  -> name / age만 변경 가능

결제 서비스로부터의 검증된 알림
  -> paymentId를 대조
  -> 중복 처리를 방지
  -> status를 paid로 변경

같은 데이터베이스 열에 저장하더라도, 변경 권한과 경로는 별개입니다. 내부 엔티티를 그대로 외부 API의 입력 타입으로 쓰면 이 경계가 사라집니다.

9. 문항2(5) ── 무차별 대입 대책은 실패 횟수를 상태로 둡니다

4자리 코드의 무차별 대입에 대해, 표5의 빈칸 d에 들어가는 처리를 30자 이내로 답합니다. 임곗값은 10입니다.

해답 예는 다음과 같습니다.

연속 실패 횟수가 임곗값을 넘으면 계정을 잠그는 처리

여기는 문항1의 stateless와 모순되지 않습니다. API 호출의 대화 상태를 서버 세션으로 갖지 않는 것과, 보안 판단에 필요한 실패 횟수를 영속화하는 것은 별개입니다.

시도 횟수 제한의 유무제한이 없으면 평균 500초에 뚫리지만, 실패 횟수 제한으로 공격 속도를 낮출 수 있습니다제한 있음공격 속도가 급락계정 잠금실패 10회에 잠금단계적 지연제한 없음약 500초인증 성공1초에 10회 시도

그림10: 횟수 제한의 유무. 실패 횟수 제한으로 무차별 대입을 실용적으로 막을 수 있습니다.

실무에서는 「영구 잠금 하나」로 두지 않습니다

계정 단위 횟수 제한은 필요하지만, 공격자가 타인의 이용자 ID를 아는 경우 일부러 10회 실패해 정규 이용자를 잠글 수 있습니다. 그래서 실무에서는 다음을 조합합니다.

제어 역할
계정 단위 실패 횟수 한 계정에 대한 무차별 대입을 막습니다
단계적 대기 시간 정규 이용자의 입력 실수는 허용하면서 공격 속도를 낮춥니다
발신지 IP·단말·ASN 등의 제어 여러 계정에 소수 회씩 시도하는 공격을 억누릅니다
위험 기반 판정 평소와 다른 지역·단말·속도를 더 강하게 제한합니다
이용자에 대한 알림 공격이나 오조작을 알아차리게 합니다
안전한 복구 절차 잠금 해제 창구를 공격 경로로 만들지 않습니다

나아가 코드를 재전송했을 때 실패 횟수를 0으로 되돌려서는 안 됩니다. 공격자가 재전송 API를 호출할 때마다 시도 한도가 되살아나기 때문입니다. 현행 NIST SP 800-63B도 새 인증 비밀을 생성해도 실패 횟수를 리셋하지 말 것을 요구합니다4.

인증 코드는 한 번만 쓰이게 합니다

문제문에서는 유효 기간이 중심이지만, 실무에서는 다음도 필요합니다.

  • 성공한 코드는 즉시 무효화합니다
  • 같은 코드의 재사용을 거부합니다
  • 코드 자체를 로그에 남기지 않습니다
  • 코드 대조의 성패로부터 이용자 존재 여부를 추측할 수 없는 응답으로 둡니다
  • 코드 전송 API에도 횟수 제한을 둡니다

짧은 비밀을 쓰는 이상, 난수 생성만으로 안전성을 맡길 수 없습니다.

인증 코드 대책자릿수·만료 시간에 더해 시도 횟수 제한·재사용 거부·알림 등으로 지킵니다인증 코드자릿수를 늘린다유효 기간을 짧게 한다시도 횟수 제한성공 후 무효화재전송으로 실패 횟수를 리셋하지 않는다코드를 로그에 남기지 않는다발신지 단위 제어

그림11: 인증 코드 대책. 자릿수와 만료 시간뿐 아니라 시도 제어와 운용을 조합합니다.

10. 문항2의 네 가지를 한 장으로 구별합니다

문항2에서 혼동하기 쉬운 논점을, 공격자가 제어한 값으로 정리합니다.

공격 공격자가 바꾼 값 믿어서는 안 됐던 곳 근본 대책
JWT 변조 JWT 헤더의 alg, 페이로드의 이용자 ID 토큰 자신이 밝히는 검증 알고리즘 허용 알고리즘을 서버 쪽에서 고정합니다
타인 정보 조회 요청의 mid 클라이언트가 지정한 대상 ID JWT 주체와 대조하거나, 대상 ID를 JWT에서 정합니다
유료 이용자로의 변경 사양 밖 status 자동 바인딩된 모든 프로퍼티 갱신 가능 프로퍼티를 허용 목록화합니다
4자리 코드 돌파 otp의 후보 무제한 인증 시도 실패 횟수 제한, 지연, 위험 판정을 넣습니다

「입력값을 검증한다」로 전부를 묶지 않는 것이 중요합니다.

  • alg는 암호 처리의 정책입니다
  • mid는 오브젝트 단위 인가입니다
  • status는 프로퍼티 단위 인가입니다
  • otp는 온라인 추측에 대한 내성입니다

같은 HTTP 요청 안에 있어도, 지키는 이유가 다릅니다.

11. 문항3(1) ── 파괴하지 않고 외부 코드 실행을 확인합니다

서비스 시작 후, 널리 쓰이는 오픈소스 라이브러리 H에 중대한 취약점 V가 공개됩니다. 문제문의 흐름은 다음과 같습니다.

  1. 공격자가 JNDI Lookup을 포함한 문자열을 HTTP 헤더에 넣어 보냅니다
  2. 공격 대상 서버가 그 값을 로그로 출력합니다
  3. 취약한 라이브러리가 JNDI Lookup을 평가하고, 공격용 LDAP 서버에 조회합니다
  4. LDAP 응답이 공격용 HTTP 서버의 URL을 반환합니다
  5. 공격 대상 서버가 클래스 파일을 가져와 명령을 실행합니다

이것은 고유 이름을 가린 Log4Shell(CVE-2021-44228)형 공격 으로 읽을 수 있습니다. Apache 설명에서도, 공격자가 로그 메시지나 파라미터를 제어할 수 있으면 LDAP 서버에서 읽어 들인 임의 코드를 실행할 수 있는 취약점으로 설명합니다9.

Log4Shell형 취약점의 확인 흐름무해한 콜백으로 JNDI에서 외부 코드 실행까지의 연쇄가 통하는지 확인합니다x-api-version에jndi:ldap://...를 주입JNDI LookupHTTP URL 응답GET을 기록도달 확인공격자취약한 서버로그 처리악용 LDAP 서버악용 HTTP 서버index.html테스트 서버접근 로그취약점을 확인

그림12: Log4Shell형 취약점의 확인 흐름. 파괴적 명령이 아니라 HTTP 접근 기록으로 도달을 확인합니다.

검증 코드는 무해한 HTTP 접근만 일으킵니다

G사는 시스템에 영향을 주지 않는 검증 코드를 실행해, 외부에서 취약점 V를 악용할 수 있는지 확인합니다. 검증 코드가 하는 명령은 테스트 서버의 index.html을 가져오는 것뿐입니다.

문항3(1)은 테스트 서버에 무엇을 구현하면 명령이 실행되었다고 확인할 수 있는지를 묻습니다.

해답 예는 다음과 같습니다.

테스트 서버의 index.html에 대한 접근을 기록하고 확인하는 구조

웹 서버의 접근 로그에 공격 대상 서버로부터의 GET이 남으면, 적어도 다음 연쇄가 통했음을 확인할 수 있습니다.

외부 HTTP 요청
  -> 로그 처리
  -> JNDI Lookup
  -> LDAP 응답
  -> 클래스 취득
  -> 검증 명령 실행
  -> 테스트 서버에 대한 HTTP 접근

왜 「화면에 글자를 내는」 것만으로는 부족한가

공격 대상은 서버입니다. 이용자 브라우저 화면에 변화가 나온다고 할 수 없습니다. 또한 취약점이 있어도, 중간 바깥쪽 통신이 방화벽에서 막히는 경우가 있습니다.

테스트 서버 쪽에서 접근을 기록하면, 공격 대상 서버에서 외부로 도달했다는 관측 가능한 증거가 됩니다.

실무에서 같은 종류의 검증을 할 때는 반드시 다음을 지킵니다.

  • 대상 시스템 소유자에게서 명시적 승인을 받습니다
  • 운영에 영향이 없거나 허용할 수 있는 검증 방법으로 둡니다
  • 쓰기·삭제·설정 변경 같은 파괴적 명령을 쓰지 않습니다
  • 검증용 도메인이나 서버를 자사에서 관리합니다
  • 검증 시각, 발신지, 대상, 기대하는 콜백을 기록합니다
  • 검증 뒤에 일시적인 LDAP·HTTP 서버나 자격 정보를 철거합니다

「임의 코드 실행을 확인한다」는 것과 「임의의 위험한 코드를 실행한다」는 것은 같지 않습니다. 목적을 채우는 최소 부작용으로 둡니다.

12. 문항3(2)(3) ── WAF는 HTTP 헤더를 검사합니다

서비스 N의 WAF는 검사 대상으로 GET, POST, PUT, ANY, Header, COOKIE, Multipart를 고를 수 있습니다.

공격 코드는 x-api-version이라는 HTTP 헤더 값에 들어갑니다. 따라서 표6의 빈칸 e와 f는 둘 다 Header 입니다.

본문에 적힌 위치를 그대로 WAF 검사 대상에 대응시킵니다

여기는 일반 지식보다 문제문의 데이터 흐름을 읽는 문제입니다.

공격 문자열의 위치:
  x-api-version 헤더
          |
          v
WAF의 검사 대상:
  Header

GET 파라미터도 POST 본문도 아닙니다. WAF 기능 목록을 보고 「공격처럼 보이니 ANY」를 고르는 것이 아니라, 문제문에서 공격자가 값을 넣은 곳을 답합니다.

대문자·소문자 치환에 대응합니다

처음 안은 개념적으로 다음 규칙이었습니다.

Header  \Wjndi\W  차단
Header  \Wldap\W  차단

그러나 jNdI처럼 대문자·소문자를 바꾸면, 소문자만의 단순한 패턴을 피할 수 있습니다.

문항3(3)의 해답 예는 다음 중 하나입니다.

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

문제 책자에서는 백슬래시가 일본어 환경의 글자 모양으로 엔 기호처럼 보일 수 있지만, 정규식으로는 \W입니다. \W는 영숫자와 언더스코어 이외의 문자에 일치합니다. JNDI Lookup 구문에서는 jndi 앞뒤에 ${: 같은 비단어 문자가 나타나므로, 그것을 포함해 봅니다.

같은 생각으로 ldap 쪽도 대문자·소문자를 무시하는 형태로 만들 수 있습니다.

\W[lL][dD][aA][pP]\W

이 정규식을 「완전한 Log4Shell 대책」으로 보지 않습니다

시험은 문제문에 나온 우회 방법에 대응하는 정규식을 답하는 것입니다. 실제 공격에서는 문자열 분할, 다른 Lookup, 인코딩, 다른 프로토콜 등, 시그니처만으로는 빠짐없이 잡기 어려운 변형이 있을 수 있습니다.

따라서 실무에서의 위치는 다음과 같습니다.

  1. 지금 파악된 공격 패턴을 WAF로 임시로 막습니다
  2. 영향받는 라이브러리가 정말 포함되는지 조사합니다
  3. 바깥쪽 LDAP·RMI·불필요한 HTTP 통신을 제한합니다
  4. 수정판으로 업데이트합니다
  5. 업데이트 뒤에도 로그를 확인해 침해 여부를 조사합니다

WAF는 수정판이 나올 때까지 시간을 버는 층입니다.

WAF의 위치WAF는 임시 완화 층이며, 근본 대책은 라이브러리를 수정판으로 업데이트하는 것입니다WAF 규칙탐지/차단바깥쪽 통신 제한중대한 취약점 공개영향 확인임시 완화공격 패턴을 일시적으로 막음악용 경로를 막음수정판 라이브러리로 업데이트사후 확인과 재발 방지

그림13: WAF의 위치. WAF는 수정판이 나올 때까지의 시간 벌기이며, 근본 대책은 업데이트입니다.

13. 문항3(4) ── 처음에 「탐지」를 쓰는 이유

변경 후 WAF 규칙에 대해, 등록 지원사 Z씨는 운영 시작 뒤 일정 기간은 동작을 「차단」이 아니라 「탐지」로 두라고 조언합니다.

문항은 탐지로 두는 이점과, 피해를 최소화하기 위해 실시해야 할 내용을 각각 25자 이내로 묻습니다.

해답 예는 다음과 같습니다.

항목 해답의 요점
이점 오탐으로 인한 차단을 막을 수 있다
실시해야 할 내용 알림을 받으면 공격인지를 정밀히 살핀다

탐지 모드는 「아무것도 하지 않는 모드」가 아닙니다

탐지 모드에서는 규칙에 일치한 통신을 통과시키면서 로그에 기록하고 알림을 냅니다. 정상 API 호출 안에 우연히 jndildap라는 문자열이 들어가도, 바로 업무를 멈추지 않습니다.

그 대신 운용 쪽에는 다음이 필요합니다.

알림 수신
   |
   v
대상 요청을 확인
   |
   +-- 정상 통신 -> 규칙을 좁힌다, 예외 조건을 검토
   |
   +-- 공격     -> 대상을 격리, 로그 보전, 영향 조사, 차단으로

알림을 아무도 보지 않으면 탐지 모드에는 방어 효과가 없습니다. 탐지는 관측해 판단하는 운용과 한 세트입니다.

탐지에서 차단으로 옮기는 흐름

일반적인 도입 절차는 다음과 같습니다.

  1. 탐지 모드로 실제 트래픽에 적용합니다
  2. 오탐과 정탐을 분류합니다
  3. 대상 헤더, 경로, API, 문자 경계 등을 조정합니다
  4. 정상 통신에 대한 영향이 허용 가능한지 확인합니다
  5. 차단 모드로 옮깁니다
  6. 차단 건수와 업무 영향을 감시합니다

다만 이것은 평시의 원칙입니다. 취약점이 중대하고, 실제로 악용되며, 대체 수단이 없는 경우에는, 오탐으로 인한 정지보다 침해 피해가 크다고 보고 처음부터 차단하기도 합니다. 시험 상황에서는 서비스를 지금까지처럼 이용할 수 있는지 확인하려고 먼저 탐지를 고릅니다.

WAF의 탐지에서 차단으로탐지 모드로 알림을 관측하고, 오탐을 조정한 뒤 차단 모드로 옮깁니다실제 트래픽오탐공격탐지 모드알림 발생공격인가오탐인가규칙을 조정차단 모드로차단 건수와 업무 영향을 감시

그림14: 탐지에서 차단으로. 먼저 관측·조정하고, 영향이 허용 가능한지 확인한 뒤 차단으로 옮깁니다.

14. WAF는 임시 대책, 업데이트가 근본 대책

문제문에서는 라이브러리 H의 공식 사이트에 수정판도 임시 대책도 아직 없고, 클라우드 사업자의 망라적인 WAF 규칙도 최대 72시간이 걸리는 상황이었습니다. 그래서 G사는 스스로 영향을 확인하고, 파악된 패턴만이라도 일시적으로 막습니다.

이 순서는 인시던트 대응의 기본형입니다.

단계 목적 이 문제에서의 대응
영향 확인 자사가 정말 위험한지를 판단한다 무해한 콜백으로 외부 악용 가부를 확인
임시 완화 수정까지의 시간을 번다 WAF 규칙, 탐지·차단, 바깥쪽 통신 제한
근본 수정 취약한 원인을 제거한다 수정판 라이브러리로 업데이트
사후 확인 이미 악용되지 않았는지 조사한다 WAF·앱·DNS·프록시 등의 로그 조사
재발 방지 다음 판단을 빠르게 한다 의존 관계 목록, SBOM, 업데이트 절차, 연락 경로

「쓰는지 모른다」가 가장 큰 지연이 됩니다

문제문에서는 G사가 F사에 라이브러리 H를 쓰는지 문의해도, 상세 구성 분석이 필요해 답변에 시간이 걸린다고 되어 있습니다.

실무에서는 중대한 취약점 공개 뒤에야 JAR 파일을 찾기 시작하면 대응이 늦습니다. 적어도 다음은 평시부터 갖춰 두어야 합니다.

  • 직접 의존과 전이적 의존의 목록
  • 배포물에 실제로 포함된 컴포넌트와 버전
  • 어느 서비스·컨테이너·단말에 배포되어 있는지
  • 의존 라이브러리를 업데이트해 다시 빌드·재배포하는 절차
  • 긴급 변경을 승인하는 연락 경로
  • 바깥쪽 통신의 허용처와, 막았을 때의 영향
  • 로그 저장 위치와 검색 방법

SBOM은 목적이 아닙니다. 「이 취약점은 어느 실행 중인 시스템에 영향을 주는가」를 짧은 시간에 답하기 위한 색인입니다.

업데이트만으로 조사를 끝내지 않습니다

취약점 공개 전후에 이미 공격을 받았을 가능성이 있습니다. 수정판으로 업데이트해 이후 악용을 막아도, 이미 침해된 자격 정보나 설치된 백도어까지는 사라지지 않습니다.

Log4Shell형이라면 적어도 다음 관점을 조사합니다.

  • JNDI나 LDAP을 나타내는 수상한 문자열을 포함한 HTTP 요청
  • 애플리케이션 서버에서 외부 LDAP·RMI·HTTP로의 통신
  • 평소와 다른 자식 프로세스 기동
  • 수상한 JAR, class, 스크립트, 실행 파일의 생성
  • 클라우드 자격 정보나 환경 변수에 대한 접근
  • 업데이트 전후의 인증·권한 변경·외부 송신

WAF 로그만으로 「공격받지 않았다」고 단정하지 않는 것이 중요합니다. WAF를 통과하지 않는 내부 경로나, 과거에 저장되지 않은 로그가 있기 때문입니다.

15. 시험에서 점수를 내기 쉬운 읽기 방법

이 문제는 지식 문제라기보다, 사양과 구현의 차이를 읽는 문제입니다.

15.1 표의 「사양」과 「구현」을 나눕니다

status 문제에서는 API 사양에 없는 값이 구현에서는 통합니다.

사양:
  mid / name / age

구현:
  받은 파라미터를 모두 P로 보낸다

이 차이가 보이면 빈칸 c가 공통 모듈 P임을 알 수 있습니다.

15.2 공격자가 바꾼 값에 선을 긋습니다

각 공격에서 바뀐 값은 다음과 같습니다.

  • JWT 헤더의 alg
  • JWT 페이로드의 이용자 ID
  • API 파라미터의 mid
  • 사양 밖 status
  • 인증 API의 otp
  • HTTP 헤더의 x-api-version

문항은 거의 모두 「그 값이 어디서 검증되어야 하는가」를 묻습니다.

15.3 해답은 문제문의 용어로 되돌립니다

실무에서는 「BOLA」「Mass Assignment」「rate limiting」이라고 부를 수 있습니다. 그러나 문항이 요구하는 것은 문제문 구성에 맞춘 구체적인 처리입니다.

나쁜 예:

인가를 적절히 수행한다.

좋은 예:

JWT에 포함된 이용자 ID가 mid 값과 일치하는지 검증한다.

나쁜 예:

브루트포스 대책을 한다.

좋은 예:

연속 실패 횟수가 임곗값을 넘으면 계정을 잠근다.

추상 이름만 알아서는 글자 수 안에서 채점 가능한 답이 되지 않습니다.

15.4 WAF는 「어디에 들어갔는가」를 따라갑니다

WAF 검사 대상은 공격 종류에서 추측하는 것이 아니라, 공격 문자열의 저장 위치에서 정합니다.

x-api-version 헤더에 넣었다
        ↓
검사 대상은 Header

채점 강평에서 문항3(1)의 정답률이 다소 낮았던 것도, 그림6의 공격 흐름에 맞지 않는 답이 많았기 때문입니다. 공격 절차를 화살표로 다시 쓰기만 해도, 무엇을 관측해야 하는지가 보입니다.

16. 실무 API 리뷰에서 쓸 수 있는 체크리스트

이 문제를 실제 설계·코드 리뷰로 가져가기 위한 체크리스트입니다.

JWT 검증

  • 허용하는 서명 알고리즘을 서버 설정으로 고정하고 있다
  • none이나 상정 밖 알고리즘을 거부한다
  • 서명, iss, aud, exp, nbf를 용도에 맞게 검증한다
  • ID 토큰, 액세스 토큰, 리프레시 토큰을 혼동하지 않는다
  • 키 로테이션과 폐기 시 절차가 있다
  • JWT 페이로드에 비밀로 해야 할 정보를 넣지 않았다

오브젝트 단위 인가

  • 요청 안의 ID를 바꿨을 때 타인 데이터에 도달하지 못한다
  • 목록, 상세, 갱신, 삭제, 다운로드 모두에서 인가한다
  • 인가는 화면이 아니라 데이터에 도달하는 공통 층에서 실시한다
  • 자기 전용 API에서는 대상 ID를 토큰에서 도출할 수 없는지 검토했다
  • 관리자용 조작은 일반 이용자용 API와 정책을 나눈다

프로퍼티 단위 인가

  • 외부 입력 타입과 데이터베이스 엔티티를 나눈다
  • 갱신 가능한 항목을 허용 목록으로 열거한다
  • 사양 밖 프로퍼티를 거부하거나 감사한다
  • 권한, 과금, 승인, 소유자 등의 상태를 이용자 입력에서 바꾸지 못한다
  • 응답에도 불필요한 기밀 프로퍼티를 넣지 않는다

인증 시도

  • 계정 단위 실패 횟수 제한이 있다
  • 단계적 지연이나 발신지 단위 제어가 있다
  • 코드 재발급으로 실패 횟수가 리셋되지 않는다
  • 인증 코드는 한 번만 쓸 수 있다
  • 인증 코드나 비밀번호를 로그에 남기지 않는다
  • 잠금 해제·복구 절차가 다른 약한 인증 경로가 되지 않는다

중대한 의존 라이브러리 취약점

  • 실행 중인 서비스와 의존 버전을 대응시킬 수 있다
  • 무해한 방법으로 영향을 검증하는 절차가 있다
  • WAF나 바깥쪽 통신 제한 같은 임시 대책을 적용할 수 있다
  • 탐지 알림을 담당자가 확인하는 운용이 있다
  • 수정판으로 업데이트하는 긴급 릴리스 경로가 있다
  • 업데이트 전에 악용되었을 가능성을 로그에서 조사한다

17. 이 문제에서 보이는 공통 부품의 양면성

이 문제에서는 JWT 관리 라이브러리 Q와 공통 모듈 P라는 두 공통 부품이 등장합니다.

공통 부품에는 큰 이점이 있습니다.

  • 한곳을 고치면 쓰는 모든 API에 수정이 반영됩니다
  • 인가나 검증 구현을 각 기능에 중복시키지 않아도 됩니다
  • 테스트 대상을 모을 수 있습니다
  • 로그와 감사 형식을 통일할 수 있습니다

한편 잘못도 전체에 퍼집니다.

  • 라이브러리 Q가 alg=none을 받아들이면 JWT를 쓰는 모든 API가 위험해집니다
  • 공통 모듈 P가 임의의 midstatus를 받아들이면 GET과 PUT 양쪽이 위험해집니다
  • 취약한 라이브러리 H가 기반에서 쓰이면 HTTP 헤더를 로그에 내는 여러 경로가 공격면이 됩니다

따라서 공통화해야 하는 것은 단순한 데이터 접근이 아닙니다. 보안 불변 조건을 공통화하고, 그 공통 부품을 단독으로 엄격히 검증해야 합니다.

예를 들어 P의 계약을 다음과 같이 둡니다.

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

다음과 같은 저수준 API를 일반 호출 측에 그대로 공개하지 않는 편이 안전합니다.

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

후자가 필요한 것은 관리 처리 등 한정된 경로뿐입니다. 저수준의 자유도를 모든 API에 나누어 주면, 각 호출 측이 매번 올바르게 쓰기를 기대하는 설계가 됩니다.

18. 정리

2024년도 봄 오후 문제1은 API 보안의 논점을 하나씩 분리해 읽는 문제입니다.

JWT를 쓰고 있다는 것은 안전한 인증을 뜻하지 않습니다. 서명 알고리즘을 공격자에게 고르게 하면 이용자 ID를 고쳐 쓸 수 있습니다.

JWT 서명이 올바르다는 것은 올바른 인가를 뜻하지 않습니다. 요청의 mid를 믿으면 정규 이용자가 타인의 정보에 접근할 수 있습니다.

자기 오브젝트를 갱신할 수 있다는 것은 모든 프로퍼티를 바꿔도 된다는 뜻이 아닙니다. status 같은 내부 상태를 자동 바인딩하면 권한이나 과금 상태를 고쳐 쓸 수 있습니다.

인증 코드에 유효 기간이 있다는 것은 무차별 대입에 강하다는 뜻이 아닙니다. 후보 수와 시도 속도를 계산하고, 실패 횟수를 제한해야 합니다.

WAF에 규칙을 넣는다는 것은 취약점을 고쳤다는 뜻이 아닙니다. 탐지와 차단으로 시간을 벌고, 영향을 확인하며, 최종적으로는 라이브러리를 업데이트합니다.

취약점과 대책의 대응표각 취약점과 그에 대한 신뢰 경계, 대책을 대응시킵니다토큰 검증오브젝트 단위 인가프로퍼티 단위 인가인증 시도 제어입력에서 실행으로JWT 변조허용 알고리즘을 고정mid 바꿔 치기JWT 주체와 대조/mid 불필요status=paid갱신 DTO를 허용 목록화4자리 코드 무차별 대입실패 횟수 제한·지연Log4Shell형 취약점라이브러리 업데이트/WAF

그림15: 취약점과 대책 대응표. 깨진 경계마다 대책을 나눕니다.

이 문제를 관통하는 원칙은 하나입니다.

바로 앞 검증에 성공했다는 사실을, 다음 신뢰 경계를 생략하는 이유로 삼지 않는다.

시리즈의 이전 글에서는 2023년 가을 오후 문제1의 저장형 XSS와, 2023년 가을 오후 문제2의 방문객용 Wi-Fi에서 정보가 나가는 경로를 해설합니다. 웹사이트 전체의 확인 관점은 IPA 「안전한 웹사이트 만드는 법」을 체크리스트로 쓰기도 참고하세요.

최종 정리바로 앞 검증에 성공해도 다음 신뢰 경계를 생략하지 않음을 나타냅니다인증 성공JWT 서명 검증오브젝트 단위 인가프로퍼티 단위 인가시도 횟수 제한입력에서 실행으로의 경계WAF/라이브러리 업데이트

그림16: 최종 정리. 신뢰 경계는 단계적으로 확인하며, 하나를 생략해서는 안 됩니다.

참고 링크

  1. IPA, 2024년도 봄 정보처리안전확보지원사 시험 오후 문제 책자. 이 글이 다루는 문제문입니다. 

  2. IPA, 2024년도 봄 정보처리안전확보지원사 시험 오후 해답 예. 각 문항의 공식 해답 예입니다. 

  3. IPA, 2024년도 봄 정보처리안전확보지원사 시험 오후 채점 강평. 정답률과 오답 경향의 설명입니다. 

  4. NIST, SP 800-63B: Authentication and Authenticator Management. 단기 비밀의 자릿수, 시도 횟수 제한, 재발급 시 실패 횟수, 전자메일을 대역외 인증에 쓰지 않는 것 등을 제시합니다.  2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). Unsecured JWT와 alg=none을 포함한 JWT의 사양입니다. 

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices. 허용 알고리즘의 고정, 발행자·주체·audience의 검증 등을 정한 BCP입니다. 

  7. OWASP, API1:2023 Broken Object Level Authorization. 이용자가 지정하는 오브젝트 ID마다 인가를 확인할 필요성을 설명합니다. 

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Mass Assignment를 포함한 프로퍼티 단위 인가 결함과 대책을 설명합니다. 

  9. Apache Logging Services, Security. CVE-2021-44228의 영향, JNDI와 LDAP을 통한 코드 실행, 수정판을 설명합니다. 

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

웹사이트 제작

회원 API나 스마트폰 연동에서는 JWT 검증, 오브젝트 단위 인가, 갱신 가능 프로퍼티 제한이 그대로 웹 시스템의 안전성으로 이어지기 때문입니다.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

JWT 서명 검증에 성공했는데, 왜 다른 사람으로 위장할 수 있나요?
이 문제에서는 JWT 관리 라이브러리가 JWT 헤더의 alg를 공격자가 지정한 그대로 받아들여, alg=none인 JWT를 서명 없이도 올바른 것으로 취급했습니다. 그래서 페이로드의 이용자 ID를 고쳐 써도 검증에 성공합니다. 시험 해답은 검증 대상을 JWT 헤더의 alg로 두고, 그 값이 NONE이 아님을 확인하는 것입니다. 다만 실무에서는 NONE만 거부해서는 부족합니다. 서버 쪽 설정으로 허용 알고리즘을 RS256 등으로 고정하고, 토큰이 스스로 밝힌 알고리즘을 그대로 선택에 쓰지 않습니다. 이어서 issuer, audience, 유효 기간, subject 등도 용도에 맞게 검증합니다.
JWT 안의 이용자 ID와 요청의 mid를 비교하면 인가 대책으로 충분한가요?
이 문제의 해답으로는 충분합니다. 공통 모듈 P에서 JWT에 들어 있는 이용자 ID와 mid가 일치하는지 검증하면, 타인의 mid를 지정하는 공격을 막을 수 있습니다. 다만 자기 정보만 다루는 API라면, 실무에서는 mid를 클라이언트에서 받지 않고 검증이 끝난 JWT의 subject에서 이용자 ID를 정하는 설계가 더 안전합니다. 예를 들어 GET /users/me나 PUT /users/me로 두면, 비교 처리를 빠뜨리는 실수 자체를 줄일 수 있습니다. 관리자가 다른 사람을 조작하는 API는 별도 엔드포인트와 인가 정책으로 나눕니다.
status를 추가하는 공격은 왜 일반적인 입력값 검증만으로는 막을 수 없나요?
name 길이나 age 범위를 검증해도, 받아서는 안 될 status를 자동 바인딩해 내부 오브젝트로 넘기고 있으면 막을 수 없기 때문입니다. 문제는 값의 형식이 아니라, 그 프로퍼티를 이용자가 바꿔도 되는가라는 프로퍼티 단위 인가입니다. 갱신용 입력 타입에는 name과 age만 정의하고, 알 수 없는 프로퍼티는 거부합니다. 과금 상태는 결제 서비스의 성공 결과처럼 서버가 믿을 수 있는 이벤트에서만 바꿔야 합니다.
4자리 인증 코드는 10분이면 만료되는데, 왜 위험한가요?
0000부터 9999까지 후보는 1만 가지뿐이고, 1초에 10회 시도할 수 있으면 평균 5,000회, 즉 500초면 맞히기 때문입니다. 유효 기간 10분은 600초이므로, 중복 없이 순서대로 시도하면 유효 기간 안에 6,000가지를 확인할 수 있습니다. 만료 시간만으로는 무차별 대입을 막지 못합니다. 후보 수, 시도 속도, 시도 횟수 상한을 함께 설계해야 합니다.
문항의 대책은 계정 잠금인데, 실무에서도 즉시 잠금만으로 충분한가요?
그렇지 않습니다. 문제의 빈칸에는 연속 실패 횟수가 임곗값을 넘으면 계정을 잠그는 처리가 들어가지만, 고정된 영구 잠금만으로는 공격자가 고의로 타인 계정을 잠그는 서비스 방해를 일으킬 수 있습니다. 실무에서는 계정 단위 실패 횟수, 단계적 대기 시간, 발신지나 단말의 위험 판정, 알림, 복구 절차를 조합합니다. 새 코드를 발급해도 실패 횟수를 0으로 되돌리지 않는 것도 중요합니다.
WAF를 차단이 아니라 탐지로 두는 의미는 무엇인가요?
정상 문자열을 잘못 공격으로 판정해도 업무 통신을 멈추지 않아도 된다는 점입니다. 문제 해답에서 이점은 오탐으로 인한 차단을 막을 수 있다는 것이고, 해야 할 일은 알림을 받으면 공격인지 정밀히 살피는 것입니다. 탐지 모드는 방치용 설정이 아닙니다. 로그를 확인해 오탐을 걸러내고, 규칙을 조정하고, 차단으로 옮기기 위한 관찰 기간으로 씁니다. 이미 알려진 중대 취약점이 실제로 악용되는 긴급 상황에서는, 가용성 위험과 비교해 먼저 차단하는 판단도 있을 수 있습니다.
이 문제의 라이브러리 H는 Log4j인가요?
문제문은 제품명을 밝히지 않지만, JNDI Lookup, LDAP 서버, HTTP 서버에서 클래스 취득, HTTP 헤더에 넣은 문자열, CVSS v3.1의 높은 기본값이라는 공격 흐름은 Log4Shell로 알려진 CVE-2021-44228을 추상화한 것으로 읽는 편이 자연스럽습니다. 이 글에서는 그 대응 관계를 설명하지만, 시험에서는 고유 이름을 답할 필요는 없습니다. 주어진 공격 절차와 WAF 사양만으로 답을 낼 수 있습니다.
이 문제에서 실무로 가져가야 할 것은 무엇인가요?
인증에 성공한 것, JWT가 변조되지 않은 것, 대상 오브젝트에 접근해도 되는 것, 대상 프로퍼티를 바꿔도 되는 것은 모두 다른 확인이라는 점입니다. 더불어 짧은 인증 코드에는 시도 횟수 제한이 필요하고, 중대한 라이브러리 취약점에서는 영향 확인, 임시 방어, 근본 수정을 함께 진행합니다. 공통 부품에 인가를 모으는 것, 입력 스키마를 허용 목록으로 두는 것, JWT 검증 조건을 서버 쪽에서 고정하는 것, 의존 라이브러리를 파악해 업데이트할 수 있는 상태로 두는 것이 실무의 핵심입니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기