보안상의 문제를 고려하자!!

sion Jeong·2024년 8월 2일
0

한주간을 돌아보며

역시 뭐든지 쉽게쉽게 진행되는 것은 없는것 같다!
계획대로라면 MVP 진행상황대로 기본기능, 추가기능 까지 완료한 후 이번주 부터는 고급기능을 들어갈 차례였다.

하지만!! 우리의 코드상에 존재하는 여러가지 문제점이 또 발견되었다.
이번주 진행되었던 상황을 쭉 정리해볼 예정이었지만 다른사람들에게도 인사이트가 될 만한 소재가 생겨 소개를 해보려 한다.


CustomException 도입!!


개발을 진행하며 CustomException을 도입해 예외가 발생했을 때 예외 이유를 구체적으로 사용자에게 알려주는 식으로 개발을 진행했다.
이렇게 개발했던 이유는 오류 메시지를 명확하게 보여줌으로써 어떤 오류인지 쉽게 파악하고 빠르게 해결하기 위함이었다.

그러나 생겨버린 보안 관점에서 문제점...

그러나 이러한 방식은 보안 관점에서 다음과 같은 우려 사항이 있는것으로 판단되었다.

  1. 정보 노출: 구체적인 오류 메시지는 공격자에게 시스템에 대한 중요한 정보를 제공할 수 있다. 예를 들어, "잘못된 비밀번호"라는 메시지는 공격자에게 해당 이메일 계정이 존재함을 알려줄 수 있다.
  2. 브루트 포스 공격: 비밀번호 오류 메시지를 통해 공격자는 계정 존재 여부를 확인한 후, 비밀번호를 추측하여 여러 번 시도할 수 있는 기회를 얻게 된다.
  3. 피싱 공격: 구체적인 오류 메시지를 통해 피싱 공격자가 실제 시스템에서 사용하는 메시지를 모방하여 더욱 신뢰성 있는 피싱 시도를 할 수 있다.
  4. 시스템 취약점 노출: 시스템의 내부 구조나 검증 로직에 대한 정보를 유추할 수 있어, 이를 악용한 공격이 가능해진다.

어떻게 해결해야할까???

바로 팀원들끼리만의 약속된 에러코드 명세서를 작성하고 사용하는 것이다!!
1. 내부적으로 구체적인 에러 코드 사용:
- 각 에러 상황에 대한 구체적인 에러 코드를 정의하고, 이를 명세서에 상세하게 작성한다.
- 예를 들어, 비밀번호 오류는 1004, 사용자 ID 오류는 1003과 같은 에러 코드를 사용한다.
- 이러한 명세서는 개발팀끼리만 공유하여, 내부적으로 문제를 파악하고 해결할 수 있도록 한다.
2. 백엔드와 프론트엔드의 협업:
- 백엔드에서는 예외 발생 시 구체적인 메시지 대신 에러 코드를 프론트엔드로 전달한다.
- 프론트엔드는 이 에러 코드를 받아 사용자에게 적절한 일반 메시지를 파싱하여 보여준다.

따라서 다음과 같은 명세서를 작성해 보았다.
https://www.notion.so/ae994788803c48afabd8aad1e9027e60


어김없이 나타나는 트러블슈팅...

에러 코드 명세를 정하고 커스텀 익셉션을 리팩터링 하면서 Response로 코드번호만 나오도록 도입을 해봤다.

하지만 어떤 이유에서 였을까...
오류가 발생했을때 우리가 정한 CustomException 형태의 오류의 형태로 나오는 것이 아닌 Internal Server Error가 나는 것을 확인했다.

디버깅을 돌려보니 컨트롤러 코드가 실행되지 않는것을 확인해 → 필터에서 문제가 생긴다는 것을 확인했다.


문제를 해결해보자

결국 2가지 문제를 해결하게 되었다.

문제1

  • JWT Util 클래스 토큰에서 이메일 뽑는 메서드의 예외 처리가 CustomException으로 처리되지 않았다.
  • CustomExceptionRuntimeException을 상속받기 떄문에 SignatureException 은 처리할 수가 없다!!

해결!!

  • 따라서 CustomException을 처리할 수 있는 Exception 으로 처리하는 것으로 수정하고 로그도 추가하였다.

