우리 웹서비스에서 비회원과 통신하는 신규 기능을 개발하게 되었다
비회원과 통신이므로 보안에 신경을 많이 썼는데, 그 중 우리가 사용한 AES, RSA, SHA에 대해 이야기 해보려고한다
1.SHA-256
우리는 비회원의 로그인(!?)을 위해, 패스워드를 받았는데, 패스워드는 본인만 알고있어야 하므로, 복호화가 되지 않는 암호화 방법을 채택했다
복호화가 되지 않는 암호화 방법 중 유명한 것이 SHA 시리즈인데, SHA는 미국 NSA가 제작하고 미국 국립표준기술연구소(NIST)에서 표준으로 제작한 해시 암호 알고리즘이다
과거 SHA-0, SHA-1 버전을 거쳐 요즘은 SHA-2를 많이 쓰고있는데, SHA-2에는 SHA-224, SHA-256, SHA-384, SHA-512 등의 종류가 있다
현재 SHA-3도 개발 중이라고한다
우리는 현재 자주쓰는 SHA-2 중에서도 대표적으로 인터넷뱅킹이나 비트코인등에 쓰이는 SHA-256을 채택했다.
SHA-2의 특징은 어떤 문자열을 암호화 할 때, "동일한 문자열"은 항상 동일한 다이제스트(해시 함수가 출력하는 압축된 문장)를 출력하고, "다른 문자열"은 항상 다른 다이제스트를 출력한다(SHA-1도 SHA-2와 큰 차이가 없다고 하니, 아마 비슷할 것 같다)
이 중 SHA-256은 다이제스트 출력 값이 256bit인 것을 말한다
가령 SHA-512라면 다이제스트 출력 값이 512bit인 셈
이 때, "다른 문자열"이라는 경우의 수는 매우 많아 다른 문자열임에도 다이제스트가 충돌할 수 있는데, SHA-256만 하더라도 2^256 라는 어마무시한 경우의 수를 제공하여 충돌 가능성이 매우매우매우 적다고 한다(이게 얼마나 큰 수인지, 관측 가능한 우주의 모든 원자수 보다 크다는 주장도 있다고한다)
우리는 이 SHA-256을 이용해, 비회원이 어떤 패스워드를 등록하더라도 SHA-256을 통해 나온 다이제스트를(256bit 해싱 값) RDB에 저장하므로써 비회원이 등록한 패스워드를 본인이 아니면, 누구도 모르게 했다
[보안 추가 방법]
이 때, "동일한 문자열"을 누군가 SHA-256 변환하여, 비교한다면 해킹이 가능하므로, 서버단에서 원본메시지(비회원이 입력한 비밀번호)에 "특정 문자열"을 추가했는데, 이런 방법을 솔팅이라고 하고, 추가한 "특정 문자열"을 솔트라고 한다
거기다 우리가 연결한 비회원이 엄밀히 말하면, 자사의 cs프로그램을 쓰고있는 고객이라서, 사용자마다 다른 cs프로그램의 유니크값을 가져와서 솔트로 사용했다
이 밖에 키 스트레칭이라는 방법도 있는데, SHA-256(단방향 해싱)을 통해 결과로 나온 다이제스트를 또 다시 해싱하는 방식으로 해커가 패스워드를 추측하는 데 많은 시간이 소요되도록 하기 위한 것이다.
다만, 팀장님 판단 하에 우리는 키 스트레칭까지는 사용하지 않았다
2. RSA
대표적인 비대칭키 암호화 방식이다
비대칭키 라는 것은 암호화할 때 키와 복호화할 때 키가 다르다는 의미인데, 암호화할 때 키를 "공개키", 복호화할 때 키를 "개인키"라고 한다
클라이언트에서 가지고있는 정보를 서버로 보낼 때, 스니핑 등으로 데이터가 노출되면 안되므로 클라이언트가 가지고 있는 "공개키"로 데이터를 암호화하여, 서버로 전달한다
다만, 암호화 가능한 길이가 딱 245byte 까지이므로, 긴 문자열을 암호화하는데는 적합하지 않다
우리는 json 데이터를 주고받고 싶은데, 암호화 가능한 문자열 길이가 제한되어 있으므로 비회원과 통신할 때, 아래 AES 암호화를 섞어서 사용했다
3.AES
AES는 "Advanced Encryption Standard"를 줄인 말이라고 한다(고급 암호화 표준)
"대칭키"라는 임의의 키를 만들어서, 암호화하고자 하는 "문자열"을 "대칭키" 라는 키로 암호화 하는 방식이다
암호화 키의 길이에 따라 AES-128, AES-192, AES-256 종류가 있다
SHA 처럼 AES 뒤의 숫자가 bit 길이이다
위의 비대칭키와 다르게 "대칭키" 하나로 암호화 및 복호화 모두 진행하는 방식이다(즉, 같은 키를 가지고 암복호화를 진행한다)
특히 문자열 길이에 상관없이 암호화가 가능하다고 한다
4.RSA와 AES 함께 쓰기
우리는 245byte를 초과할 수 있는 "긴 문자열"(json 데이터)를 클라이언트와 서버간 주고받아야했다
때문에 RSA로는 암호화해서 데이터를 보낼 수 없었는데, 찾아보니 아래와 같은 방법이 있었다
1.RSA 공개키, 개인키를 서버에서 생성해서 클라이언트에게 "공개키"를 알려준다
(우리는 통신할 때 마다 매번 공개키, 암호화를 생성하지는 않았고, 한 번만 생성한 후 클라이언트 개발 담당자에게 "공개키"를 넘겨줬다)
2.클라이언트는 "공개키"를 안전한 장소에 보관하고, 서버는 "개인키"를 안전한 장소에 보관한다
3.클라이언트에서 서버로 데이터를 보낼 때, AES 대칭키를 생성한다
이 대칭키로 서버로 보낼 데이터를 AES 암호화 한다
4.서버에서는 클라이언트가 생성한 AES 대칭키를 알고있어야 전달받은 데이터를 복호화 할 수 있다
때문에 클라이언트에서 AES대칭키를 안전한 장소에 보관된 "공개키"로 RSA 암호화를 진행한다
5.서버에서는 RSA 암호화된 AES 대칭키를 "개인키"로 RSA 복호화하고,
복호화된 AES 대칭키로 실제 데이터를 AES 복호화한다
이 뒤에 세션을 만들어서 게이트웨이 보안검증을 하는 등 몇 가지 절차가 더 있으나, 암호화 관련된 내용은 여기까지 진행 했었다
보안에 안전한 클라이언트 <-> 서버 통신 기능을 만든다는 생각에 재밌게 개발한 경험이었다
'Back-end > JAVA & Spring' 카테고리의 다른 글
[JAVA] Quartz job Scheduler 기본 사용법 정리 (0) | 2021.05.24 |
---|---|
[RSA] RSA MODULUS, EXPONENT (1) | 2021.05.20 |
AES와 SHA 차이 (0) | 2021.05.20 |
[Java] 버퍼(BUFFER)란? 버퍼 개념 (2) | 2021.05.17 |
[Java] BufferedReader의 EOF 처리 & 여러 줄 입력 처리 - .readLine() (0) | 2021.05.17 |
댓글