지난 교육에서 배운 내용을 다시 사용하여 복습하고 보안, 삭제 정책, REST API 등 배우지 못하거나 생각하지 못했던 부분을 추가하는 것을 목표로 개발
풀스택 개발자 양성 과정을 통해 Spring이나 mybatis 등 다양한 기술을 배웠지만 보안적인 부분이 부족했던 것 같습니다. 그래서 대학교 때 배웠던 부분과 면접 때 질문받았던 내용을 추가하여 부족한 보안의 내용을 보완했습니다.
HTTPS는 HTTP에 SSL을 추가하여 보안을 강화한 방법입니다.
SSL은 클라이언트와 서버 간의 통신을 공인된 제 3자(CA) 업체가 보증하는 방법으로, 통신 내용이 노출되고 변경되는 것을 방지하고 클라이언트가 접속하려는 서버가 신뢰할 수 있는 서버인지 확인할 수 있게 해줍니다.
다음 사진은 SSL의 통신 방식입니다. 최종적으로 서로 데이터를 주고받기 위해 대칭 키 암호화 방식을 사용하고 이 대칭 키를 만들기 위해 공개키 방식과 전자 서명이 사용됩니다.
옵션 | 설명 |
---|---|
-alias | 키의 별칭을 지정합니다 |
-certreq | 인증서 요청을 생성합니다. |
-changealias | 항목의 별칭을 변경합니다. |
-delete | 항목을 삭제합니다. |
-exportcert | 인증서를 익스포트합니다. |
-genkey | 키를 생성합니다. |
-genkeypair | 키 쌍을 생성합니다. |
-genseckey | 보안 키를 생성합니다. |
-gencert | 인증서 요청에서 인증서를 생성합니다. |
-importcert | 인증서 또는 인증서 체인을 임포트합니다. |
-importpass | 비밀번호를 임포트합니다. |
-importkeystore | 다른 키 저장소에서 하나 또는 모든 항목을 임포트합니다. |
-keyalg | 키 생성 알고리즘을 지정합니다. |
-keypasswd | 항목의 키 비밀번호를 변경합니다. |
-keysize | 키 사이즈를 지정합니다. |
-keystore | 키 저장파일 |
-list | 키 저장소의 항목을 나열합니다. |
-printcert | 인증서의 콘텐츠를 인쇄합니다. |
-printcertreq | 인증서 요청의 콘텐츠를 인쇄합니다. |
-sigalg | 인증서의 알고리즘을 지정합니다. (해시 알고리즘으로 구성) |
-printcrlCRL | 파일의 콘텐츠를 인쇄합니다. |
-storepasswd | 키 저장소의 저장소 비밀번호를 변경합니다. |
-v | 결과를 상세하게 보여줍니다. |
-validity | 인증서의 유효기간을 설정합니다. |
톰캣에서 https를 사용하기 위해 넣어준 세팅입니다.
세팅 후 이클립스에서 톰캣 설정을 보면 포트에 SSL이 바로 추가가 안 됩니다. 이 경우 톰캣을 다시 등록하면 설정한 SSL이 추가되는 것을 확인할 수 있습니다.
https로 설정한 포트가 443번인데 http로 접근을 시도할 경우 나타나는 메시지입니다.
https로 시도할 경우 연결이 비공개로 설정되어 있지 않습니다라는 내용이 보이고, 안전하지 않음(으)로 이동 버튼을 클릭할 경우 이동할 수 있습니다.
이동 후 URL을 확인하면 다음과 같이 경고 표시가 나온 것을 확인할 수 있습니다.
인증서를 확인할 경우 위에 사진과 같은 내용을 확인할 수 있습니다. 이렇게 나타난 이유는 SSL의 통신 방식에서 CA에서 발급된 인증서인지 확인하는 과정이 있는데 그 부분에 대한 세팅을 안 해서 나타난 것으로 예상됩니다. 추후 무료로 이용할 수 있는 CA를 찾은 후 그다음 작업을 진행할 계획입니다.
지금 이 프로젝트를 진행하기 전에는 DB에 모든 값을 그대로 저장했습니다. 그러다 보니 로그인 이후에 사용할 수 있는 기능들을 확인할 때 DB에 저장된 아이디와 비밀번호를 찾아서 로그인한 경험이 있습니다. 다시 생각해보면 DB에 접근할 수 있으면 다른 사람들의 정보를 쉽게 볼 수 있고 특히 사용자의 로그인 정보를 가져갈 수 있는 위험한 상황이 올 수 있다는 것을 느꼈고, 이 부분을 보완하고자 비밀번호에 해시 알고리즘을 적용하여 보안을 강화했습니다.
비밀번호와 salt 값을 바이트 범위로 인코딩 후 합쳐주고 다이제스트 업데이트할 때 사용해줍니다. digest() 메소드를 통해 업데이트된 값을 가져오고 그 값들을 HexString으로 변환하여 비밀번호로 저장합니다.
DB에 저장된 내용을 보면 회원가입을 통해 입력한 내용하고 전혀 다른 값이 저장된 것을 확인할 수 있고 로그인을 할 때도 위에 메소드를 이용하여 비밀번호를 해시 알고리즘에 적용할 경우 DB에 저장된 값 같은 값이 나와 로그인에 성공한 모습을 확인할 수 있다.
현재 페이지 이동은 a 태그와 button 태그를 통해 이루어지는데 만약 URL을 안다면 브라우저에 URL을 입력하여 원하는 위치로 이동할 수 있습니다. 그럼 만약 일반 사용자가 관리자 페이지 URL을 입력할 경우 관리자 페이지에 들어올 수 있기 때문에 관리자가 아닌 사용자의 접근을 막았습니다.
특정 경로로 접근할 경우 미리 설정된 클래스가 실행되게 spring-servlet.xml에 설정합니다.
preHandle과 postHandle을 통해 처리가 가능합니다. preHandle는 controller로 보내기 전에 처리하고 postHandle은 controller의 handler가 끝나면 처리됩니다.
접근을 막기위해 설정했기 때문에 preHandle에 내용을 추가했습니다. 현재 로그인 기능이 관리자 로그인 뿐이기 때문에 로그인 여부를 통해 판단을 했지만, 일반 사용자 로그인과 관리자 로그인이 나누어진 경우면 DB에 그 정보를 저장하는 컬럼을 추가하고 그 정보를 통해 막으면 될 것으로 예상합니다.
쿠키와 세션은 웹 통신간 유지하려는 정보를 저장하기 위해 사용되는 방법입니다.
위에 사진에서 빨간 박스에 있는 내용은 관리자 페이지를 제외한 모든 페이지에 사용되기 때문에 계속 DB에 접근하여 값을 가져오는 건 비효율적이라고 생각했고, 세션을 사용하여 서버의 저장하면 사용자가 늘어남에 따라 서버의 큰 부담이 될 것으로 예상되어 쿠키를 통해 값을 저장하고 저장된 값을 사용하는 방법을 생각했습니다.
그러나 문제가 있었던 게 빨간 박스의 내용을 저장하기 위해 게시판 목록이 저장된 List와 커뮤니티 게시판의 최신 글들이 저장된 List를 Map으로 저장해서 보내려 했지만, 쿠키의 경우 text만 지원되고, 용량 제한, 만료 시점 등 더 고려할 사항들이 있다는 점을 뒤늦게 파악했습니다.
현재 코드는 임시로 세션으로 저장하는 방식으로 만들었으나 이미 사이트에 들어온 사람은 그 이후에 만들어진 게시판을 못 보는 등 저장된 값을 가져와 사용할 경우 발생했습니다. 이유는 관리자가 게시판을 추가하더라도 기존에 있던 쿠키나 세션으로 가지고 오기때문에 변화가 없었던 것으로 확인되었습니다. 실습을 다니면서 이러한 게시판 목록을 캐싱을 통해 해결한 것으로 확인했고, 같은 방법을 통해 해결할 계획입니다.
지난 교육 때 클라이언트에서 삭제 요청이 오면 DB에 저장된 데이터를 지우는 것이 맞는지 간단하게 이야기가 나온 적이 있었는데 그때 당시에는 크게 생각을 안 하고 저장된 데이터를 지웠습니다.
그러다 최근 면접을 보면서 다시 생각해본 게 만약 사용자가 실수로 삭제한 데이터에 대해 복구를 신청할 경우 어떻게 처리할까에 대해 고민하게 되었습니다. 만약 DB에 데이터를 지우면 지우기 전으로 완전히 돌려야 하는데 그러면 다른 사용자들이 추가하고 변경된 내용도 이전으로 돌아가게 되어 큰 문제가 발생할 거라 생각이 되었습니다.
이 부분을 해결하기 위해 테이블에 삭제를 판단하는 컬럼을 추가하여 삭제되었는지만 판단하고 실제 데이터는 남아있는 방식을 생각하게 되었습니다.
변경 전 테이블
변경 후 테이블
변경된 쿼리
모든 쿼리문에도 미리 삭제한 데이터인지 판단이 필요하기 때문에 isDelete 값을 확인하는 내용을 추가했습니다.
REST API는 HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미합니다.
HTTP Method의 역할
METHOD | 역할 |
---|---|
GET | GET를 통해 해당 리소스를 조회합니다. 리소스를 조회하고 해당 도큐먼트에 대한 자세한 정보를 가져온다. |
POST | POST를 통해 해당 URI를 요청하면 리소스를 생성합니다. |
PUT | PUT를 통해 해당 리소스를 수정합니다. |
DELETE | DELETE를 통해 리소스를 삭제합니다. |
Spring에서 사용하는 Controller은 GET과 POST를 동시에 받는 것이 기본값이기 때문에 항상 Method에 대한 내용은 작성하지 않았는데, 이번에 REST API를 알게 되고 적용을 해보니깐 메시지가 의도하는 바를 명확하게 보여 의도하는 바를 쉽게 파악할 수 있다는 장점을 느꼈습니다.
그러나 로그인 기능에 대해 고민이 많았는데, 위에 Method를 정리한 표를 보면 로그인은 GET을 통해 접근할 것으로 예상되는데 GET을 사용할 경우 URL에 로그인 정보가 그대로 나타나기 때문에 보안에 취약할 것 같다는 고민을 했습니다. 그래서 이 부분에 대해 자료를 더 찾아보았습니다.
한 해외 자료에서 Method에 대한 설명은 다음 표와 같이 나와 있습니다.
HTTP Method의 역할
METHOD | 역할 |
---|---|
GET | 대상 리소스의 현재 정보를 전송합니다. |
POST | 요청 페이로드에서 리소스별 처리를 수행합니다. |
PUT | 대상 리소스의 현재 정보를 모두 요청 페이로드로 바꿉니다. |
DELETE | 대상 리소스의 현재 정보를 모두 제거합니다. |
GET, PUT, DELETE의 경우 이전 자료와 같지만, POST의 경우 GET, PUT, DELETE가 수행하는 역할 외 모든 내용을 수행할 것 같은 느낌을 받았고, 위키피디아에서 GET, PUT, DELETE Method는 스토리지 관리 의미론을 가지므로 CRUD 작업이며, POST 방법은 CRUD 작업이 아니라 스토리지 관리 의미론을 제외한 대상 리소스별 의미론을 갖는 프로세스 작업을 수행한다는 설명이 있는 것으로 보아 POST는 꼭 CREATE뿐아니라 GET, PUT, DELETE의 역할을 제외한 역할들을 수행한다고 생각합니다.
실제 코드에서 역할에 맞게 Method를 설정한 코드입니다. 또한 파일 업로드 기능 때문에 enctype에 multipart/form-data가 있는 경우 POST만 사용한 점도 확인했습니다.