481. Insecure Design (안전하지 않은 설계)

핵심 인사이트 (3줄 요약)

  1. 본질: 안전하지 않은 설계(Insecure Design)는 개발자가 코드를 완벽하고 버그 없이 짰음에도 불구하고, 애초에 시스템을 기획하고 아키텍처 도면을 그릴 때부터 보안(비즈니스 예외, 인증/인가 룰)을 전혀 고려하지 않아 태생적으로 털릴 수밖에 없는 근본적 논리 결함이다.
  2. 가치: 2021년 OWASP Top 10에 4위로 혜성처럼 새롭게 등극했다. 아무리 훌륭한 시큐어 코딩(SQL 인젝션 방어 등)과 방화벽을 발라놔도, 기획 자체가 멍청하게 설계된 시스템은 해커의 '로직 우회' 한 방에 허무하게 붕괴한다는 뼈아픈 진실을 전 세계 IT 업계에 폭로했다.
  3. 융합: 설계의 빵꾸를 코딩 단계에서 고치는 것은 불가능하다. 이를 막기 위해 프로젝트 첫날 칠판에 그림을 그리는 **위협 모델링(Threat Modeling, STRIDE)**과, 테스트와 보안 점검을 맨 왼쪽으로 확 당겨오는 시프트 레프트(Shift-Left) 철학이 전사적으로 융합되어야만 박멸할 수 있는 거버넌스(Governance) 레벨의 영역이다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: Insecure Design(안전하지 않은 설계)은 코드의 오타나 문법 에러(Bug)가 아니다. 설계 자체의 멍청함(Flaw)이다.

    • 결함(Flaw): "비밀번호 찾기 질문" 기능에서, 질문이 "어머니의 결혼 전 성(姓)은?" 이다. 해커가 피해자의 페이스북이나 가족관계증명서를 털어 답을 알아낸 뒤 비밀번호를 100% 합법적(?)으로 바꿔버린다. 코드는 100% 정상 작동했다. 기획(설계)이 미친 짓이었을 뿐이다.
  • 필요성: 수백억 원의 보안 예산을 들여 방화벽(WAF)을 세우고, 정적 분석기(SonarQube)를 달아 코드를 쥐어짰다. 해커가 들어올 기술적 구멍(버퍼 오버플로우, 인젝션)은 0(Zero)이 되었다. 그런데 영화 예매 사이트에서 "어린이표 2장, 어른표 -1장"을 결제하니까 총액이 0원이 되어 공짜로 예매가 되는 사고가 터졌다. 방화벽도 기계도 이 논리적 미친 짓(마이너스 수량 꼼수)을 에러라고 잡지 못했다. 왜? 문법적으론 완벽한 숫자 계산이었으니까. 코딩의 완벽함이 비즈니스 로직의 허술함을 지켜주지 못한다는 한계를 깨닫고, 아키텍처 설계도(Blueprint) 자체의 논리를 뜯어고쳐야 생존할 수 있다.

  • 💡 비유: 안전하지 않은 설계는 **'잠금장치가 문 안쪽에 없는 감옥'**과 같습니다. 감옥을 티타늄(완벽한 코드)으로 엄청 튼튼하게 지었습니다. 벽을 부수고(해킹) 탈출하는 건 불가능합니다. 그런데 설계자가 실수로 '문 여는 버튼'을 감옥 안쪽 벽(멍청한 기획)에 달아버렸습니다. 죄수는 그냥 버튼을 누르고 유유히 당당하게 걸어 나갑니다. 건물의 튼튼함(코딩)과 상관없이 도면(설계) 자체가 바보 같아서 벌어진 재앙입니다.

  • 등장 배경 및 발전 과정:

    1. 코더(Coder)의 시대: 2000년대 해커들은 주로 C언어 메모리나 SQL 인젝션 같은 기술적인 잔버그(Implementation Bug)를 뚫고 들어왔다.
    2. 프레임워크 방어망의 고도화: Spring, Django 같은 현대 프레임워크들이 기술적 버그(SQLi, XSS)를 기계적으로 거의 100% 방어해 내는 수준에 이르렀다.
    3. 비즈니스 로직 해킹의 부상 (현재): 기술적 구멍이 막히자, 해커들은 "개발자가 빼먹은 비즈니스 예외 로직"을 노리기 시작했다. 이에 OWASP 재단은 2021년, "이제 코드(구현) 고치는 건 그만하고, 기획(설계)부터 똑바로 해라!"라는 분노를 담아 Insecure Design(A04)을 거대한 새로운 카테고리로 신설해 버렸다.
  • 📢 섹션 요약 비유: 구현(코딩) 오류가 옷의 **'실밥이 터져 구멍이 난 것'**이라면, 안전하지 않은 설계는 실밥은 완벽하게 바느질되었는데 애초에 디자인을 **'목 들어가는 구멍을 막아놓고 소매를 3개로 만든 옷'**으로 재단해버린 것입니다. 옷감(코드)을 아무리 비싼 걸 써도, 애초에 설계도(패턴)가 망했기 때문에 옷을 찢지 않고는 절대 고쳐 입을 수 없습니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

