문의폼, 예약폼, 회원가입이 있다면 배포 전에 꼭 확인해야 할 보안 체크리스트

카페인코더·2026년 6월 18일

AI 웹앱 보안 점검

목록 보기
5/11
post-thumbnail

AI로 웹앱을 만들다 보면 가장 먼저 붙는 기능 중 하나가 문의폼, 예약폼, 회원가입이다.

이 기능들은 얼핏 보면 단순 입력창처럼 보인다.
이름, 이메일, 전화번호, 날짜, 요청사항 정도만 받으니까 가볍게 느껴질 수도 있다.

하지만 실제로는 그렇지 않다.

문의폼이나 회원가입이 붙는 순간부터, 내 서비스는 개인정보를 다루는 서비스가 된다.

이번 글에서는 AI로 만든 웹앱에 문의폼, 예약폼, 회원가입 기능이 있다면 배포 전에 무엇을 먼저 확인해야 하는지 정리해보려고 한다.


1. 어떤 정보를 왜 받고 있는지 먼저 정리한다

가장 먼저 해야 할 일은 기술 설정이 아니라 수집 항목 정리다.

예를 들어 문의폼이라면 흔히 이런 항목이 들어간다.

  • 이름
  • 이메일
  • 전화번호
  • 회사명
  • 문의 내용

예약폼은 여기에 더해질 수 있다.

  • 예약 날짜
  • 예약 시간
  • 인원 수
  • 방문 목적
  • 요청사항

회원가입은 보통 이렇다.

  • 이메일
  • 비밀번호
  • 이름 또는 닉네임
  • 전화번호
  • 주소
  • 프로필 정보

여기서 먼저 물어야 할 질문은 간단하다.

이 정보가 정말 필요한가?

예를 들어:

  • 답변을 이메일로만 보낼 거라면 전화번호가 꼭 필요한가?
  • 예약 확정 문자 발송이 없다면 휴대전화 번호를 굳이 받는가?
  • 회원가입에 주소 정보가 정말 필요한가?

많이 받는다고 좋은 것이 아니다.
불필요한 정보가 많을수록 저장, 접근권한, 삭제, 노출 리스크가 같이 커진다.


2. 브라우저 검증만 믿지 않는지 확인한다

폼을 만들 때 프론트엔드에서 흔히 이런 검증을 한다.

  • 필수 입력
  • 최소 길이
  • 최대 길이
  • 이메일 형식
  • 전화번호 형식

이건 UX에는 좋다.
하지만 보안 검증으로는 부족하다.

OWASP도 입력값 검증은 반드시 서버 측에서 수행되어야 한다고 설명한다. 브라우저 측 JavaScript 검증은 우회될 수 있기 때문이다.

즉 이런 구조면 부족하다.

프론트엔드에서만 required, minLength, pattern 체크
서버는 그대로 저장

최소한 서버에서도 확인해야 한다.

  • 입력 길이 제한
  • 허용 문자/형식 검증
  • 날짜/시간/인원 수 범위 검증
  • 선택값 allowlist 검증
  • 비정상적으로 큰 payload 차단

예를 들어 예약폼이라면 이런 식의 의미 검증도 필요하다.

예약 날짜가 과거 날짜는 아닌가?
인원 수가 0명 또는 10000명은 아닌가?
선택 가능한 시간대만 허용하는가?

기술적으로 동작하는 것과, 이상한 입력을 막는 것은 다르다.


3. 입력한 정보가 어디에 저장되는지 알아야 한다

문의폼이나 예약폼을 만들 때 실제 저장 위치는 생각보다 다양하다.

  • DB 테이블
  • Supabase
  • Firebase
  • Google Sheets
  • Notion
  • 이메일 수신함
  • Slack/Discord 알림
  • Airtable
  • CRM

문제는 UI를 만든 사람과 데이터를 저장하는 사람이 다를 수 있다는 점이다.
특히 AI로 빠르게 만든 프로젝트에서는 더 자주 그렇다.

최소한 이 질문은 답할 수 있어야 한다.

이 폼 데이터는 어디에 저장되는가?
누가 그 데이터를 볼 수 있는가?
운영자 외 다른 사용자가 접근할 수 있는가?

Supabase나 Firebase를 쓴다면 이전 글에서 다룬 RLS, Security Rules가 다시 중요해진다. [지난글-Supabase/Firebase를 쓴다면 꼭 봐야 할 권한 설정 문제]
문의 데이터나 예약 데이터는 보통 공개 데이터가 아니다.

