TIL - authentication, session/token based server building

김수지·2020년 1월 8일
1

TILs

목록 보기
23/39

Today What I Learned

Javascript를 배우고 있습니다. 매일 배운 것을 이해한만큼 정리해봅니다.


1. 인증(authentication) 관련 개념 정리

1. 쿠키(cookie)란?

  • 서버가 사용자 위치에 정보를 저장하고 불러올 수 있는 수단
  • 서버와 클라이언트가 대화하는 수단이라고도 볼 수 있음
  • 브라우저가 서버와 연결되면 서버는 쿠키 유무를 확인하고 → response 내 쿠키 부분에 내용을 담아서 전달 → 해당 호스트에서 보내는 요청에는 쿠키가 계속 심겨서 주고 받을 수 있다.
  • 쿠키의 구성: 이름, 값, 만료날짜, 경로 정보

2. 세션(session)이란?

  • 서버와 클라이언트 연결이 활성화 된 상태
  • 브라우저의 첫 접속 시에 고유 값(세션ID)을 따서 서버 측에서 in-memory 형식으로 관리한다.
  • 서버에서는 세션 객체에 임의 정보를 추가할 수 있다.
  • 사용자 정보 중 보안상 중요한 데이터는 쿠키가 아닌 세션에서 관리한다. 출처: 코드스테이츠

3. 토큰(token)이란?

  • 인증을 위해 사용되는 암호화된 문자열
  • 클라이언트가 토큰을 가지고 있으면 신원 인증이 된 것으로 여기고 별도의 유저 상태 관리를 하지 않기 때문에 서버 측에서는 해당 api를 수월하게 생성할 수 있다.
  • 토큰을 사용하면 세션처럼 서버에서 클라이언트의 상태 정보를 저장하지 않아도 되어서 서버 측의 무리가 덜 가고, 사용자 세션 관리를 위한 비용이 절감된다.

4. 캐시(cache)란?

  • 가져오는데 비용이 드는 정보/데이터를 임시로 복사/저장하는 장소(컴퓨터나 서버 등)

5. 개념 정리 참고

2. 암호화(Encryption)

1. 해싱(hashing)이란?

  • 어떤 문자열에 임의의 연산을 적용해 다른 문자열로 변환하는 것
  • 해싱의 특징
    • 해시로 변환 시 계산이 너무 오래 걸리면 안 된다.
    • 어떤 값을 해싱했을 때 매번 고유한 값으로 변환해야 한다.
    • 값에 아주 작은 단위의 변화를 주어도 해시 값은 완전히 달라야 한다.

2. crypto

  • node JS 내장 암호화 모듈, 다양한 해싱 알고리즘을 적용하여 암호화를 쉽게 구현해준다.
  • salt: 해시하려는 원본 값에 추가하는 임의의 값(소금을 치는 느낌?), salt를 더해 기존 해시값과 전혀 다른 해시 값을 반환되해 알고리즘이 노출 되더라도 원본 값을 보호
  • 암호화 하려는 값 + salt → 해싱
  • user마다 salt를 다르게 적용할 수 있음

3. 해싱 알고리즘

  • hmac 사용하기
     const password = crypto.createHmac('sha256', 'salt')
       .update(data.password)
       .digest('hex');
  • createHash 사용하기
    const secret = 'aaaaaa'
    const password = crypto.createHmac('sha256', secret)
    .update('hello world')
    .digest('base64');
  • pbkdf2/pbkdf2Sync 사용하기
    - Sync 없이 작성 시 해싱 후 callback을 사용할 수 있다.
    - Sync를 추가하면 callback 없이 동기적 처리가 가능하다.
    let password = 'abcdef'
    const hash = crypto.pbkdf2Sync(
    password, //해싱할 값
    'salt', // salt
    123456, // 해싱 반복 횟수
    64, // 바이트 길이
    'sha512' // 알고리즘 방식
    );
    password = hash.toString('hex');
  • randomBytes 사용하기
    • salting 부분을 랜덤으로 생성하고 싶다면 randomBytes를 사용하면 된다.
    crypto.randomBytes(64, (err, buf)=>{ 
    // 랜덤하게 생성된 버퍼를 base64를 통해 인코딩
    	  const randomlyGeneratedSalt = buf.toString('base64') 
    	  crypto.pbkdf2
            ('aaaaaa', //해싱할 값
              randomlyGeneratedSalt, //랜덤으로 생성된 salt
              123456, // 해싱 반복 횟수
              64, // 바이트 길이
              'sha512', // 알고리즘 방식
              (err, derivedKey)=>{
              //콜백, derivedKey에 해싱된 값이 담김
      	      if(err) throw err; 
       	 	console.log(derivedKey.toString('hex')) })
    })

