동기화 이슈 - BadPaddingException
동시성과 암호화와 세션과 에러
문제
Request 할 때 보내는 Parameter 들을 암호화기 위해서 암복호화 기능을 적용하는 중에 에러 이슈가 나타났다.
에러 메세지는 BadPadding Exception으로 복호화가 안되는 문제로 판단됐다. 원인은 파악하기 위해 복호화가 안되는 이유가 무엇인지 생각해봤다.
현재 사용한 암호화 알고리즘은 RSA로 되어있다. 그리고 Key 생성은 서버에서 하며 Public Key는 Front로 보내어 클라이언트단에서 암호화 후 데이터를 보내준다. Private Key는 세션에 저장되고, Request가 올 때 마다 ParamFilter에 Private Key를 static 변수에 저장 후 getParamters or getParameter 메서드를 호출할 때 복호화하여 Parameter 값을 반환한다.
복호화가 안되는 이유는 다음과 같은 경우가 있을 것 같다.
- Private Key가 유효하지 않다.
- 데이터 암호화가 잘 못 됐다.
1번이 이유가 될 가능성이 크다고 생각했다. 모든 요청이 복호화가 안됐다면 2번 가능성이 더 크겠지만 간헐적으로 데이터 복호화가 되지 않았다.
그래서 1번 이유를 생각하면서 코드를 따라가 봤는데 ParamFilter 쪽에 privateKey를 담는 static 변수가 문제였다.
class FilterA implements Filter {
private static PrivateKey pk;
@override
pulbic void init(Filterconfig filterConfig) thorws ServletExcpetion {
}
@Override
public void doFilter(ServletRequest, reqeust, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
}
FilterA는 Servlet에 Filter 인터페이스를 상속받아서 구현한다. 그렇게 해서 스프링을 실행할 때 Filter로 등록하면 클라이언트에게 요청이 올 때 마다 순서에 따라서 FilterA를 거쳐가게 된다.
이 Filter의 역할은 위에 코드에는 나와있지 않지만, HttpServletRequestWrapper를 상속받은 새로운 ReqeustWrapper static 클래스를 FilterChain을 통해서 기존 Request를 바꿔서 넘겨준다. RequestWrapper가 getParamterValues, getParameter를 오버라이딩 하면서 복호화 로직이 추가로 들어가 있다.
그리고 ReqeustWrapper는 복호화 할 때 FilterA의 static 멤버 변수 PrivateKey를 사용한다. PrivateKey는 doFilter를 거칠때마다 Session에서 Private Key 값을 가져와 할당한다.
이미 눈치 챈 사람은 알겠지만 멀티 쓰레드 환경에서 static 값을 계속해서 재할당 한다면 당연히 문제가 발생할 것이다. 동기화 처리를 안해 줬다면 Private Key 값은 계속 재할당 되고, 결국 순서가 엉키게 되면서 잘못된 Private Key 값을 사용하게 된다.
해결
해결은 간단히 static 변수를 없애고, getParameterValues, getParameter 메서드 호출시 세션에서 값을 가져와서 사용하도록 변경했다.
public String getParamter(String parameter) {
HttpServletRequest request = (HttpServletRequest) getRequest();
request.getSession().getAttributer(PRIVATEVAL);
}
Http(s) 웹에서는 멀티 쓰레딩 환경이란 것을 잊지 말자.