다음 상황은 다시 봐야 한다.

  • 문의 테이블이 로그인 사용자 전체에게 열려 있음
  • 예약 데이터가 인증만 되면 누구나 읽힘
  • 관리자 페이지 없이도 DB API로 데이터 조회 가능
  • 저장소는 private라고 생각했는데 실제로는 public bucket임

4. 관리자 알림이 의도치 않게 개인정보를 퍼뜨리지 않는지 본다

폼 데이터는 DB에만 저장되지 않는 경우가 많다.

실무에서는 보통 이런 흐름이 붙는다.

사용자 제출
→ 이메일 발송
→ Slack 알림
→ Notion 기록
→ Google Sheets 저장

문제는 이 흐름이 편한 만큼 개인정보가 넓게 퍼질 수 있다는 점이다.

예를 들어:

  • 문의 내용 전체가 Slack 공개 채널에 올라감
  • 예약자 전화번호가 팀 전체 메일로 전송됨
  • 회원가입 정보가 여러 외부 툴에 복제됨
  • 관리자 알림 메일에 토큰이나 내부 링크가 포함됨

특히 이메일은 전달, 포워딩, 검색, 아카이빙이 쉽기 때문에 더 조심해야 한다.

질문은 이것이다.

이 알림을 꼭 이렇게 상세하게 보내야 하는가?
모든 운영자가 이 전체 정보를 봐야 하는가?

5. 스팸과 자동 제출을 막는 장치가 있는지 확인한다

문의폼과 예약폼은 기능상 공개되는 경우가 많다.
그래서 자동화 공격에 매우 취약할 수 있다.

대표적인 문제는 이런 것들이다.

  • 봇이 무한 제출
  • 스팸 홍보 메시지 대량 발송
  • 예약 시간대 점유 시도
  • 회원가입 대량 생성
  • 비밀번호 추측/credential stuffing
  • 이메일 인증 메일 폭탄

회원가입이 있다면 OWASP Authentication Cheat Sheet에서 말하는 자동화 공격 방어도 같이 봐야 한다.

최소한 아래 중 일부는 있어야 한다.

  • rate limiting
  • CAPTCHA 또는 bot protection
  • 이메일 인증
  • 로그인/회원가입 시도 제한
  • 비정상 요청 패턴 탐지
  • 동일 IP/디바이스 과다 요청 차단

문의폼은 “로그인 없이 누구나 써야 하니까” 더 위험할 수 있다.
예약폼은 “자리가 한정되어 있으니까” 비즈니스 영향이 더 클 수 있다.
회원가입은 “계정이 생기니까” 이후 보안 문제가 더 커질 수 있다.


6. 로그인된 사용자의 폼이라면 CSRF도 봐야 한다

모든 폼에 CSRF가 필요한 것은 아니다.
하지만 로그인된 상태에서 서버 데이터를 바꾸는 요청이라면 확인해야 한다.

예를 들어 이런 경우다.

  • 로그인 사용자의 프로필 수정
  • 회원 정보 변경
  • 예약 변경/취소
  • 문의 내역 수정
  • 관리자 답변 등록

OWASP는 CSRF 방어에 대해 synchronizer token pattern 같은 서버 측 토큰 기반 방식을 권장한다.

핵심은 이거다.

사용자가 로그인된 상태일 때,
다른 사이트가 사용자의 브라우저를 악용해서
내 서비스에 원치 않는 요청을 보내지 못하게 해야 한다.

즉 “폼이 있으니 제출된다”가 아니라,
“이 요청이 정말 우리 서비스에서 의도된 사용자 행동인가”를 확인해야 한다.


7. 로그와 에러 메시지에 개인정보를 남기지 않는지 본다

폼 기능을 붙이면 디버깅을 위해 로그를 남기고 싶어진다.

예를 들어 이런 코드가 쉽게 들어간다.

console.log("signup payload", req.body)
console.log("inquiry submitted", formData)

개발 중에는 편하다.
운영에서는 위험할 수 있다.

OWASP Logging Cheat Sheet는 로그에 남기면 안 되는 정보로 인증 비밀번호, 토큰, DB 연결 문자열, 일부 민감한 개인정보 등을 명시한다. 이메일 주소나 전화번호 같은 비민감 개인정보도 필요 이상 남기지 말고, 필요 시 비식별화나 축약을 고려하라고 안내한다.

