백엔드 복습-Day21

이예음·2022년 12월 27일
0

백엔드 복습하기

목록 보기
18/23
post-thumbnail

암호화(양방향 암호화)와 해싱(단방향 암호화)

백엔드 복습-Day20에서 회원 가입을 할 땐, DB에 비밀번호가 그대로 저장됨

이렇게 되면 DB가 해킹을 당하게 되면, 헤당 서비스 사이트의 이메일과 비밀번호를 가져감
=> 일반적으로 다른 사이트에도 동일한 이메일과 비밀번호를 사용하는 경우가 많아 추가 해킹의 가능성이 있음
따라서, 기밀성을 유지하기 위해 DB에 저장할 때, 비밀번호를 암호화(Encrypt)해서 저장한다.
암호화는 암호화에 사용된 키(비밀번호)를 통해 데이터를 복호화 할 수 있다.
이건 완전히 안전한 암호화가 아니어서 해싱(단방향 암호와)가 나타났다.

정의

암호화를 구성하는 요소

  • 평문(Plaintext) : 해독 가능한 형태의 메세지(암호화 전 메세지)
  • 암호문(Cipertext) : 해독 불가능한 형태의 메시지(암호화된 메세지)
  • 암호화(Encrytion) : 평문을 암호문으로 변환하는 과정
  • 복호화(Decrytion) : 암호문을 평문으로 변환하는 과정

암호화의 종류

여러가지가 있으나 크게 두 가지를 정리

  • 양방향 암호화(Encryption) : 암호화와 복호화과정을 통해 송 ・ 수신 간 주고받는 메시지를 안전하게 암호화하고 평문으로 복호화하는 과정
  • 단방향 암호화(Hash) : 평문을 암호문으로 암호화는 가능하지만 암호문을 평문으로 복호화 하는 것은 불가능(완전히 안전한 암호화)

단방향 암호화(Hash)

: 어떤 수학적 연산(또는 알고리즘)에 의해 원본 데이터를 매핑시켜 완전히 다른 암호화된 데이터로 변환시키는 것
hash에 의해 암호화된 데이터를 다이제스트(digest)라고 한다.
다이제스트를 복호화하여 원본 데이터를 구할 수 없게 하기때문에 단방향성이다.

단방향 해시 함수의 종류

  • SHA
  • MD
  • HAS
  • WHIRLPOOL

Key Stretching

: 개발자가 횟수를 정해서 Hash 함수를 돌리는 방법
알고리즘 암호화를 통해 나온 값을 보고 그 값이 나올 수 있는 모든 경우를 스캔하는 레인보우 테이블을 이용해여 무작정 다 대입해 복호화 하는 경우가 나오게 됨
그 점을 보완하기 위해 나온 방법 중 하나


단점

Hash 함수를 여러 번 돌리는 만큼 최종 다이제스트를 얻는데 그만큼 시간이 소요되기 때문에 속도 면에서 분리

Salt

: 해시함수를 돌리기 전에 원문에 임의의 문자열을 덧붙이는 것. 소금친다(Salting)는 표현을 사용했다.
Key Stretching은 결국 몇 번 돌렸는지 횟수만 알게된다면, 해킹의 가능성이 올라간다.
이를 방지하기 위해 도입한 것이 Salt이다.

이렇게 하면 다이제스트를 알아낸다 하더라도 password 를 알아내기 더욱 어려워진다.

21-01-signup-bcrypt

createUser Bcrypt

Bcrypt : Hashing의 대표적인 라이브러리
설치: yarn add bcrypt, yarn add --dev @types/bcrypt
user.resolver.ts

user.service.ts

결과

로그인 API

서버 기반 인증 시스템

: 서버 측에서 유저들의 정보를 기억하고 있어야함

단점

  • 서버가 유저의 인증 기록을 세션에 저장하는데, 로그인 유저가 많아지면 서버가 과부화됨
  • 세션을 사용하면 분산된 시스템을 설계하고 서버를 확장하는 것이 러여움
  • 세션을 관리할 때 사용되는 쿠키는 여러 도메인에서 관리하는 것이 번거로움

토큰 기반 인증 시스템

: 위에 서버 기반 인증 시스템의 문제점들을 해결하기 위해 사용

장점

  • 무상태(Stateless) & 확장성(Scalability)
    Stateless Server에서는 상태정보를 저장하지 않고, 서버는 클라이언트의 요청만으로 작업을 처리하며 세션을 사용하지 않는다. 따라서 토큰을 사용하면 클라이언트와 서버의 연결고리가 없어 서버를 확장하기에 매우 적합한 환경을 제공
  • 확장성(Extensibility)
    서버 확장 뿐만이 아닌, 로그인 정보가 사용되는 분야를 확장할 수 있음
    예시로는 Google계정을 이용해 Notion, Slack 등을 이용하는 것처럼 토큰에 선택적인 권한을 부여해서 발급할 수 있음

