단방향 암호화(One-Way Encryption)
단방향 암호화란 한쪽 방향으로 암호화를 한다는 의미입니다. 즉 암호화만 가능하고 복호화는 할 수 없습니다. 그렇기 때문에 비밀번호를 관리할 때 유용하게 사용됩니다. 비밀번호 단방향 암호화 방식으로 저장하는 경우에는 패스워드 DB가 유출되어도 안전합니다.
패스워드를 검증할 때에는 사용자로부터 입력받은 비밀번호를 똑같은 방식으로 암호화하여 암호화된 패스워드끼리 비교를 하면됩니다.
유저가 비밀번호를 잊어버렸을 때는 찾기가 불가능합니다. 대신 비밀번호 변경 메일, SMS인증을 통해 새로운 비밀번호를 입력하도록 하면 됩니다.
대표적으로 많이 사용하고 있는 알고리즘은 SHA-256암호화 알고리즘입니다.
단방향 암호화 종류
해시(Hash) 알고리즘을 사용한다.
- MD5(Message-Digest algorithm 5)
- 128비트 암호학 해시 함수
- 현재는 안전하지 않아 사용하지 않습니다. (결함을 이용해 SSL인증서 변소 가능)
- SHA-1 : 안전X
- SHA-2(SHA-256) : 권장
임의의 길이 메세지를 256 bits의 축약된 메세지로 만드는 알고리즘이다.
경우의 수가 2^256 이므로 무차별 대입을 통한 공격에 비교적 안전하다.
(SHA-512의 경우의 수는 2^512)
SHA-256은 현재 가장 많은 분야에서 채택하여 사용되고 있는 암호 방식이다.
속도가 빠르기 때문에 인증서, 블록체인 등 많이 사용되고있으며, SHA-2라고 하면 SHA-256이라고 말할 정도로 상용화가 잘 되어있다
단방향 해시 함수의 한계점
Rainbow Table (레인보우 테이블)
동일한 메세지는 동일한 다이제스트를 갖는다.
다이제스트 : 해시 함수라는 수학적인 연산을 통해 생성된 암호화된 메시지를 의미합니다.
Rainbow table은 해시 함수를 사용하여 변환 가능한 모든 해시 값을 저장시켜 놓은 표입니다. 보통 해시 함수를 이용하여 저장된 비밀번호로부터 원래의 비밀번호를 추출해 내는데 사용됩니다. (다이제스트들의 테이블)
이렇게 사용자들이 많이 사용하는 password나 복잡하지 않은 암호의 경우 이미 공격자들이 대입해보았을 확률이 높습니다.
다음은 CrackStation 사이트 입니다.
- dog 문자열 SHA-256으로 암호화
CD6357EFDD966DE8C0CB2F876CC89EC74CE35F0968E11743987084BD42FB8944
"dog"결과가 나오는 것을 알 수 있습니다.
보완 : Salting
패스워드에 임의의 문자열인 salt를 추가하여 다이제스트를 생성합니다.
같은 패스워드라고 각기 다른 salt가 들어가 다이제스트가 다르게 생성되어 rainbow table을 무의미하게 만듭니다.
salt는 최소 128bit정도는 되어야 안전합니다.
Brute Force 공격 (무차별 대입 공격) - 속도
해시 함수는 원래 빠른 데이터의 검색을 목적으로 설걔된 것입니다. 그렇다보니 해시 함수를 써도 원문의 다이제스트는 금방 얻어집니다.
해시 함수는 원래 짧은 시간에 데이터를 검색하기 위해 설계된 것으로 해시함수의 빠른 처리 속도로 인해 해커는 매우 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있습니다.
: 빠른 속도의 해싱 함수가 문제점입니다.
보완 : 키 스트레칭
해시를 여러 번 반복하여 시간을 늘림으로써 무차별 대입 공격에 대비합니다.
패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력값으로 하여 또 다이제스트를 생성하는 것을 반복합니다.
python bcrypt
hahsing: 원본의 의미를 알 수 없게 섞어놓는 것
salting: 실제 비밀번호에 랜덤 값을 더해서 해시값을 계산하는 방법
key stretching : 해쉬 값을 여러번 반복해서 해시 하는 행위
import bcrypt
password = 'pass1234'
b = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
result = bcrypt.checkpw(password.encode('utf-8'), b)
print(result) # True
다음과 같이 bcrypt를 불러오고 password라는 변수에 str형식의 암호화 할 대상을 담았습니다. 그리고 암호화 함수는 오직 bytes string에서만 작동하기 때문에, 해싱하기 전에 password를 bytes string으로 변환해 줘야 합니다. 현재 password는 str입니다. 이 때 utf8인코딩을 하기로 합니다.
토큰 발행
import jwt
encoded_jwt = jwt.encode({'user_id': 1}, "secret", algorithm="HS256")
print(encoded_jwt)
print(jwt.decode(encoded_jwt, "secret", algorithms=["HS256"]))
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.J_RIIkoOLNXtd5IZcEwaBDGKGA3VnnYmuXnmhsmDEOs
// {'user_id': 1}
사용자가 id, password를 입력해 로그인 버튼을 누르면 서버는 id와 password를 db에 저장된 것과 비교하고 인증이 완료되면, 회원에게 토큰을 발행해 줍니다. 그 토큰은 사용자 브라우저의 쿠키나 세션스토리지에 저장되어 서버에 데이터를 요청할 때마다 토큰을 담아서 정보를 요청하고, 서버는 해당 토큰을 검증한 뒤 데이터를 전송합니다.
토큰 대상, 시크릿 키, 해싱 알고리즘을 차례로 넣어줍니다.
다만 토큰 방식도 토큰이 탈취당할 경우 정보 보안에 타격을 입는다는 것은 같습니다. 이를 보완하기 위해 refresh token방식도 존재합니다.
'School > Security' 카테고리의 다른 글
[ Web Hacking ] - Cookie & Session (0) | 2022.05.26 |
---|---|
[ Web Hacking ] - W31c0m3, w3b h4ck3rs! (0) | 2022.05.25 |
[ Node.JS ] Express-Session과 암호화 (0) | 2022.05.23 |
[ Basic Security ] - Ubuntu in Server (0) | 2022.05.15 |
[Basic Security ] - Linux basic (0) | 2022.05.10 |