soulee.dev

개발자가 놓치기 쉬운 보안 체크리스트

· 14 min read

정보보안영재원을 거쳐 정보보안 특기자로 대학에 입학했고, 정보보안기사도 공부했다. 지금은 개발자로 일하고 있지만 여전히 보안에 관심이 많아 관련 뉴스와 이슈를 챙겨보는 편이다.

그러다 보면 사고의 원인이 대단한 제로데이보다는, 알고는 있었지만 놓쳤던 기본적인 것들인 경우가 많다는 걸 느낀다.

개발자들이 생각보다 자주 놓치는 것들, 그리고 정보보안 부서라면 어떻게 접근하면 좋을지를 개발자 입장에서 정리해봤다.

원칙

  • 사용자를 불편하게 하는 건 보안이 아니다. 사용자가 불편을 느끼면 원점에서 다시 검토해봐야 한다.
  • 사람은 귀찮아서, 실수로, 본인의 이득을 위해 우회한다.
    • 가능하면 전부 자동화한다. 사람을 믿으면 안 된다.
  • 직접 만들지 마라. 특히 암호화 관련은 검증된 라이브러리를 쓴다.
  • 내부망이라고 안전한 게 아니다 (제로 트러스트)
  • 내부자도 믿지 않는다
  • 랜덤 값은 무조건 CSPRNG로 생성하고, 추측을 방지하기 위해 128비트 이상을 쓴다
  • Security by obscurity는 좋은 선택이 아니다. 어느 정도는 공개해야 적절한 audit을 받을 수 있다.
  • Webhook 같은 곳에서는 HMAC 같은 것을 신경 쓰자
  • 내가 개발하고 있는 게 CIA(기밀성, 무결성, 가용성) 중 어디에 문제가 될지 확인해보자

인증

비밀번호 정책

  • 주기적 변경을 강제하지 않는다
    • 사용자들은 비슷한 비밀번호를 돌려쓴다 ex) password1password2
    • 대신 다른 곳과 겹치지 않는 긴 비밀번호 + 패스워드 매니저를 활용한다.
  • 유출 비밀번호 차단
    • 가입/변경 시 HIBP Pwned Passwords 등을 활용하여 유출 여부를 확인해본다
    • 유출 정황이 있을 때만 변경을 요구한다
  • 길이 하한만 두고(8자 이상, 권장 12자) 문자 조합 규칙은 강제하지 않는다
  • 붙여넣기 허용, 최대 길이는 넉넉하게(64자 이상)
  • 키보드 보안 프로그램은 패스워드 매니저 활용을 어렵게 한다

비밀번호 저장

  • Argon2id, bcrypt, scrypt 등 안전한 알고리즘을 활용한다
    • SHA 해시 등은 무차별 대입 공격에 취약하다

다중 인증 (MFA)

  • 이차 인증은 필수적으로 설정한다
  • 최소한 TOTP (앱 기반)
  • Passkey / FIDO2 보안키 (보안성 높음)
  • SMS OTP / Email (SIM 스와핑, SS7에 취약하지만 없는 것보단 낫다)

계정 복구 / 비밀번호 초기화

  • 쉽게 해주면 안 된다. 철저한 신원 확인 필요
    • 계정 복구는 인증 체계 중 가장 약한 고리가 되기 쉽다. MFA를 걸어놔도 복구 절차가 허술하면 무의미하다

계정 존재 여부 노출 방지

  • 로그인 실패, 비밀번호 찾기, 회원가입 응답이 계정 유무를 알려주면 안 된다
  • 응답 메시지는 전부 동일한 문구로 통일한다
    • “가입되지 않은 이메일입니다” / “비밀번호가 틀렸습니다” 구분 금지
    • 비밀번호 찾기: 계정 유무와 무관하게 “메일을 보냈습니다”

무차별 대입 방어

  • 캡차를 넣어라
  • 계정 단위 + IP 단위 rate limit
  • 유출 비밀번호 차단
  • 디바이스 핑거프린트 / 행동 기반 위험 점수
  • 위험 신호가 있을 때만 추가 인증(step-up)
    • sudo mode
  • 계정 잠금은 오히려 DoS 공격에 활용될 수 있다 → 영구 잠금 대신 점진적 지연으로