3. Express-session package

1. express-session 패키지란?

  • 익스프레스 서버에서 세션 객체를 사용해 세션을 관리해주는 기능을 한다.

2. session 객체의 이용

  • express-session가 만들어준 세션 객체는 request에 위치하고, 세션 객체 내 변수로 값을 담을 수 있다. 세션 객체 내 sessionId라는 키에는 해당 session의 고유값을 담고 있다. 서버는 인메모리 형태로 sessionId들을 저장하고 기억한다.

3. 클라이언트와의 소통

  • express-session 덕분에 서버는 request.session.sessionId 값을 response의 header 부분에 set-cookie 키에 대한 값으로 담아서 보낸다. 그 결과 클라이언트는 서버로부터 sessionId를 받게 된다. (의사소통이 이루어진 부분)
  • 클라이언트는 이렇게 받은 sessionId를 storage에 담고 있다가 다음번 api를 호출할 때 다시 request의 cookie에 담아서 보내준다. 서버는 api 호출 요청이 오면 request.cookie에 담긴 sessionId를 찾아 해당 하는 세션 객체에 담긴 유효한 값들을 꺼내와서 이를 기준으로 db와 소통하고 적절한 response를 보낸다.
  • 만약 이 과정에서 클라이언트로부터 서버가 기억하지 못하는 sessionId가 오면 서버는 클라이언트에 권한이 없다는 응답을 보내게 된다.(unauthorized)

4. JWT(JSON Web Tocken)

1. JWT란?

  • 개념: 위에서 토큰이란 인증을 위해 사용하는 암호화 된 문자열이라고 했는데, jwt는 JSON 형식의 토큰이라고 이해하면 된다.
  • 구성 요소 : 헤더, 페이로드, 서명
    • 헤더 : 토큰 타입과 알고리즘 형식을 지정해준다 {"type" : "JWT", "alg": "HS256" }
    • 페이로드: 서버에 보낼 정보가 들어간다.일반적으로 유저별 고유값과 토큰의 유효기간이 담긴다. 각 정보의 단위는 클레임(claim)이라고 부르고 이 클레임들을 모아 페이로드를 배치한다.
      • registered claims : 토큰의 발급자, 제목, 대상자 등 토큰에 대한 정보들을 담는다. 키 값은 이미 예약되어있다.
      • public claims : 공개 가능한 클레임들은 충돌이 방지된 (collision-resistant) 이름으로 키 값을 URI 방식으로 작성한다.
      • private claims : registered도 public도 아닌 클레임을 담는다. 보통은 서버와 클라이언트 간 합의 한 클레임들을 담는다.
    • 서명: Base64 방식으로 인코딩한 헤더, 페이로드 그리고 SECRET KEY를 더해 해싱된다.
    • JWT의 최종 형태 : 헤더 + "." + 페이로드 + "." + 서명
  • 참고 : 벨로퍼트님 블로그 https://velopert.com/2389

2. token 인증 방식

  • session 방식과 달리 token 방식 인증에서는 서버가 세션을 저장하지 않는다. 대신 클라이언트와 소통을 위해 토큰을 만들어 발행하여 response의 cookie에 값을 담아 전달한다.
  • 그런 다음 서버는 클라이언트로부터 받은 request.cookie에 토큰을 decode하여 authorization 여부를 확인한다.
  • 참고 글 : jwt 기반으로 사용자 인증 구현하기 https://victorydntmd.tistory.com/116