시스템 작동 원리

Json Web Token
: 토큰 기반 인증 시스템의 구현체
JWT는 웹표준으로서 C, Java, Python, JS등 대부분의 주류 프로그래밍 언어에서 지원
또한 필요한 모든 정보를 자체적으로 가지고 있어 자가 수용적(Self-contained)이며 그렇기에 두 개체 사이에서 쉽게 전달될 수 있는 장점을 가지고 있음

JWT 구성
.으로 구분되는 Header, Payload, Signature의 3가지 문자열로 구성

출처

  • Header
    : 토큰의 타입과 해싱 알고리즘을 담고 있음
{"alg":"HS256","typ":"JWT"}

보통 해싱 알고리즘은 HS256을 사용하지만 HS512를 이용해 토큰을 더 길게 만들 수 있음

  • Payload
    : 토큰에 담을 정보가 들어감
    담는 정보의 한 조각은 name/value의 한 쌍으로 이루어진 Claim이라고 부름
    Claim은 Registered, Public, Private의 세 분류로 나뉘어져 있으며 Registered Claim은 토큰 발급자, 토큰 제목, 토큰 만료시간, 토큰 발급시간 등 토큰에 대한 정보를 담기 위해 이미 이름이 정해진 Claim 이다.
  • Signature
    : 서명, Header의 인코딩값과 Payload의 인코딩값을 합친 후 주어진 비밀키로 해싱하여 생성

21-02-login-authentication

login API

auth.service.ts

JWT 모듈을 사용해서 accessToken 만들기
설치 : yarn add @nestjs/jwt passport-jwt, yarn add --dev @types/passport-jwt
참조 - NestJS Docs

auth.service.ts

💡 참고 - JwtService의 sign 메서드


user.service.ts

auth.resolver.ts

auth.module.ts

app.module.ts

결과

Authorization(인가)

Flow Chart

형식

{"Authorization":"Bearer accesstoken정보"}

Bearer : 토큰을 통해 인증할 때, 붙여서 사용하는 약속. 큰 의미가 없는 문자열

token 정보를 실어서 보내주게 되면 요청 Headers에 해당 토큰 정보가 들어옴
그러면 Backend API에서 해당 토큰을 뽑아서 복호화를 하여 인가를 할 수 있음
인가 과정은 passport 라이브러리를 사용
설치 : yarn add @nestjs/passport, yarn add passport
출처 - npm

21-03-login-authorization

fetchUser 1차 구현

user.resolver.ts

jwt-access.strategy.ts

user.module.ts

검증 로직 정리
1. 프론트에서 fetchUser API 요청
2. Query(fetchUser)를 실행하기전에 먼저 ‘myGuard’ 검증을 실행
3. strategy 파일에서 ‘myGuard’의 이름을 가진 검증 로직을 찾음
4. 해당 검증 로직을 찾아 super을 통해 JWT 옵션값들이 PassportStrtegy 로 넘겨져 jwt 토큰 방식으로 검증을 시작
5. 검증이 완료되면 토큰을 복호화되었을때 나오는 id와 email 값을 payload 형태로 받아옴
그렇지 않다면 프론트로 에러 반환
따라서, 검증에서 통과가 되면 fetchUser API가 실행 되고, 통과되지 않으면 API 가 실행되지 않음

fetchUser 2차 구현

GraphQL에서는 @UseGuards(AuthGuard('access'))를 사용할 수 없음
GraphQL에 사용 하기 위해선 graphql 에서 필요한 부분을 뽑아서 AuthGuard로 보내주어야함

gql-auth.guard.ts

user.resolver.ts

검증 로직 정리
1. 프론트에서 fetchUser API 요청
2. GqlAuthAccessGuard 를 통해 GraphQLGuard 검증을 진행 - gql-auth.guard.ts 파일에서 진행
3. 검증이 완료되면 AuthGuard('myGuard') 검증을 진행
4. jwt-access.strategy.ts 파일에서 ‘myGuard’의 이름을 가진 검증 로직을 찾음
5. 해당 검증 로직을 찾아 super을 통해 JWT 옵션값들이 PassportStrtegy 로 넘겨져 jwt 토큰 방식으로 검증을 시작
6. 검증이 완료되면 토큰을 복호화되었을때 나오는 id와 email 값을 payload 형태로 받음
그렇지 않다면 프론트로 에러 반환
7. 검증이 통과되면 UseGuard가 통과되어 fetchUser API가 실행 되고, 통과되지 않으면 API 가 실행되지 않음

결과


📌 오류 노트

자동완성이 validate가 아니라 validation으로 써놔서 에러가 나왔지만 금방 해결했다.

fetchUser 3차 구현

요청된 토큰으로 인증을 받은 로그인한 유저의 정보를 받아오기 위해서 커스텀으로 데코레이터를 제작
gql-user.param.ts

user.resolver.ts

결과

profile
응애

0개의 댓글