인가 (Authorization)

  • 인가 로직을 한 곳(미들웨어/정책 엔진)에 모은다. 컨트롤러마다 흩어지면 반드시 누락이 발생한다

토큰

  • Revoke 불가능한 JWT는 위험하다
  • 차라리 opaque token + introspection이 나을 수도 있다

파일

  • 업로드/다운로드 시 경로를 노출하지 않는다. 랜덤 UUID가 낫다
  • 사용자 입력을 파일 경로에 직접 쓰지 마라 (Path Traversal 방어)
  • 업로드 검증
    • 확장자가 아니라 실제 콘텐츠 타입(magic number)을 검사한다
    • 파일 크기 제한, 압축 폭탄 대비
    • 업로드 파일은 웹루트 밖 또는 별도 스토리지에 저장한다
    • 실행 권한 제거
  • 업로드 도메인을 애플리케이션 도메인과 분리한다 (XSS 격리)

암호화

  • 암호화 라이브러리를 직접 만들지 말고 검증된 것을 쓴다
  • 키 관리
    • KMS 또는 시크릿 매니저 사용, 로테이션 절차
    • 암호화 키와 서명 키 분리
  • 양자내성 암호(PQC)도 이제 하긴 해야 한다

통신

  • 내부망이라고 안전하지 않다. 서비스 간 통신도 무조건 HTTPS
    • 더 나아가면 mTLS로 양방향 인증
    • 서비스 메시(Istio, Linkerd)를 쓰면 자동화할 수 있다
  • TLS 1.2 이상(가능하면 1.3), 구버전·약한 cipher suite 비활성화
  • 인증서 만료를 모니터링하고 자동 갱신(ACME)한다
  • HSTS + preload

설정

  • 패키지 이름 오타 조심 (typosquatting)
  • 의존성 관리
    • lockfile 커밋 + 무결성 해시 검증
    • 내부 패키지명을 선점하고 레지스트리 우선순위를 명시한다 (dependency confusion 방어)
    • 알려진 취약점 스캔을 CI에 통합한다 (npm audit, Dependabot, Trivy 등)
  • 기본 계정/기본 비밀번호 제거, 불필요한 기능·포트 비활성화
  • 디버그 모드, 상세 에러 페이지, 디렉터리 리스팅은 프로덕션에서 끈다
  • 관리 콘솔이 노출됐는지 확인한다 (Actuator, phpMyAdmin, Kibana 등)

데이터 보호

  • 아이디를 랜덤(UUID)으로 쓰면 정보 유출을 예방할 수 있다
    • DB 인덱스 성능 고려 시 UUIDv7 / ULID (시간 정렬성 있음)
    • 내부 PK는 정수, 외부 노출용은 별도 랜덤 ID로 분리하는 방식도 있다
  • 민감 페이지에는 Cache-Control: no-store (캐시 제어)

인프라 / 운영

Govern (사람과 문화)

기술 통제는 사람이 우회하면 무의미하다.

  • 그냥 교육하는 건 효과가 없다
    • 맥락 안에서 즉시 주는 피드백
      • PR 코멘트, IDE 경고, CI 실패 메시지에 “왜 위험한지” 한 줄 (교육을 문서가 아니라 도구에 녹인다)
    • 보안 챔피언 제도
    • 사고 공유
      • 다른 사례보다 우리 사례 postmortem
      • 비난 금지 (blameless)
    • 교육은 나쁜 기본값을 고칠 수 없다
      • “시크릿 하드코딩하지 마세요”를 매년 가르치는 것보다, 시크릿 매니저를 쉽게 쓰게 만들고 CI에서 자동 차단하는 게 낫다
      • 안전한 길이 가장 쉬운 길이어야 한다 (paved road / golden path)