1. Insecure Design이 터지는 대표적 3대 시나리오 (아키텍처의 맹점)

코드 스캐너(SAST)는 절대 이런 논리 결함을 잡아주지 않는다. 오직 아키텍트의 뇌지컬로 쳐내야 한다.

  1. 상태(State) 전이의 무결성 검증 실패
    • 설계 빵꾸: 쇼핑몰에서 장바구니 ➡ 결제 ➡ 배송지 입력 순서로 페이지가 넘어간다. 그런데 해커가 결제를 안 하고, URL에 직접 http://shop.com/address를 쳐서 3단계로 워프해버렸다.
    • 현상: 서버는 "2단계를 거치지 않은 놈이 3단계로 올 리가 없다"는 순진한 설계 믿음 때문에, 결제 안 한 놈에게 물건을 무료로 배송해 버린다.
  2. 비즈니스 한계(Limit) 및 율속(Rate) 통제 누락
    • 설계 빵꾸: 선착순 쿠폰 발급 버튼을 만들었다. 해커가 매크로로 이 버튼을 1초에 1,000번 눌렀다.
    • 현상: 1인 1매 정책이 설계(기획서)에 없었거나, 1초에 광클하는 것(동시성 레이스 컨디션)을 막는 락(Lock) 아키텍처가 누락되어 쿠폰이 1,000장 털린다.
  3. 가장 약한 고리(Weakest Link)를 통한 인증 우회
    • 설계 빵꾸: 로그인 보안은 MFA(지문+문자 인증)로 군대처럼 빡세게 막아놨다. 그런데 하단에 조그맣게 있는 [비밀번호 찾기] 기능은 "이름, 전화번호" 2개만 덜렁 맞추면 비밀번호를 초기화해 준다.
    • 현상: 해커는 철통같은 로그인을 공격하지 않는다. 쓰레기처럼 널린 이름과 폰번호 2개로 비번을 합법적으로 바꿔버리고 새 비번으로 로얄 로그인해 버린다. 가장 멍청한 우회 설계다.

2. 안전한 설계(Secure Design)를 위한 3대 핵심 원칙

  1. 심층 방어 (Defense in Depth): 프론트엔드에서 수량을 -1 못 치게 막았더라도, 뚫릴 것을 가정하고 백엔드 서버에서 다시 검사하고, DB 프로시저에서도 또 검사하는 3중 콘크리트 장벽을 설계한다.
  2. 기본 거부 (Deny by Default): API 접근이든, 데이터 조회든 "명확히 허락된 룰(Whitelist)이 없으면, 전 우주의 모든 요청을 100% 무조건 거부(Block)한다"는 극도의 폐쇄적 아키텍처 뼈대를 잡는다.
  3. 위협 모델링 (Threat Modeling): 개발 전, 칠판에 DFD 도면을 펴놓고 STRIDE(해커의 6가지 꼼수)를 대입하여 악의적인 우회 시나리오를 몽땅 쥐어짜 내 방어 코드를 선제적으로 박는다.
  • 📢 섹션 요약 비유: 멍청한 설계는 **'철통같은 금고 문을 달아놓고, 열쇠를 금고 위 화분 밑에 숨겨두는 설계'**입니다. 금고 문을 아무리 두껍게(코딩) 만들어봤자, 우회로(비밀번호 찾기, 파라미터 조작)가 너무 쉽게 노출되어 있으면 해커는 코웃음을 치며 문을 땁니다. 심층 방어는 "화분 밑에 열쇠를 둬도, 금고가 지문 인식을 한 번 더 요구하게 만드는(2중, 3중 안전망)" 치밀한 악몽(Paranoia)의 아키텍처입니다.

