개발놀이터
??? : 비밀번호를 암호화 해달라고요? base64로 하면 되죠? (feat. Argon2) 본문
죄송합니다.. 블로그 인생 처음으로 어그로좀 끌어봤습니다... 이번 포스팅에선 비밀번호 암호화, 암호화 된 해시값을 추적하는 해킹 기법들, 그리고 어떻게 웹 개발자로서 방지할 수 있는지에 대해서 공부한 내용을 정리해보도록 하겠습니다.
비밀번호 암호화
보통 비밀번호는 평문으로 저장하는건 위험하다고 알려져있습니다. 데이터베이스가 털렸을 때를 대비해서 비밀번호만큼은 지켜낸다는 일념인 것이죠.
데이터베이스를 털릴걸 가정하고 암호화를 한다니?
데이터베이스가 털릴걸 가정하고 암호화를 하는게 아니라 데이터베이스가 '털리더라도' 안전하게 지키는 것이 목적인 것이죠.
그런 의미에서 base64로 인코딩하는건 말도안되는 소리지요. base64는 역연산으로 얼마든지 비밀번호를 얻어낼 수 있으니까요.
BCrypt
BCrypt는 단방향 해시로서 비밀번호 암호화에 자주 쓰이는 알고리즘입니다. 암호화 알고리즘하면 빠지지 않는 SHA256와는 어떻게 다를까요?
이 표를 처음 봤을 때 살짝 애매했던 개념이 일부러 느리게 설정되어있다? 반복 연산으로 강도를 조절한다?
이 부분을 짚고 넘어가겠습니다.
Brute-Force
무차별 공격이라고 부르는 부르트포스는 임의의 비밀번호를 무차별적으로 10만개 100만개씩 집어넣는 해킹 기법을 이야기합니다. 그래서 보통 비밀번호를 암호화하는 알고리즘 (BCrypt) 에서는 일부러 연산을 느리게 만듭니다.
CPU, 메모리를 '일부러' 많이 잡아먹게 만들어서 이 브루트 포스에 면역을 가지게 하는 것이죠. 심지어 이 연산을 더 느리게 만들어서 브루트포스에 더 면역을 가지게 할 수도 있습니다.
이게 일부러 BCrypt 알고리즘이 일부러 느리게 설정된 이유이고 강도를 조절해서 이 브루트포스를 방지한다는 이야기입니다.
이런 내용을 공부하니 이 물음이 떠오르더라구요.
이게 공격자의 컴퓨터를 느리게 한다는 얘기인가? 서버가 느려진다는 말인가?
둘 다입니다.
공격자의 컴퓨터가 느려지면서 서버 또한 이 단방향 해시값을 처리하기위해 서버가 처리하는 연산이 많아집니다.
CPU와 메모리를 일부러 많이 잡아먹게 한다고 언급했습니다. 만약 공격자가 비밀번호를 해시값만 탈취했을 때 이걸 다시 비밀번호 원문으로 돌리는 과정에서 CPU와 메모리를 많이 잡아먹게 한다는 이야기입니다.
그럼 서버가 느려지는건 뭐냐? 만약 강도를 조절해서 강도를 강하게 해 역연산을 하기 힘들게 하는 순간 서버가 데이터베이스에서 해싱된 값을 가져와서 사용자가 넣은 평문을 '같은 해싱'을 거쳐서 비교하는 과정, 이 과정이 오래 걸리게 됩니다.
그럼 BCrypt는 무적이군요?
최신 브루트포스 해킹 기법
BCrypt는 1999년에 나온 암호화 기법으로 지금까지도 많이 사랑받는 암호화 기법입니다. 하지만 이 알고리즘이 나온지 25년이 지난만큼 해킹 기법도 많이 발전하게 되었습니다.
CPU 캐시 히트 모니터링 (사이드 채널 공격)
브루트포스도 그냥 무차별로 때려박는건 구식 트렌드입니다. CPU 캐시를 모니터링 하면서 히트했는지 안했는지 하나하나 따져가면서 브루트포스를 하는 방식으로 진화했습니다.
이를 예시로 들면 기존에는 보물섬에 가서 보물이 있는지 없는지 모든 땅을 다 파고 다니던 것이 금속탐지기를 가지고 금속함유량이 높은 곳만 파는 곳으로 바뀌었다는 이야기입니다.
그럼 설령 철조각이 나오더라도 보물일 '확률'이 올라가겠죠.
근데 CPU 캐시를 모니터링 한다고 어떻게 비밀번호를 유추할 수 있다는걸까요?
일단 공격자는 서버의 캐시를 깨끗하게 비웁니다. 이 단계를 Prime 단계라고 부릅니다. 그리고 평문으로 브루트포스를 날려봅니다. 그리고 메모리를 확인해보죠. 메모리를 확인하는 단계를 Probe 단계라고 부릅니다.
이 Prime, Probe 단계를 통해 어떤 메모리 블록이 활성화 되어있는지 확인합니다.
- 예를 들어서 내 비밀번호는 1098입니다. 근데 공격자가 1234, 1034, 1094 이렇게 세개의 값을 브루트포스합니다.
- 이렇게 비밀번호를 브루트포스 하면서 생기는 메모리버스의 패턴을 추적합니다.
- 그러면서 갑자기 팍! 하고 튀는 부분을 모니터링합니다. 그럼 공격자는 '아 이 비밀번호가 의미있는 처리를 유발했구나' 하고 추측하는 것이죠.
근데 좀 이상합니다? 그럼 지금 공격자가 내 서버에 들어와있다는 얘기인가요?
그럼 더 공격자에게 의미있는 데이터를 가져가면 되는데 쓸데없이 비밀번호만 깨작깨작 거리고 있나요?
예를 들어서 파일 업로드 취약점을 이용해서 내 악성 코드를 서버에 집어넣었다고 가정해봅시다. 근데 이 애플리케이션이 도커 컨테이너로 띄워져있네?
컨테이너는 호스트머신과 논리적으로 분리된 공간입니다. 그래서 실제 호스트 머신에 접근하는 것이 막혀있거나 권한이 없게 되죠. 해봐야 docker socket을 열어두는 정도려나요?
그 상황이지만 사실 물리적으로 봤을 때 도커 컨테이너는 호스트 머신과 다르지 않습니다. 그래서 L3 캐시나, 메모리 버스같은 시스템 자원에는 접근이 가능하죠.
예로 들자면 지금 공격자는 우리 아파트에 침입했습니다. 근데 옆집에 침입한거죠. 들리는 소리는 발소리와 비밀번호 도어락 누르는 소리뿐.. 어떤 정보도 얻을 수 없습니다.
이 때 공격자는 '아 옆집 사람이 평일에는 늘 오후7시에 집에 돌아오고 비밀번호는 4자리구나' '아 옆집에서 된장찌개를 먹고있구나' 이런 간접적인 정보만 가지게 되는 것이죠.
GPU와 병렬성
요즘 브루트 포스는 하나하나 집어넣지 않는다고 말씀드렸죠. 요즘은 GPU를 이용해서 병렬로 브루트포스를 진행해버립니다. GPU는 CPU와 비슷하지만 다른 것이 수십만개의 병렬 코어로 연속적인 처리를 할 수 있다는 것이 특징이죠.
이런 특징 때문에 디자인, 게임, AI 등 널리 사용되는 하드웨어입니다. 하지만 이걸 해커도 주로 사용합니다. 10만개의 병렬 코어로 병렬로 데이터를 집어넣어서 브루트포스에 걸리는 시간을 획기적으로 줄여버립니다.
성능 좋은 GPU를 사용한다면 공격에 성공할 확률도 매우 높아지게 됩니다.
Argon 2
보안 전문가들도 호구는 아니죠. 2015년 Argon2라는 암호화 알고리즘이 등장하였습니다. Argon2에는 세가지 알고리즘이 있는데요. Argon2i, Argon2d, Argon2id 입니다. 각 특징에 대해서 알아보겠습니다.
- Argon2i : Argon2i는 메모리를 순차적으로 접근해서 앞서 설명했던 메모리 버스의 모니터링 (사이드채널) 을 의미없게 만들어버립니다. 왜냐하면 메모리 접근이 일정하면 패턴을 파악하기 힘들기 때문이죠.
마치 이것은 보물섬에서 금속탐지기를 들고 여기저기 탐색중인데 모든 곳에서 신호가 일정하게 흘러나오는 것과 같습니다. 그럼 어디를 파봐야하는지 알 수 없게 되죠.
하지만 병렬 공격에 대해서는 취약하다는 단점을 가지고 있습니다. - Agron2d : Argon2d는 메모리는 랜덤으로 접근합니다. 그래서 사이드채널이 가능하게 만들죠. 하지만 메모리 블록을 여러개 만들고 각각의 메모리 블록이 서로를 의존하게 만들어 병렬 공격에 면역을 가지게 합니다.
일반적으로 64MB의 메모리 블록으로 암호화를 진행하는데 하나의 메모리 블록은 보통 1KB입니다. 그럼 6만5천개 가량의 메모리 블록이 서로를 의존하게 되죠.
메모리 블록이 하나 만들어질 때 이전 메모리 블록 + 랜덤한 메모리 블록 이렇게 두 개를 조합해서 새로운 메모리 블록을 만들어냅니다.
이렇게 메모리 블록간 의존성이 강해지면 브루트포스를 진행할 때 병렬로 공격하기가 매우 힘들어집니다. 왜냐면 병렬로 공격한다는 얘기는 논블로킹에 가까운데 지금 상황은 블로킹에 가까워지니까요. - Argon2id : Argon2i와 Argon2d는 서로가 서로의 장점과 단점이 엇갈리는 기술들입니다. 그래서 이 둘을 하이브리드로 합쳐놓은게 바로 Argon2id입니다.
Argon2id는 초반에는 메모리 접근을 일정하게 해 사이드 채널 공격으로부터 안전하게 지키다가 후에는 메모리 접근을 랜덤하게 바꾸고 메모리를 서로 의존하게 만들어 의존성을 높입니다.
이렇게 되는 경우 메모리 접근이 일정해서 사이드 채널 공격으로부터 안전해지고 GPU를 이용한 병렬 공격에서도 안전해지는 것이죠.
2015년 Argon2 암호화 알고리즘이 등장한 이후 Password Hashing Competition (PHC) 에서 1등을 차지해서 보안전문가들도 인정하는 업계 표준이 되었습니다. 누가누가 더 암호화 잘하나 대회에서 일등한 것 같습니다.
그래서 권장하는 알고리즘은 Argon2id를 사용하는 것이 권장된다고 하네요.
Spring Boot와 Argon2
스프링 부트에서는 Spring Security Starter를 사용한다면 BCryptPasswordEncoder를 사용할 수 있었죠. Argon2도 사용할 수 있게 되었는데 2018년 Spring Security 5.05 버전에서부터 사용할 수 있게 되었습니다.
기존과 마찬가지로 BCryptPasswordEncoder를 주입받듯이 Argon2PasswordEncoder를 주입받아서 사용하면 Argon2로 비밀번호가 암호화됩니다.
저도 지금 운영하는 프로젝트가 BCryptPasswordEncoder로 되어있는데 제 프로젝트는 인증된 소수의 사용자만 사용가능 하도록 해야해서 이참에 Argon2로 암호화 알고리즘을 바꿔야겠습니다.
마치며
제목에서 어그로가 강하게 느껴지지만 BCrypt 암호화 알고리즘이 단방향 해시이고 SHA256과 어떤 부분이 다른지 챗지피티와 커피챗을 하다가 지피티가 요즘은 Argon2라는 알고리즘이 표준이라는 얘기를 듣고 공부해봤습니다.
요즘 지피티와 이론 공부하는 것이 너무 재밌네요. 질문에 대한 답이 끝나면 "이런저런것도 있어 어떤게 궁금해?" 이런식으로 사고를 확장해주는게 너무 마음에 듭니다.
지피티와 이론 공부는 예전부터 많이 했었지만 항상 평소에 궁금한 것에 대해 답변을 받으면 그 이후에 뭘 물어봐야할지 몰라서 막막했었는데 요즘엔 다음 질문을 추천해줘서 너무 편합니다.
여러분들도 지피티를 이용해서 이론 공부를 해보시면 만족스러울 것이라고 확신합니다.
이번 포스팅은 여기서 마치도록 하겠습니다. 오늘도 즐거운 하루 되세요~
'CS 지식 > 보안' 카테고리의 다른 글
JWT, 함부로 사용하면 다친다? (0) | 2024.08.18 |
---|---|
인증과 인가 (0) | 2023.05.04 |
SSL handshake / TLS handshake (0) | 2023.03.26 |
공개키 암호화 (0) | 2023.03.21 |