유저의 identification 을 확인하는 절차를 인증(Authentication) 이라고 한다.
access token
을 클라이언트에게 전송.access token
을 request header 에 넣어 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다. (stateless 한 HTTP 의 특징)패스워드를 암호화 하는 방식에는 여러가지가 있지만 우리가 흔히 쓰는 방식은 단방향 암호화 이다.
양방향 암호화는 해쉬된 값을 복호화 할 수 있지만 단방향 암호화는 복호화가 불가능하다.
우리가 사이트 비밀번호를 까먹었을 때 비밀번호 찾기를 누르면 원래 비밀번호를 알려주는게 아니라 새로 다시만들라고 하는 것도, 복호화가 불가능하기 때문에 아예 새로 만들라고 하는 것이다.
avalance 한 성격 또한 단방향 암호화의 특징이다.
'test' 를 해쉬했을 때와, 'test1' 을 해쉬했을 때의 값은 전혀 비슷하지 않고 완전히 다른데 이를 avalance 한 효과 라고 한다.
물론 단방향 암호화에도 단점이 존재하긴 한다.
Rainbow table attack 이라고 해서 미리 해쉬값들을 계산해 놓은 테이블을 Rainbow table 이라고 한다.
해시 함수는 원래 패스워드를 저장하기 위해서 설계된 것이 아니라 짧은 시간에 데이터를 검색하기 위해 설계된 것
처리 속도가 최대한 빠르도록 설계됨
덕분에 공격자는 매우 빠른 속도로 Rainbow table 의 해쉬된 값들과 해킹할 대상의 암호화 된 다이제스트를 비교 가능
MD5를 사용한 경우 일반적인 장비를 이용하여 1초당 56억 개의 다이제스트를 대입 가능
따라서 salting 과 key stretching 기법을 이용하여 이런 취약점을 보완할 수 있다.
실제 패스워드 이외에 추가적으로 랜덤 데이터를 넣어서 함께 해시하는 방법이다.
이미 한번 계산 된 단방향 해쉬 값을 해쉬하고 또 해쉬하고 반복하는 방법이다.
해쉬 반복이 추가될 때 마다 다이제스트 값을 비교하는 속도가 엄청나게 느려진다.
Salting 과 Key Stretching 로직을 동시에 수행하도록 구현한 해쉬 함수중 가장 널리 사용되는 것이 bcrypt 이다.
db 내부에 패스워드를 db 내에서 비교하지 않고,
db 에서 패스워드를 꺼낸 다음에 값을 비교해야 되는 상황이기 때문에
다음과 같이 User instance 를 가져온다음,
해당하는 password 를 bcrypt.checkpw() 메소드로 비교할 계획이였다.
User.objects.filter(Q(email=email) | Q(username=username) | Q(phone_number=phone_number))
이 과정을 수행하던 중 문제가 발생했다.
원래는 username, email, phone_number 셋 중 하나만 입력해도 로그인에 성공할 수 있게 하려고 했었다.
그래서 테스트를 해보던 중
username=<유저이름>
phone_number=''
을 넘겨보았을 때 문제가 발생했다.
한 명의 유저 객체만 가져와야 하는데 여러 객체가 반환이 되었다.
unique=True
이지만 동시에 null=True
인 경우phone_number 를 등록하지 않은 유저의 phone_number 값은 db 상에서 null 이다.
따라서 만약 phone_number 를 입력을 받지 않았는데
Q(phone_number=None)
또는
Q(phone_number='')
을 수행하면 phone_number 를 등록하지 않은 유저가 몽땅 filter 에 걸리게 된다.
그래서 다음과 같이 email, username, phone_number 별로 해서 User 객체를 가져오면 그 객체를 사용하는 것으로 로직을 변경했다.