Ⅲ. 융합 비교 및 다각도 분석

1. 결함의 종류: 구현 결함(Implementation Bug) vs 설계 결함(Design Flaw)

보안 결함을 고치는 데 드는 피눈물의 단위가 완전히 다르다.

척도구현(코딩) 결함 (Implementation Bug)설계 결함 (Design Flaw / Insecure Design)
발생 원인개발자의 손가락 실수 (오타, 문법 미숙, 방어 코드 누락)기획자/아키텍트의 뇌 구조적 실수 (예외 케이스 상상 실패)
탐지 방법취약점 스캐너(SAST/DAST) 돌리면 1분 만에 시뻘겋게 뜸.기계가 절대 못 잡음. 인간 해커가 직접 뇌로 뚫어보거나 위협 모델링 회의를 해야만 나옴.
수정(Patch) 난이도코드 1줄 (예: +PreparedStatement로) 바꾸면 즉시 끝남.DB 구조 파괴, 로직 흐름 역전 등 시스템 전체 뼈대를 갈아엎어야 함. (비용 100배)
대표 OWASP 항목A03 인젝션, A05 보안 설정 오류, A02 암호화 오류 등A04 안전하지 않은 설계, A01 취약한 접근 제어

과목 융합 관점

  • 소프트웨어 공학 (시프트 레프트, Shift-Left): Insecure Design을 막는 유일무이한 공학적 해법은 시간 역행(Shift-Left)이다. 코드를 치는 순간(구현) 이미 설계는 끝난 상태라 늦었다. 개발자가 키보드에 손을 얹기 전인 기획/요구사항 명세 단계에 보안 팀원(또는 깐깐한 아키텍트)이 난입하여, "기획자님, 이 시나리오에 해커가 중간에 끊고 도망가면 포인트는 어떻게 회수할 건가요?"라고 딴죽을 걸어(Threat Modeling) 기획서 텍스트 자체를 수정해 버리는 가장 앞단의 방어벽이 생명이다.

  • 애자일 (BDD, 행위 주도 개발): 기획자와 개발자가 엑셀로 소통하면 무조건 설계 빵꾸가 난다. 이를 융합하기 위해 BDD(Cucumber)를 쓴다. 기획자가 [Given] 마이너스 수량을 장바구니에 담고, [When] 결제를 요청하면, [Then] 서버는 '불가능' 에러를 뱉는다 라고 악의적인 해킹 시나리오(Abuse Case)를 영어 문장으로 명세서에 적어버린다. 이 기획서가 그대로 자동화된 테스트 코드로 치환되어, 개발자가 이 멍청한 설계를 빼먹고 코딩하지 못하도록 족쇄(Quality Gate)를 채우는 애자일의 승리다.

  • 📢 섹션 요약 비유: 구현(코딩) 결함은 **'요리사가 실수로 소금을 두 번 친 짠맛'**입니다. 다음부터 소금 한 번만 치면 바로 고쳐집니다. 설계 결함(Insecure Design)은 기획자가 애초에 **'초콜릿 케이크에 고등어를 넣으라는 레시피'**를 준 것입니다. 요리사가 아무리 프라이팬을 씻고 예쁘게 구워봤자(완벽한 코딩) 쓰레기 요리가 나옵니다. 맛을 살리려면 조리법을 고치는 게 아니라 레시피(도면) 자체를 찢어버려야 합니다.


