2022-07-15-TIL
Today I Learned
보안에 관하여, 클라이언트 vs 서버
웹 개발에 있어서 보안 요소는 크게 두 가지 부분에서 고려한다. 바로 클라이언트에서의 보안과 서버 측에서의 보안이다.
클라이언트 측에서 보안은 생각보다 쉽게 해제가 되고 얼마든지 탈취 가능하다. 말 그대로 공격자의 입장에서는 자기 집 안방에서의 홈 구장 플레이다. 브라우저, 크롬 확장 프로그램, 각종 도구를 활용해서 여러 가지 분석과 시도를 할 수 있는 통로가 많다.
반면에 서버 측 보안은 그나마 방어할 만한 경로가 정해져 있다. 결국 공격자도 네트워크 트래픽을 통해서 접근하기 때문이다. IP와 포트, 그리고 이후에 일어나는 행위들만 막으면 된다. 물론 그것 또한 심혈을 기울여서 작업해야 한다. 그래도 공격자 입장에서는 원정 경기이고 미지의 영역이므로 훨씬 더 공격하기 까다로운 대상이다.
이 차이에서 나오는 원칙은 “클라이언트에서 온 것은 믿지 않는다”이다. 프론트엔드의 입력 검증, 버튼 비활성화, 숨긴 필드, 자바스크립트에 넣은 가격 계산은 모두 사용자가 개발자 도구로 바꿀 수 있다. OWASP Input Validation Cheat Sheet도 브라우저가 같은 필드를 검사하더라도 서버에서 다시 검증하라고 하고, 클라이언트 측 검증은 우회할 수 있으니 사용자에게 즉시 피드백을 주는 용도로 쓰라고 한다.
그래서 역할을 나눈다.
- 클라이언트: 사용자 경험을 위한 검증. 잘못 입력하면 바로 알려 준다.
- 서버: 보안과 정합성을 위한 검증. 권한 확인, 입력값 범위, 가격 같은 중요한 계산은 모두 서버에서 다시 한다.
클라이언트 코드에 API 키나 비밀 값을 넣는 것도 같은 이유로 피해야 한다. 번들된 자바스크립트는 누구나 열어 볼 수 있다.
@Transactional로 인한 lock
@Transactional로 설정된 메서드에서 쿼리가 실행되면 그동안에 lock이 걸린다.
조금 더 정확히 하면, lock을 거는 것은 Spring이 아니라 DB이고, 어떤 쿼리를 실행하느냐에 따라 다르다. @Transactional은 트랜잭션의 시작과 끝(커밋 또는 롤백)을 메서드 경계에 맞추는 역할을 한다. 그래서 트랜잭션 안에서 걸린 lock이 메서드가 끝날 때까지 유지된다는 점이 중요하다.
MySQL InnoDB 기준으로 정리하면 다음과 같다.
- 일반
SELECT는 일관된 읽기(consistent read)로 처리되어 테이블에 lock을 걸지 않는다(MySQL 문서). 다른 세션이 동시에 같은 테이블을 수정할 수 있다. UPDATE ... WHERE,DELETE ... WHERE는 검색하면서 만나는 레코드에 배타적 lock을 건다(Locks Set by Different SQL Statements). 유니크 인덱스로 한 행을 찾으면 그 행의 인덱스 레코드에만 lock을 건다.SELECT ... FOR UPDATE,SELECT ... FOR SHARE는 읽으면서 lock을 건다. 이렇게 걸린 lock은 트랜잭션이 커밋되거나 롤백될 때 풀린다(Locking Reads).
1
2
3
4
5
6
@Transactional
public void order(Long productId) {
Product product = productRepository.findById(productId).orElseThrow(); // 일반 SELECT: lock 없음
productRepository.decreaseStock(productId); // @Modifying UPDATE 쿼리를 바로 실행: 해당 행에 lock
externalPaymentClient.pay(product); // 외부 호출이 느리면 그동안 lock을 쥐고 있다
} // 커밋되면서 lock 해제
여기서 주의할 점은 트랜잭션 안에서 외부 API 호출이나 오래 걸리는 작업을 하는 것이다. 어떤 행에 lock이 걸린 상태로 트랜잭션이 오래 열려 있으면, 같은 행을 수정하려는 다른 요청이 그동안 기다리게 된다. 그래서 트랜잭션은 DB 작업에 필요한 범위로 짧게 유지하고, 외부 호출은 가능하면 트랜잭션 밖으로 뺀다. 또 인덱스가 없는 컬럼으로 UPDATE ... WHERE를 하면 검색하면서 만나는 레코드가 많아져 lock 범위가 넓어지므로, 수정 조건에도 인덱스가 중요하다.
댓글
아직 댓글이 없습니다