Govern (내부자 위협)

  • 감시받는다고 느끼면 실수를 신고하지 않는다
  • 실수: 잘못된 대상에게 메일/파일 전송, 공개 버킷 설정, 잘못된 배포
  • 계정 탈취: 피싱·크리덴셜 스터핑으로 털린 계정
  • 악의: 퇴사 전 데이터 반출, 권한 남용, 내부 정보 판매
  • 상시 관리자 권한을 없애고 JIT(Just-In-Time)로 승격시킨다
    • 필요할 때 사유와 함께 신청, 시간 제한(예: 2시간) 후 자동 회수
    • 승인자와 신청자 분리
  • 권한은 정기적으로 재검토한다
    • 부서 이동 시 이전 권한이 남는 권한 누적(privilege creep)
    • “사용 이력 없는 권한 자동 회수” 규칙이 재검토보다 효과적이다
  • 오프보딩을 자동화한다
    • 퇴사자 계정은 대표적 사고 경로다
    • HR 시스템 ↔ IdP 연동으로 자동 비활성화
    • 개인 기기, SaaS 개별 계정, 공유 계정 비밀번호, API 키까지 포함
    • 사직 통보 ~ 실제 퇴사 사이가 위험 구간 → 민감 권한 선제 축소
  • 이중 통제 / 4-eyes
    • 프로덕션 DB 접근, 대량 데이터 내보내기, 결제·정산, 암호화 키 접근, 프로덕션 배포
    • 한 사람이 단독으로 완결할 수 없게
  • 대량 조회·다운로드 탐지

요즘 가장 수가 많은 내부자는 사람이 아니다. 비인간 신원(NHI)이다.

  • 서비스 계정, CI/CD 파이프라인, SaaS 연동 토큰, 웹훅, AI 에이전트
  • 사람 계정보다 훨씬 많고, 퇴사를 안 하니 권한이 쌓이기만 한다
  • 필요한 것
    • 토큰 인벤토리를 만들고 누가/무엇이/왜 소유하는지 오너를 지정한다
    • 만료 기한 강제, 장기 정적 크리덴셜 → 단기 동적 크리덴셜(OIDC 페더레이션 등)로 전환
    • 미사용 토큰 자동 폐기
    • CI/CD가 프로덕션 전체 권한을 갖는 구조는 재검토한다. 빌드 시스템이 가장 강력한 내부자인 경우가 많다

Identify (자산 및 공격 표면)

  • 공격 표면 관리(ASM)
    • 정적분석·동적분석이 있듯, 해커가 쓰는 방식으로 우리도 확인한다
  • Shodan, Censys 같은 자원 스캐너를 확인한다
    • 정기적으로 우리 IP 대역·도메인을 직접 조회해서 “모르던 게 뜨는지” 본다
    • 인증서 투명성 로그(crt.sh)를 모니터링하면 발급된 줄 몰랐던 서브도메인이 나온다
  • 자산 인벤토리는 자동으로 수집한다. 수동 관리는 반드시 낡는다
  • 서브도메인 테이크오버 점검 (댕글링 DNS)

Identify (외부 제보: VDP, 버그바운티)

  • VDP → 비공개(초대제) 바운티 → 공개 바운티
  • /.well-known/security.txt
    • “선의의 보안 연구에 대해 법적 조치를 하지 않는다”를 정책에 명문화한다
  • 범위(scope)와 금지 행위를 명시한다
    • 금지: DoS/부하 테스트, 소셜 엔지니어링, 물리적 침입, PoC 범위를 넘는 데이터 수집·유출, 타 사용자 데이터 접근

Protect (접근 통제 및 공급망)

  • IP 제한도 좋은 방법
  • SBOM은 자동화된 시스템이 수집해야 한다
    • SPDX 또는 CycloneDX 형식, 빌드 파이프라인에서 자동 생성
    • SLSA 참조
  • 시크릿 관리
    • 시크릿 매니저 / 패스워드 매니저를 쓴다 (Vault, AWS Secrets Manager 등)
    • 코드·설정·git 히스토리에 이미 들어간 키를 찾는다
      • gitleaks / trufflehog를 CI와 pre-commit에 통합한다
      • 유출된 시크릿은 삭제가 아니라 로테이션이 답이다. 커밋을 지워도 이미 노출된 것이다
      • 짧은 수명의 동적 크리덴셜로 전환을 검토한다

Detect (탐지)

  • SIEM/SOAR는 필요하다
    • 중앙화해야 연결 고리를 확인할 수 있다