다음은 피하는 게 좋다.

  • 비밀번호 전체 로그
  • 이메일/전화번호 원문 전체 로그
  • 문의 내용 전체 로그
  • access token, reset token 로그
  • 내부 관리자 URL이나 DB 정보가 섞인 에러 메시지
  • 사용자가 입력한 내용을 그대로 에러 화면에 출력

특히 회원가입 기능에서는 더 민감하다.

운영 환경 로그는 디버깅 도구이지, 개인정보 보관소가 아니다.


8. 회원가입 기능이 있다면 추가로 확인할 것

문의폼, 예약폼보다 회원가입은 한 단계 더 무겁다.

이유는 단순하다.
사용자 계정과 인증 정보가 생기기 때문이다.

최소한 이것들은 봐야 한다.

비밀번호를 직접 저장하지 않는가

직접 인증을 구현한다면 평문 저장은 물론 안 되고, 적절한 해시 방식이 필요하다.
가능하면 Supabase Auth, Firebase Authentication 같은 검증된 제품을 우선 검토하는 편이 낫다.

이메일 인증 또는 본인 확인 흐름이 있는가

가입 직후 아무 검증 없이 바로 중요한 기능을 다 열어두는 구조라면 악용 가능성이 커질 수 있다.

중복 계정 확인 응답이 과하게 친절하지 않은가

예를 들어 “이 이메일은 이미 가입되어 있습니다” 같은 메시지는 UX에는 좋지만, 경우에 따라 계정 존재 여부를 추측하는 데 쓰일 수 있다. 서비스 성격에 따라 균형을 봐야 한다.

비밀번호 재설정 링크가 안전한가

토큰이 짧거나, 너무 오래 유효하거나, URL이 로그에 남는 구조면 다시 봐야 한다.


9. 삭제와 보관 기준도 미리 생각해둔다

이건 초반 MVP에서 자주 놓치는 부분이다.

폼을 만들고 저장은 했는데, 그다음이 없다.

  • 언제 지우는가?
  • 오래된 문의는 어떻게 처리하는가?
  • 탈퇴한 회원 데이터는 어떻게 다루는가?
  • 테스트 데이터는 언제 정리하는가?
  • 스프레드시트/노션/슬랙에 흩어진 데이터는 어떻게 회수하는가?

기능이 잘 되는 것만으로는 끝나지 않는다.
특히 문의폼과 예약폼은 생각보다 빨리 데이터가 쌓인다.

최소한 이런 기준은 필요하다.

무엇을 저장하는가
왜 저장하는가
누가 보는가
얼마나 보관하는가
언제 삭제하는가

배포 전에 확인할 체크리스트

문의폼, 예약폼, 회원가입 기능이 있다면 배포 전에 아래 항목은 한 번씩 체크해보는 것이 좋다.

  • 꼭 필요한 정보만 받고 있는가
  • 입력값 검증을 서버에서도 하고 있는가
  • 데이터 저장 위치를 정확히 알고 있는가
  • 문의/예약/회원 데이터가 관리자 외 다른 사용자에게 보이지 않는가
  • 이메일/Slack/Notion/Sheets로 개인정보가 과하게 퍼지지 않는가
  • rate limiting, CAPTCHA, 이메일 인증 같은 자동화 방어가 있는가
  • 로그인된 상태에서 데이터 변경이 일어나는 폼이라면 CSRF 방어가 있는가
  • 비밀번호, 토큰, 문의 내용, 이메일, 전화번호가 로그에 과하게 남지 않는가
  • 회원가입이라면 인증/비밀번호 재설정 흐름이 안전한가
  • 보관/삭제 기준이 정리되어 있는가

마무리

문의폼, 예약폼, 회원가입은 기능적으로는 단순해 보인다.

하지만 보안 관점에서는 이런 문제들이 한 번에 붙는다.

  • 개인정보 수집
  • 접근 권한
  • 스팸/자동화 공격
  • 인증
  • 로그 노출
  • 운영자 알림 확산
  • 삭제와 보관 기준

즉 이런 기능을 붙이는 순간부터 내 서비스는 그냥 “화면 몇 개 있는 웹앱”이 아니라, 사용자 데이터를 책임지는 서비스가 된다.

개인적으로는 AI로 만든 웹앱일수록 이 지점을 더 의식해야 한다고 생각한다.
기능은 빠르게 붙일 수 있지만, 데이터 책임은 자동으로 해결되지 않기 때문이다.

다음 글에서는 관리자 페이지를 만들었다면 배포 전에 꼭 확인해야 할 보안 포인트를 정리해보려고 한다.


참고자료

profile
꾸준히 기록 중 입니다.

0개의 댓글