Convince-X ConvinceX
← Stories

보안 감사를 AI에게 맡겼더니
— OWASP 대응기

"아직 사용자도 없는데 보안이 왜 필요해?"

이 질문을 스스로에게도 했습니다. 사용자 0명인 서비스에 OWASP 보안 감사라니. 과하지 않나? 하지만 한 번 터지면 끝이라는 걸 알고 있었습니다. 특히 채용 데이터를 다루는 서비스에서 개인정보 유출이 발생하면, 사업 자체가 끝납니다.

그래서 사용자가 오기 전에 먼저 보안을 잡기로 했습니다. AI에게 맡겨서.

• • •

1인 SaaS도 해킹당합니다

"우리 같은 작은 서비스를 누가 해킹하겠어?"라는 생각은 위험합니다. 실제로 자동화된 봇이 인터넷을 스캔하면서 취약한 서비스를 찾습니다. 서비스 크기와 상관없이요.

클라우드에 배포한 첫 주에 서버 로그를 보고 놀랐습니다. 아무에게도 알리지 않은 URL인데, 하루 수십 건의 비정상 요청이 들어오고 있었습니다. /admin, /wp-login.php, /.env 같은 경로를 자동으로 탐색하는 봇들이었습니다.

실제 로그에서 발견된 공격 시도들

GET /admin — 관리자 페이지 탐색
GET /.env — 환경 변수(API 키, DB 비밀번호) 탈취 시도
GET /wp-login.php — WordPress 기본 로그인 페이지 탐색
POST /api/login (대량) — 브루트포스 로그인 시도
GET /api/users?id=1 OR 1=1 — SQL 인젝션 시도

이걸 보고 보안이 선택이 아니라 필수라는 걸 체감했습니다. 사용자가 0명이어도, 서비스가 인터넷에 올라간 순간부터 공격 대상이 됩니다.

• • •

OWASP Top 10이란

OWASP(Open Web Application Security Project)는 웹 애플리케이션 보안에 대한 오픈 프로젝트입니다. "Top 10"은 가장 흔하고 위험한 10가지 보안 취약점 목록인데, 업계 표준으로 통합니다.

OWASP Top 10 (2021) — Convince-X 대응 현황
A01 Broken Access ControlDONE
A02 Cryptographic FailuresDONE
A03 Injection (XSS, NoSQL)DONE
A04 Insecure DesignDONE
A05 Security MisconfigurationDONE
A06 Vulnerable ComponentsDONE
A07 Auth FailuresDONE
A08 Software & Data IntegrityDONE
A09 Logging & MonitoringDONE
A10 SSRFDONE

10개 항목 전체를 대응한다는 건, 1인 개발자 입장에서 상당한 작업량입니다. 하지만 AI가 각 항목에 대한 구체적인 대응 방안을 제안하고, 코드를 작성하고, 테스트를 만들어줬기 때문에 가능했습니다.

• • •

XSS: 보이지 않는 위협

XSS(Cross-Site Scripting)는 공격자가 웹사이트에 악성 스크립트를 주입하는 공격입니다. 예를 들어, JD 입력 필드에 <script>alert('hacked')</script>를 넣으면, 다른 사용자의 브라우저에서 그 스크립트가 실행될 수 있습니다.

채용 서비스에서 XSS가 특히 위험한 이유가 있습니다. JD나 이력서 데이터에 악성 코드를 심으면, 그 데이터를 열람하는 다른 리크루터나 관리자의 세션을 탈취할 수 있습니다.

대응 방법은 세 겹입니다.

1층: 입력 살균
모든 사용자 입력에서 HTML 태그와 스크립트를 제거합니다. DOMPurify 라이브러리로 서버 사이드 살균.
2층: 출력 인코딩
데이터를 화면에 렌더링할 때 HTML 엔티티로 인코딩. <가 &lt;로 변환되어 실행 불가.
3층: CSP 헤더
Content Security Policy 헤더로 인라인 스크립트 실행 자체를 차단. 브라우저 레벨 방어.

보안 미들웨어가 CSP 헤더를 포함한 여러 보안 헤더를 한 번에 설정해줍니다. 미들웨어 한 줄로 X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security 등 10여 개의 보안 헤더가 적용됩니다.

• • •

인증과 세션 보안

Convince-X는 OAuth + 토큰 기반 보안 인증 방식으로 인증을 처리합니다. 사용자가 소셜 계정으로 로그인하면, 서버가 인증 토큰을 발급하고, 이후 모든 API 요청에 이 토큰이 포함됩니다.

보안의 핵심 포인트들.