3. 클라이언트와 소통

  • 서버로부터 response.cookie를 통해 토큰을 받은 클라이언트는 이 토큰을 브라우저의 cookies 등에서 가지고 있는다.
  • 그 후 서버에 또 다른 api 호출을 해야할 때 이 토큰을 request.cookie에 담아 호출할 때 함께 서버로 보낸다.

4. 디코딩 방식

  • 서버에서는 jwt 패키지를 사용하여 jwt.verify 방식으로 디코딩을 실시한다.
  • 디코딩 된 모양 또한 JSON이므로 객체 내에 필요한 정보를 검색하여 인가 여부를 확정지을 수 있다. (이쯤되면 객체지향만세)
  1. jwt 인증의 장점과 단점
  • 장점: 토큰 이용 시 서버가 자체 세션을 인메모리로 저장하지 않아도 되어서 유저의 세션 관리 비용 부담이 줄어들고 서버의 부하를 줄일 수 있다.
  • 단점: 토큰에 중요한 내용을 많이 담고 있기 때문에 토큰이 쉽게 디코딩 되는 경우, 악의적으로 유저만 볼 수 있는 내용들을 중간에서 가로챌 수 있는 위험성이 있다.

5. Sequelize association 설정

1. 모델 간 관계 정의(association)

  • SQL을 사용하여 테이블 간 관계 정의가 가능하듯, ORM인 sequelize에서도 모델링 과정에서 모델 간 관계 정의가 가능하다.

2. 관계 별 사용 구문

  • 1 대 1 관계 : 모델1.hasOne(모델2) *모델2를 타겟 모델이라 부른다.
       users.associate = function(models) {
        users.hasMany(models.urls, { //hasMany 안에서 foreignKey 설정
          foreignKey: 'user_id'
        }); //! belongsTo를 하지 않더라도 urls에 userId(외래키)를 자동으로 추가해줌
     };
  • 1 대 다 관계 : 모델1.hasMany(모델2) 혹은 모델2.belongsTo(모델1)
 urls.associate = function(models) {
    urls.belongsTo(models.users, { //belongsTo 안에서 foreignKey 설정
     foreignKey: 'user_id'
     }); //! hasMany를 하지 않더라도 urls에 userId(외래키)를 자동으로 추가해줌
  };

3. references로 직접 지정

  • 모델링을 하면서 필드를 설정하면서 reference 옵션으로 키를 직접 연결할 수도 있다.
     module.exports = (sequelize, DataTypes) => {
     const urls = sequelize.define(
       'urls',
       {
         url: DataTypes.STRING,
         baseUrl: DataTypes.STRING,
         code: DataTypes.STRING,
         title: DataTypes.STRING,
         user_id: {
           type: DataTypes.INTEGER,
           allowNull: false,
           references: { //references에서 model과 key를 직접 설정
             model: 'user',
             key: 'user_id'
           } // user 모델에 user_id가 foreignKey가 된다.
         }
       }

4. 참고 글

6. Sequelize hooks lifecycle

1. hooks lifecycle이란?

  • Sequelize가 모델링하는 전체적인 과정이 lifecycle, 각 단계마다 특정 작업을 지정할 수 있도록 hook 설정을 할 수 있다.
  • 난 개인적으로 훅스를 보면서 마치 리액트의 라이프사이클 같다는 인상을 받았다.

2. 사이클 종류

  • 크게 3단계를 기준으로 앞/뒤를 나눈다.
    • BulkCreate/BulkDestroy/BulkUpdate의 앞/뒤
    • Validate의 앞/뒤
    • Create/Destroy/Update/Save/Upsert의 앞/뒤
  • 시퀄라이즈 매뉴얼 : https://sequelize.org/master/manual/hooks.html

3. hooks lifecycle을 이용해서 패스워드 해싱하기

  • beforeCreated, beforeFind에서 비밀번호를 해싱하면 서버에서 db에 비밀번호를 생성하거나 검색할 때 해싱 처리된다. 따라서 db에는 해싱된 비밀번호만 남게 된다.
profile
선한 변화와 사회적 가치를 만들고 싶은 체인지 메이커+개발자입니다.

0개의 댓글