Ⅳ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — '환불 꼼수'가 빚은 마이크로서비스(MSA) 트랜잭션의 파국: 쇼핑몰을 MSA로 분리했다. 주문 서버(A)와 결제 취소 서버(B)가 찢어졌다. 고객이 아이패드를 주문(성공)한 뒤, 물건이 출고되는 그 0.1초의 찰나에 미친 듯이 '결제 취소' 버튼을 광클(Race Condition)했다. A서버는 출고 지시를 내렸고, B서버는 동기화 속도 지연(Eventual Consistency) 때문에 "아직 출고 안 됐네? 취소 환불 처리해!"라고 돈을 돌려줘 버렸다. 아이패드는 배송됐고 돈도 돌려받는 무한 복사의 기적이 터졌다.

    • 아키텍트의 해결책: 비동기 분산 시스템에 대한 방어적 설계(Defensive Design) 붕괴다. 코드가 틀린 게 아니다. MSA의 분산 트랜잭션 맹점을 아키텍트가 설계에 반영하지 않은 죄다. 아키텍트는 이런 레이스 컨디션을 막기 위해, DB 단위에 비관적 락(Pessimistic Lock)을 강제로 걸거나, 사가 패턴(Saga Pattern) 및 보상 트랜잭션(Compensation) 도면을 기획 첫날 완벽하게 그려놓고 "취소 요청이 오면 무조건 출고 상태를 Lock 걸고 돈을 줘라"는 동시성 통제 아키텍처를 강제했어야 했다.
  2. 시나리오 — 인증 정보(Token) 탈취 후 무제한 좀비 패스: 프론트엔드가 로그인할 때 JWT 토큰 만료 시간(Expiration)을 귀찮다며 1년으로 세팅해 달라고 했다. 백엔드가 "오케이" 하고 설계했다(Insecure Design). 유저가 피시방에서 로그인하고 깜빡 잊고 로그아웃 안 하고 갔다. 6개월 뒤 해커가 그 피시방 PC를 털어서 남아있던 JWT 토큰을 훔쳤다. 이 토큰 하나로 해커는 6개월 내내 이 유저의 이름으로 쇼핑하고 돈을 뺐다. 서버는 토큰이 1년짜리니까 아무 의심 없이 100% 정상 요청으로 받아들였다.

    • 아키텍트의 해결책: 세션 및 자원 라이프사이클 통제(Lifecycle Control)의 실패다. 아키텍트는 개발자 편의(1년 무한 토큰)라는 악마의 유혹을 설계 단계에서 쳐내야 한다. 아무리 코딩을 잘해도 권한의 유효기간이 무한대면 그 자체로 폭탄이다. 아키텍트는 "JWT 엑세스 토큰(Access Token)의 수명은 30분으로 강제 박제한다. 만료되면 리프레시 토큰(Refresh Token)으로 몰래 연장해 오고, 수상한 IP 접속 시 리프레시 토큰마저 강제 폐기(Revoke)하는 블랙리스트(Redis) 아키텍처를 도입하라"는 빡빡한 생명주기 통제 도면을 하달해야 한다.

도입 체크리스트

  • 조직적: 오용 케이스 (Abuse Case / Misuse Case) 문서를 작성하는가? 기획팀이 던져주는 요구사항 명세서(SRS)에는 항상 "사용자는 ~할 수 있다(Use Case)"라는 예쁜 꽃길만 100개 적혀있다. 보안 조직과 아키텍트는 문서 뒷장에 무조건 **"해커는 ~를 우회하여 ~를 파괴할 수 있다(Abuse Case)"**라는 블랙 시나리오 100개를 강제로 창작해서 덧붙여야 한다. 이 블랙 시나리오가 없는 기획 문서는 폭탄과 다름없다.
  • 기술적: 안전한 기본값 (Secure by Default) 원칙이 인프라에 스며있는가? 스프링(Spring)을 부팅하거나 클라우드(AWS) S3 버킷을 팔 때, "나중에 보안 세팅해야지" 하고 기본(Default) 옵션으로 넘기면 무조건 퍼블릭(전체 공개)으로 뚫려있다. 아키텍트는 인프라 생성 스크립트(Terraform)나 프로젝트 뼈대(Boilerplate) 코드를 나눠줄 때, 애초에 모든 방화벽과 비밀번호 규칙이 가장 빡세게 막혀있는 '폐쇄형 템플릿'을 디폴트로 배포하여 멍청한 개발자의 우발적 실수를 구조적으로 원천 봉쇄해야 한다.