문제 2
아쉽게도 1번 문제를 해결한 후에도 오류가 해결되지 않았다. 다시한번 디버깅을 돌려보니

  • jwtUtil 클래스의 getEmailFromToken(token) 메서드가 여전히 동작하지 않는것을 확인했다.
  • 오류의 이유는 if이 사용되고 있어 예외를 처리할 수 없는 상태였다.

해결!

  • 예외를 처리하기 위해 if문이 아닌 try catch문으로 변경하였다.

    이렇게 하여 예외가 CustomException 으로 정상적으로 처리되는 것을 확인할 수 있었다.

여기서 생기는 궁금증 2가지

  1. 분명 getEmailFromToken(token); 메서드안에 CustomException 을 던지도록 이미 처리했다.
    때문에 예외처리를 해줄텐데 왜 또 캐치문을 작성해줘야할까??
  • 필터에서 CustomException을 캐치하면 예외를 구체적으로 처리하고 클라이언트에게 던질 수 있고
  • 혹시나 예외가 CustomException만으로 캐치가 안될수 있기 때문에 일반적인 오류 응답을 클라이언트에게 보낼 수 있도록 해야하는 이유가 있다.

2. 서비스 단에서 예외 처리가 가능한 이유

예외는 Try-catch문으로 처리해야한다. 근데 어떻게 서비스 로직에서는 if 문으로 예외 처리를 하는걸까?

그것은 바로 JPA, 트랜잭션과 관련이 있다.
서비스 로직에서 우리는 JPA를 활용한다. 즉 @Transactional 안에서 데이터가 처리된다! 이것을 기억하고 있자.

  1. JPA와 @Transactional:
    • JPA를 활용하는 서비스 로직에서 @Transactional 어노테이션을 사용하면, 해당 메서드 내에서 발생하는 모든 데이터베이스 작업이 하나의 트랜잭션으로 관리된다.
    • @Transactional 메서드 내에서 예외가 발생하면 기본적으로 트랜잭션이 롤백된다.
  2. 런타임 예외와 트랜잭션 롤백:
    • @Transactional 어노테이션은 런타임 예외가 발생할 경우 자동으로 트랜잭션을 롤백한다.
    • 이때!! 발생하는 런타임 예외는 RuntimeException의 하위 클래스인 것이다.
  3. CustomException:
    • 우리는 CustomExceptionRuntimeException을 상속받아 만들었다. 따라서 CustomException이 발생하면 트랜잭션이 롤백된다.
    • 서비스 단에서는 if 문을 사용하여 조건을 검증하고, 조건이 만족되지 않으면 CustomException을 던질 수 있다.
  4. if 문을 사용한 예외 처리:
    • 서비스 로직에서 특정 조건을 검증할 때 if 문을 사용한다.
    • 조건이 만족되지 않으면 CustomException을 던진다.
    • 이 예외는 컨트롤러 또는 글로벌 예외 처리기에서 처리된다.

아주 간단하고 빠르게 정리하면
서비스 로직에서 우리는 JPA를 활용한다. 즉 @Transactional 안에서 데이터가 처리되는데
트랜잭셔널 안의 메서드에서 예외가 발생하면 롤백이 되고 → 런타임 예외가 발생하면 트랜잭션이 롤백된다.
근데 우리는 CustomException 을 런타임 예외를 상속받아 만들었다!!
따라서 서비스 단에서는 if문으로 예외처리가 가능하다.


총 정리

이 처럼 우리는 CustomException 시작하여 보안상의 문제를 고려하게되고
디버깅, 필터, try-catch문, if문, JPA와 트랜잭셔널 까지 고려하고 적용되는 상황이 나왔다.
개발을 하다보면 한가지로 시작하지만 점차 다른것이 연결되고 연결되는 느낌을 받을때가 있다.
그때마다 "아!" 하면서 이게 이거구나 하는 쾌감이 있다.
어떨때는 머리를 싸메고 몇일동안 고민을 해야 해결되는 부분도 있지만 이러한 배움에서 오는 쾌감과 깨달았을때의 도파민! 개발자로서 수명이 깎이는 느낌도 들지만 이러한 배움에서 오는 도파민이 우리를 성장시키는 것 같다!!!

나의 깨달음이 모두에게 인사이트가 되었으면 한다!! 오늘의 개발일기 끝!!!!!!!!

profile
개발응애입니다.

1개의 댓글

comment-user-thumbnail
2024년 8월 5일

기술적인 고민이 묻어나는 아티클 감명깊게 봤습니다~!!
역시 우리팀의 부리더!!

답글 달기