우리는 굉장히 많은 플랫폼에 개인정보를 저장해두고 있다.
페이스북과 인스타그램과 같은 SNS에서부터 구글과 네이버, 그리고 서비스를 운영하는 스타트업까지도 사용자는 개인정보를 저장해 둔다.
하지만 우린 해커들로부터 개인정보가 유출된다는 기사를 종종 접하게 된다.
심지어 금융기관까지도.
https://www.boannews.com/media/view.asp?idx=96191
https://imnews.imbc.com/news/2023/econo/article/6458346_36140.html
그리고 그렇게 유출된 개인정보는 다른 플랫폼에서의 개인정보과 같을 가능성이 크다.
왜냐하면 사람들은 비밀번호를 매번 다르게 저장하지 않고 거의 비슷하게 저장하기 때문이다.
이러한 성향을 해커가 알고 있기 때문에 2차, 3차 금융피해가 발생한다.
그렇기 때문에 개인정보를 밀접하게 다루는 개발자들은 보안을 위해 힘쓰고
해커가 해독하지 못하게 해야 한다.
그럼 개발자들은 어떠한 방법으로 사용자의 개인정보, 즉 비밀번호를 어떻게 저장할까?
실제로 (거의) 모든 기업에서 비밀번호는 있는 그대로, 즉, 평문으로 저장하지 않는다.
거의 대부분은 해시 알고리즘을 통해 비밀번호를 관리한다.
해시는 임의의 길이를 갖는 임의의 데이터를 고정된 길이의 데이터로 매핑하는 단방향 함수를 뜻한다.
해시에는 4가지 특징이 존재하는데 다음과 같다.
- 암호화된 데이터는 복호화가 불가능하다.
- 입력값과 상관없이 항상 일정한 길이의 암호화 데이터를 출력한다.
- 입력값이 동일하면 암호화 데이터 또한 동일하다.
- 입력값이 다르지만 암호화 데이터가 같을 수도 있다.
하나씩 중요한 개념이기 때문에 알아가도록 하자.
단방향 함수라는 것은 복호화가 불가능하다는 뜻이다.
다시 말해, 해시를 통해 원본 데이터를 암호화된 데이터로 매핑이 가능하지만
암호화된 데이터로 원본 데이터를 구할 수는 없다.
참고로 암호화된 데이터는 다이제스트(digest)라고 한다.
암호화된 데이터 예시를 보기 위해 SHA 에 대해 알아보도록 하자.
단방향 해시 함수의 종류는 SHA
, MD
, WHIRLPOOL
등 다양하다.
그 중 SHA
는 미국 국가 안전 보장국(NSA)에서 개발된 함수로서 최초에 SHA-0
가 만들어지고 그 후 변형하여 SHA-1
, SHA-2
가 만들어졌다.
SHA-1
까지는 보안 취약점이 발견되어 사용하지 않는 것을 권고한다.
SHA-2
는 SHA-256
, SHA-512
등을 묶어서 부르는 명칭이다.
SHA-256
와 SHA-512
의 차이는 보안이 SHA-512
가 더 뛰어나다고 한다.
SHA-256 암호화: https://coding.tools/kr/sha256
SHA-512 암호화: https://coding.tools/kr/sha512
다시 돌아와서 SHA-256
을 통해 apple
를 암호화한다면 다음과 같다.
3A7BD3E2360A3D29EEA436FCFB7E44C735D117C42D1C1835420B6B9942DD4F1B
그리고 a
과 appleappleapple
을 입력해보면 다음과 같다.
// a
CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB
// appleappleapple
331D0E6DD9626B809681FB6C0F8F63E659CD151592D1905C35246DEBF15E00F1
이처럼 입력값의 길이와 상관없이 일정한 길이의 다이제스트를 출력하는 것을 볼 수가 있다.
또한 apple
과 a
와 appleappleapple
과는 어느정도 규칙성이 존재하지만
다이제스트를 보면 규칙성이 존재하지 않는 것처럼 완전히 다른 값인 걸 확인할 수가 있다.
이러한 다이제스트 값을 개발자들은 DB에 저장하는 것이다.
이는 곧 해시를 통해 사용자의 비밀번호를 보관할 수 있는 이유이다.
SHA-256을 통해 apple
을 해싱한다면 다음과 같다.
3A7BD3E2360A3D29EEA436FCFB7E44C735D117C42D1C1835420B6B9942DD4F1B
그리고 다시 apple
을 입력해보면 다음과 같다.
3A7BD3E2360A3D29EEA436FCFB7E44C735D117C42D1C1835420B6B9942DD4F1B
토씨하나 안틀리고 같은 걸 볼 수가 있다.
즉 사용자가 회원가입하면서 비밀번호를 입력했다면 해당 다이제스트 값을 DB에 저장하고
사용자가 로그인 할 때에 입력한 비밀번호의 다이제스트 값을 DB에 있는 값과 비교해 일치하면 로그인을 허락해 주는 원리이다.
코드로 구현하면 다음과 같이 작성할 수 있겠다.
(node.js
코드이고, SHA-512
를 사용했다.)
app.post('/auth', (request, response) => {
const { id, password } = request.body
connection.query(`SELECT * FROM user WHERE user_id="${id}" and password=SHA2("${password}", 512)`,
(error, results) => {
if (error) {
response.status(500).json({
message: '서비스 처리 불가'
})
return
}
if (results <= 0){
response.status(403).json({
code: 4,
message: '아이디를 다시 확인해주세요.'
})
return
}
response.status(200).json(results[0])
})
})
해시가 서로 다른 2개의 입력값에 대해 동일한 출력값을 내는 상황을 해시 충돌 이라고 한다.
이는 해시는 항상 고정된 길이의 다이제스트를 출력한다는 특징에 비롯된다.
만약 고정되지 않고 무한대의 다이제스트를 출력한다면 해시 충돌은 발생하지 않을 것이다.
하지만 무한대가 아니라는 점에서 이미 충돌의 가능성을 시사하는 점은 생일 문제, 비둘기집 원리를 통해 설명이 가능하다.
생일 문제: 생일 문제란 생일이 겹치는 2명은 366명의 사람이 모였을 때 달성된다는 것으로 모든 경우의 수를 넘어서는 통계 표본이 존재할 때 중복되는 값은 필연적으로 발생한다는 것이다. 실제로 23명만 모여도 생일이 같은 두 사람이 있을 확률이 50%가 넘고 57명이 모이면 99%를 넘는다고 한다.
비둘기집 원리: 비둘기가 5마리일 때 상자가 4개 뿐이라면 비둘기를 균등하게 분배해도 최소한 한 상자에는 2마리의 비둘기가 들어간다.
위의 특징을 통해 개발자들은 사용자의 비밀번호를 DB에 안전하게 보장할 수 있을 것 같지만 실상은 다르다.
위에서 언급했던 apple
을 입력하면 언제나, 항상 동일한 다이제스트 값을 출력한다.
그리고 이러한 특징은 해커 또한 인지하고 있다.
그래서 해커들은 방대한 경우의 수를 통해 알아낸 다이제스트 값들을 리스트화한다.
그렇게 리스트화 한 것을 레인보우 테이블(Rainbox Table)이라고 부른다.
그리고 다이제스트 값을 레인보우 테이블에서 찾아보면서 평문화된 비밀번호를 찾는 것이다.
위의 이미지와 같이 apple
을 SHA-256
을 통해 해싱한 값을 입력했더니
정확히 apple
을 출력한 것을 볼 수가 있다.
해시를 통해서 암호를 보관한다고 하더라도 위와 같이 취약점이 있다.
이를 보완하기 위해 도입된 것이 바로 솔트(Salt) 이다.
솔트란 해싱하기 전에 원본 데이터에 임의의 문자열을 덧붙이는 것을 뜻한다. 단어 뜻 그대로 원본 데이터에 임의의 문자열을 붙인다는 의미의 소금친다(Salting)는 것이다.
그리고 각 사용자마다 다른 임의의 문자열, 즉 다른 솔트를 사용한다면 한 명의 비밀번호가 유출되어도 각각 다른 다이제스트 값을 가진다.
예를 들면 모든 사용자가 apple
이라는 비밀번호를 사용한다고 할 때,
각각 솔트는 다음과 같다.
유재석: 2jqwuhqwiub3ruq9haois===
박명수: fidf89hieufisdoiasjfo===
노홍철: asidjaoisnoqiwfqokr2n===
그럼 해싱하기 전 원본 데이터는 다음과 같을 것이다.
유재석: apple2jqwuhqwiub3ruq9haois===
박명수: applefidf89hieufisdoiasjfo===
노홍철: appleasidjaoisnoqiwfqokr2n===
이 상태로 SHA-256
을 돌려보면 다음과 같은 값을 얻을 수 있다.
유재석: 91AC9A2F3CC71515092471385DE1E61503A41414AEE25D642F4EDC5C79C2760F
박명수: E28A83BD9D6716879F052454D91E9F54F167C93DB68CC602F5AF9AD4207C1701
노홍철: E864F4955863EF8A392B1119FD907B036CECC2035AF4BC77B3C3B836B7B3C207
그럼 아무리 같은 비밀번호를 사용한다 손치더라도, 사용자마다 각기 다른 솔트를 사용하기 때문에 다이제스트 값 또한 다르게 출력된다.
마지막으로 솔트된 문자열을 해싱한 다이제스트 값을 여러 번 반복시킨다면 더욱 보완이 뛰어나게 된다.
예를 들면 솔트를 치고 해싱한 유재석의 비밀번호는 다음과 같다.
유재석: 91AC9A2F3CC71515092471385DE1E61503A41414AEE25D642F4EDC5C79C2760F
해당 값을 한번 더 SHA-256
에 돌린다면 다음과 같은 값이 나올 것이다.
유재석: B4B5817DA42C870538DD96FE43689574F09AFADC9E8E20FE5959F858C1DCFC4D
이로써 해커들의 레인보우 테이블을 방지할 수 있을 뿐더러 추측할 가능성도 현격하게 낮추게 된다.