안티패턴

  • "보안은 나중에 붙이자 (Bolt-on Security)": 가장 흔하고 회사를 멸망시키는 안티패턴. "일단 다음 달이 오픈이니까 기능부터 쌩쌩 돌아가게 만들고, 암호화나 권한 체크 필터(Interceptor)는 배포 1주일 전에 껍데기로 한 번 슥 씌우자!"라고 미루는 짓. 막상 1주일 전에 필터를 씌우면, 그동안 짜놨던 모든 API와 프론트엔드 연동 로직이 시뻘건 403(권한 없음) 에러를 뿜으며 대폭발을 일으켜 결국 런칭이 3달 밀리거나 보안을 포기한 채 배포하게 된다. "보안은 시멘트 반죽할 때(설계) 모래랑 섞는 것이지, 건물 다 짓고 벽에 바르는 페인트가 아니다."

  • 📢 섹션 요약 비유: 나중에 보안을 덧붙이는 짓은, **'집 다 짓고 화장실 배관(보안망) 넣기'**와 똑같습니다. 벽돌과 타일(기능)을 완벽하게 다 깔고 도배까지 끝났는데, 화장실에 똥물이 안 빠진다는 걸 깨달았습니다. 이제 남은 길은 수천만 원을 들여 대리석 바닥을 함마드릴로 다 부수고 까발려서(기존 아키텍처 파괴) 배관을 억지로 구겨 넣는 절망적인 재재건축뿐입니다. 애초에 바닥을 깔 때(설계) 파이프부터 심었어야 합니다.


Ⅴ. 기대효과 및 결론

정량/정성 기대효과

구분기능 구현 100% 몰빵, 사후 보안 점검(AS-IS)위협 모델링 및 Abuse Case 기반의 설계 내재화 (TO-BE)개선 효과
정량배포 후 발견된 아키텍처 논리 결함 수정 비용 10x~100x기획 단계 도면 수정으로 결함 예방(수정 비용 0원)라이프사이클 전반의 보안 디버깅 매몰 비용 99% 절약
정량매크로, 레이스 컨디션 등 비즈니스 어뷰징 피해 10억선제적 율속(Rate Limiting) 및 트랜잭션 락(Lock) 설계해커의 꼼수에 의한 금전적/데이터 유출 피해액 제로화
정성"왜 맨날 막판에 코드를 뒤집으래?" 개발팀 분노 폭발기획 첫날부터 방어 로직 합의 후 마음 편하게 코딩 몰입무결점 설계에 기반한 개발 조직의 압도적 심리적 안정감(Confidence)

미래 전망

  • 보안의 설계 코딩화 (Security Policy as Code): 과거엔 기획서에 한글로 "권한 없는 자는 거부한다"라고 적는 게 설계였다. 미래엔 OPA(Open Policy Agent)나 AWS IAM JSON 같은 명확한 기계어 코드로 보안 룰(설계도)을 짜서 GitHub에 박아버린다. 개발자가 코드를 푸시하면 파이프라인이 이 보안 설계 코드와 일치하는지 1초 만에 자동 채점해 버리는 '설계도 자동 검수 시대'가 데브옵스를 지배하고 있다.
  • AI 주도 취약 설계 시뮬레이터 (AI Threat Simulator): 아키텍트가 칠판에 AWS 시스템 구성도(네모와 화살표)를 그려서 스캔하면, 초거대 AI가 1초 만에 "해커 1만 명을 뇌내 시뮬레이션 돌려본 결과, 저 화살표 구간에서 결제 꼼수(우회)가 일어날 확률 99%!"라고 인간이 절대 상상하지 못할 엣지 케이스를 찾아 훈수를 두는 AI 아키텍트 조력자의 등장이 가시화되었다.

참고 표준

  • OWASP Top 10 (A04: Insecure Design): 2021년에 4위로 화려하게 신규 진입하며, 전 세계 IT 업계에 "제발 코딩만 하지 말고 머리(설계) 좀 써라!"라는 일침을 날린 위대한 카테고리 시프트 선언.
  • STRIDE (위협 모델링): 안전하지 않은 설계(Insecure Design)를 쳐부수기 위해 회의실에서 꺼내 들어야 하는 유일무이한 6가지 마법의 해킹 추리 공식. (이전 장 475번)