보안 항목 위험 대응
토큰 저장 위치 클라이언트 저장소에 저장 시 XSS로 탈취 가능 보안 쿠키에 저장 — JS로 접근 불가
토큰 만료 토큰 탈취 시 영구 사용 가능 다중 보안 레이어 (토큰 만료 + 갱신 회전)
요청 위변조 쿠키 자동 전송으로 위조 요청 요청 위변조 방지 + SameSite 쿠키
NoSQL 인젝션 DB 쿼리 조작 입력값 검증 + DB 쿼리 보호

보안 쿠키 설정은 단순하지만 강력한 방어입니다. JavaScript로 쿠키에 접근할 수 없으니, XSS가 성공하더라도 토큰 탈취가 불가능합니다. 이 한 가지 설정만으로 공격 표면이 크게 줄어듭니다.

• • •

Rate Limiting과 브루트포스

Rate Limiting은 "일정 시간 내 요청 횟수를 제한하는 것"입니다. 왜 필요한가? 브루트포스 공격 때문입니다.

브루트포스는 비밀번호나 토큰을 무차별 대입하는 공격입니다. Google OAuth를 쓰니 비밀번호 브루트포스는 없지만, API 엔드포인트에 대한 무차별 요청은 여전히 위험합니다. 서버 자원을 소진시켜 서비스를 마비시킬 수 있습니다.

Rate Limiting 전략

일반 API: 표준 요청 제한 적용
인증 관련: 엄격한 요청 제한 (브루트포스 방지)
AI 분석: 사용자별 시간당 제한
파일 업로드: 사용자별 시간당 제한

→ 엔드포인트별로 다르게 설정하는 게 핵심

모든 엔드포인트에 같은 제한을 걸면 안 됩니다. 로그인은 엄격하게, 검색은 약간 느슨하게. 사용자 경험과 보안 사이의 균형입니다.

• • •

414건의 보안 테스트

보안 대응을 코드로 구현하는 것과, 그 구현이 실제로 작동하는지 검증하는 것은 별개의 문제입니다. 그래서 414건의 보안 테스트를 만들었습니다.

127
XSS 방어
98
인증/세션
89
인젝션
100
위변조방지/Rate/기타

테스트는 단순히 "방어 코드가 있는지" 확인하는 게 아니라, "실제 공격 패턴을 시뮬레이션"합니다. 예를 들어 XSS 테스트는 실제 공격자가 사용하는 수십 가지 인코딩 우회 패턴을 입력으로 넣어봅니다.

재미있는 건, 테스트를 만드는 과정에서 실제 취약점이 발견됐다는 겁니다. 입력값 살균이 빠진 엔드포인트 3곳, 요청 위변조 방지 미검증 라우트 2곳. 테스트를 "만들었더니" 버그가 "잡혔습니다". 보안 테스트는 확인 도구인 동시에 발견 도구입니다.

보안 테스트가 발견한 취약점: 5건. 테스트가 없었다면 이 5건은 프로덕션에 그대로 올라갔을 겁니다.

• • •

보안은 기능이 아니라 기반입니다

보안을 "기능"으로 생각하면 후순위로 밀립니다. "먼저 기능을 만들고 나중에 보안을 추가하자"는 접근은 위험합니다. 나중에 추가하려면 기존 코드를 전부 뜯어고쳐야 하거든요.

Convince-X에서 보안은 처음부터 기반에 깔았습니다. 보안 미들웨어, 입력 살균, 보안 쿠키 — 이것들은 첫 번째 기능을 만들기 전에 설정했습니다.

보안을 나중에 하면
• 기존 코드 전체 리팩토링 필요
• "어디가 취약한지" 파악부터 난관
• 기능과 보안이 충돌 → 타협
• 사고 나서야 대응 (사후 약방문)
보안을 처음부터 하면
• 기반 설정 후 기능 추가 → 자연스러움
• 새 기능마다 자동으로 보안 적용
• 414건 테스트가 지속적 검증
• 프로덕션 보안 인시던트 0건

이 글을 쓰는 시점에 Convince-X의 프로덕션 보안 인시던트는 0건입니다. 물론 사용자가 적어서일 수도 있습니다. 하지만 414건의 보안 테스트가 매 배포마다 전부 통과하는 한, 최소한의 안전망은 갖춰져 있다고 말할 수 있습니다.

1인 SaaS라서 보안을 대충 해도 된다? 아닙니다. 1인이라서 더 철저해야 합니다. 대기업은 보안 사고가 나도 팀이 대응합니다. 1인은 사고 = 사업 종료입니다. AI가 보안 감사를 대신해주는 시대에, 비용 핑계는 더 이상 안 통합니다.

보안은 기능이 아니라 기반입니다. 건물의 철근 같은 것입니다. 보이지 않지만, 없으면 무너집니다.