안전하지 않은 설계(Insecure Design) 카테고리의 탄생은, 소프트웨어 보안이 '개발자의 타자 연습(구현)'에서 '아키텍트의 두뇌 싸움(설계)'으로 전쟁의 터전을 옮겼음을 알리는 거대한 신호탄이다. 당신의 프론트엔드 버튼이 예쁘게 빛나고, 백엔드 서버가 0.01초 만에 반응한다고 자만하지 마라. 해커는 그 화려한 정문을 부수지 않는다. 그들은 설계 도면에 희미하게 남겨진 빈틈, 즉 '이 과정을 건너뛰고 바로 저기로 가면 시스템이 속지 않을까?'라는 비즈니스 로직의 미로 속으로 교활하게 숨어든다. 기술사는 눈에 보이는 코드를 다듬는 노가다를 넘어, 눈에 보이지 않는 유저의 엉뚱한 행동(Abuse)의 궤적을 예측하고, 그들이 상상하기도 전에 미리 콘크리트 장벽을 치는 체스판의 지배자가 되어야 한다.

  • 📢 섹션 요약 비유: Insecure Design을 막아내는 아키텍트는 무적의 성(Castle)을 지을 때 **'성벽을 높이는 자'**가 아니라, **'우물과 하수구를 막는 자'**입니다. 멍청한 설계자는 적의 화살(SQL 인젝션)을 막으려 성벽만 10미터로 높게 칩니다(시큐어 코딩). 하지만 적은 화살을 쏘지 않고 성벽 밑으로 흐르는 더러운 하수구(비즈니스 로직 우회)를 타고 기어 들어와 왕의 목을 땁니다. 훌륭한 아키텍트는 성벽을 쌓기도 전인 도면 그리기 첫날에, "하수구 파이프 안에는 무조건 쇠창살(인가 필터)을 박아라!"라고 못을 박는 통찰력을 지닌 자입니다.

📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
위협 모델링 (Threat Modeling)안전하지 않은 설계(Insecure Design)라는 병을 예방할 수 있는 전 우주 유일의 백신. 코딩하기 전에 화이트보드 펴놓고 털릴 구멍 상상하기. (이전 장 474번)
시프트 레프트 (Shift-Left)이 모든 사상의 뼈대. 오른쪽 끝(배포)에서 취약점을 잡으면 재앙이니까, 아예 왼쪽 맨 끝(기획/설계)에서 지우개로 쓱 지워서 방어하자는 위대한 철학. (이전 장 466번)
취약한 접근 제어 (A01)Insecure Design의 멍청함이 코드(서버)에서 구체적으로 폭발했을 때 나타나는 최악의 1위 결과물. "권한 체크 안 함"이라는 설계 구멍의 실사판. (이전 장 478번)
오용 사례 (Abuse Case)유즈 케이스(Use Case)가 천사 고객을 위한 기획서라면, 어뷰즈 케이스는 흑마법사(해커)가 우리 앱을 갖고 놀 악질 시나리오 100가지를 기획자가 상상해서 적어놓는 흑마법 방어 주문서.
제로 트러스트 (Zero Trust)안전하지 않은 설계의 기본 뼈대를 뜯어고치는 사상. "내부 시스템이니까, 회원가입 유저니까 안전하겠지"라는 순진한 설계를 부수고, 1분 1초마다 의심하는 편집증적 아키텍처.

👶 어린이를 위한 3줄 비유 설명

  1. 내가 레고로 거대한 은행을 튼튼하게 지었어요! 절대 안 무너질 자신(완벽한 코딩)이 있었죠.
  2. 그런데 친구가 훔쳐보더니, "와, 벽은 엄청 튼튼한데 금고 문에 자물쇠를 안 달았네?" 하고 그냥 문 열고 돈을 가져갔어요(논리적 해킹).
  3. 이렇게 레고 블록 자체는 단단하게 꽉 끼워졌지만, 애초에 은행 설계도를 그릴 때 자물쇠 다는 걸 깜빡하고 멍청하게 지어버린 진짜 무서운 실수를 **'안전하지 않은 설계(Insecure Design)'**라고 한답니다!