[TIL] Authentication 인증

Captainjack·2021년 5월 27일
0

TIL

목록 보기
38/260

HTTPS

Hyper Text Transfer Protocol Secure Socket layer
HTTPS는 HTTP 요청을 SSL 혹은 TLS라는 알고리즘을 이용해, HTTP 통신을 하는 과정에서 내용을 암호화하여 데이터를 전송하는 방법

HTTPS 프로토콜의 특징

  1. 암호화된 데이터를 주고받기 때문에, 중간에 인터넷 요청이 탈취되더라도 그 내용을 알아볼 수 없다.

  2. 브라우저가 응답과 함께 전달된 인증서 정보를 확인할 수 있다.
    브라우저는 인증서에서 해당 인증서를 발급한 CA 정보를 확인하고 인증된 CA가 발급한 인증서가 아니라면 아래와 같이 화면에 경고창을 띄워 서버와 연결이 안전하지 않다는 화면을 보여줍니다.


데이터 제공자의 신원을 확인하고 보장 받는 이유는?

클라이언트는 데이터 제공자가 제공해준 데이터를 사용할 수 없어서,
서버에 데이터 요청을 하고 이후 받은 데이터를 이용해서 화면을 렌더링을 해야한다.

그렇기에 요청 및 응답을 중간에서 가로채는 중간자 공격에 취약
'중간자 공격'은 클라이언트와 서버 사이에서 공격자가 서로의 요청, 응답의 데이터를 탈취 및 변조하여 다시 전송하는 공격입니다.

데이터가 중간에 다른 도메인을 거쳐서 전달되기 때문에 서버가 해당 데이터는 https://example.com 도메인에서 제공되었습니다. 라는 추가데이터를 응답객체에 실어 보낸다면 '중간자 공격'으로 인해 다른 도메인에서 데이터를 받은 클라이언트는 데이터를 제공한 도메인과 전달받은 내용의 도메인을 비교하여 '중간자 공격'이 존재하는지 아닌지 확인할 수 있다.


mkcert라는 프로그램을 이용해서 로컬 환경(내 컴퓨터)에서 신뢰할 수 있는 인증서를 만들 수 있습니다.

설치방법

$ brew install mkcert

//If you use a firefox you add this too
$ brew install nss 

로컬 호스트 환경에대한 인증서를 만들기 위해서

$ mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1

이로 부터 key.pem과 cert.pem을 얻게 되고 이것으로 쿠키, 세션, 토큰에 이용


인증서 파일들을 HTTPS 서버에 적용해주려면??

//node.js https 모듈 이용
const https = require('https');
const fs = require('fs');

https
  .createServer(
    {
      key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
      cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
    },
    function (req, res) {
      res.write('Congrats! You made https server now :)');
      res.end();
    }
  )
  .listen(3001);
//express.js 이용
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

https
  .createServer(
    {
      key: fs.readFileSync(__dirname + '/key.pem', 'utf-8'),
      cert: fs.readFileSync(__dirname + '/cert.pem', 'utf-8'),
    },
    app.use('/', (req, res) => {
      res.send('Congrats! You made https server now :)');
    })
  )
  .listen(3001);

Hashing

어떠한 문자열에 임의의 연산을 적용하여 다른 문자열로 변환하는 것

특징

  1. 모든 값에 해쉬 값을 계산하는데 오래 걸리지 않아야 한다.
  2. 최대한 해시 값을 피해야 하며, 모든 값은 고유한 해쉬 값을 가진다.
  3. 아주 작은 단위의 변경이라도 완전히 다른 해쉬 값을 가져야 한다.

Salt

암호화 해야하는 값에 어떤 별도의 값을 추가하여 결과를 변형 하는 것 (진짜 양념 치는거임)

왜 양념을 쳐야할까?

  1. 암호화만 해놓으면 해쉬된 결과 값이 늘 동일
    -> 해쉬된 값과 원래 값의 테이블(레인보우 테이블)로 만들어서 decoding하면 쉽게 해킹당함

  2. 별도의 값을 추가했기에 기존 해쉬값과 전혀 다른 값을 가질 뿐만 아니라 알고리즘이 노출되어도 원본 값을 보호 가능

https://crackstation.net/
여기서 해쉬 값을 확인가능한데

솔트의 정보를 서버가 가지고 있고,
비밀번호에 + 양념을 쳐서 해쉬를 하게 된다.

좀 더 코드적으로 본다면

//original simple hashing 
sha256('redflOwer') //hashed

//hashing with salt
sha256('8zff4gflgfd93fgdl4fgdgf4mlf45p1redflOwer')

즉, 솔트 값을 추가하면 완전히 다른 값을 가지게 된다.

https://emn178.github.io/online-tools/sha256.html
직접 예제를 encoding 해볼 수 있는 사이트

salt 사용시 주의 점

  1. salt는 유저와 패스워드 별로 유일한 값을 가져야 한다.
  2. 사용자 계정 생성, 비밀번호 변경할 때 마다 새로운 salt 사용해야한다(서버)
  3. salt 재사용 금지
  4. salt는 DB의 유저 테이블에 같이 저장되어야 한다.

COOKIE

쿠키는 서버에서 클라이언트에 데이터를 저장하는 방법의 하나입니다.

기존 http의 가장 큰 문제점은 바로 statless(무상태성)이다.
인터넷 쇼핑에 있어서 장바구니의 항목들이 우리가 아무리 접속이 오래되도 다음 날 들어가도 유지되는 이유가 바로 이 쿠키 때문이다.

쿠키 옵션

  1. Domain
    도메인이라는 것은 여러분들이 흔하게 보실 수 있는 www.google.com과 같은 서버에 접속할 수 있는 이름입니다.
    쿠키 옵션에서 도메인은 포트 및 서브 도메인 정보, 세부 경로를 포함하지 않습니다.

여기서 서브 도메인이란 www 같은 도메인 앞에 추가로 작성되는 부분을 말합니다.

따라서 요청해야 할 URL이 http://www.localhost.com:3000/users/login 이라 하면

여기에서 Domain은 localhost.com이 됩니다.

만약 쿠키 옵션에서 도메인 정보가 존재한다면 클라이언트에서는 쿠키의 도메인 옵션과 서버의 도메인이 일치해야만 쿠키를 전송할 수 있습니다.

  1. Path
    세부 경로는 서버가 라우팅할 때 사용하는 경로입니다.
    만약 요청해야 하는 URL이 http://www.localhost.com:3000/users/login 인 경우라면 여기에서 Path, 세부 경로는
    /users/login이 됩니다.

명시하지 않으면 기본으로 / 으로 설정되어 있습니다.

Path 옵션의 특징은 설정된 path를 전부 만족하는 경우 요청하는 Path가 추가로 더 존재하더라도 쿠키를 서버에 전송할 수 있습니다.
즉 Path가 /users로 설정되어 있고, 요청하는 세부 경로가 /users/login 인 경우라면 쿠키 전송이 가능합니다.

하지만 /user/login으로 전송되는 요청은 Path 옵션을 만족하지 못하기 때문에 서버로 쿠키를 전송할 수 없습니다.

  1. MaxAge or Expires
    쿠키가 유효한 기간을 정하는 옵션입니다.

MaxAge는 앞으로 몇 초 동안 쿠키가 유효한지 설정하는 옵션입니다.

Expires 은 MaxAge와 비슷합니다. 다만 언제까지 유효한지 Date를 지정합니다.
이때 클라이언트의 시간을 기준으로 합니다.

이후 지정된 시간, 날짜를 초과하게 되면 쿠키는 자동으로 파괴됩니다.

하지만 두 옵션이 모두 지정되지 않는 경우에는 브라우저의 탭을 닫아야만 쿠키가 제거될 수 있습니다.

  1. Secure
    쿠키를 전송해야 할 때 사용하는 프로토콜에 따른 쿠키전송 여부를 결정합니다.
    만약 해당 옵션이 true로 설정된 경우, 'HTTPS' 프로토콜을 이용하여 통신하는 경우에만 쿠키를 전송 할 수 있습니다.

  2. HttpOnly
    자바스크립트에서 브라우저의 쿠키에 접근 여부를 결정합니다.
    만약 해당 옵션이 true로 설정된 경우, 자바스크립트에서는 쿠키에 접근이 불가합니다.

명시되지 않는 경우 기본으로 false로 지정되어 있습니다.
만약 이 옵션이 false인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 'XSS' 공격에 취약합니다.

  1. SameSite
    Cross-Origin 요청을 받은 경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부를 결정하게 됩니다.
    사용 가능한 옵션은 다음과 같습니다.

Lax :Cross-Origin 요청이면 'GET' 메소드에 대해서만 쿠키를 전송할 수 있습니다.

Strict : Cross-Origin이 아닌 same-site 인 경우에만 쿠키를 전송 할 수 있습니다.

None: 항상 쿠키를 보내줄 수 있습니다. 다만 쿠키 옵션 중 Secure 옵션이 필요합니다.

이때 'same-site'는 요청을 보낸 Origin과 서버의 도메인이 같은 경우를 말합니다.

이러한 옵션들을 지정한 다음 서버에서 클라이언트로 쿠키를 처음 전송하게 된다면 헤더에 Set-Cookie라는 프로퍼티에 쿠키를 담아 쿠키를 전송하게 됩니다.

이후 클라이언트 혹은 서버에서 쿠키를 전송해야 한다면 클라이언트는 헤더에 Cookie라는 프로퍼티에 쿠키를 담아 서버에 쿠키를 전송하게 됩니다.

mdn 쿠키내용
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie


SESSION

사용자가 인증에 성공한 상태는 세션이라고 부릅니다.
서버는 일종의 저장소에 세션을 저장
=> in-memory(like js 객체), 세션 스토어(redis 등과 같은 트랜잭션이 빠른 DB)에 저장

  • 서버가 클라이언트 한테 유일하고 암호화 된 ID를 부여
  • 중요 데이터는 서버에서 관리

클라이언 요청에 서버가 디비에서 해당 정보를 반환 할때 세션은 쿠키에 넣어서 같이 보내는데, 쿠키가 위험하기 때문에 session은 한번 더 암호화를 거치게 된다.(Set-cookie)
이 후 같은 클라이언트가 요청해 오면 기존에 저장되있던 암호화된 session id를 통해 인증을 진행하고 서버도 session id를 확인하고 요청에 결과를 응답해준다.

note: 클라이언트 로그아웃시에 서버가 클라이언트의 쿠키를 임의로 삭제할 수는 업성서 set-cookie로 세션 아이디의 키값을 무효한 값으로 갱신해야 함.


session의 단점

  1. 항상 세션 유지 데이터를 서버 저장 공간(메모리)에 가지고 있어야해서 가용 메모리가 줄어 서버의 성능이 떨어진다.

  2. 세션은 쿠키를 대체한 것이 아니라 쿠키를 이용하기 때문에 XSS공격에 취약하다.


express-session은 세션을 위한 미들웨어로, 'Express'에서 세션을 다룰 수 있는 공간을 보다 쉽게 만들어줍니다.
https://github.com/expressjs/session#reqsession


what is difference between session and cookies??


Token

Session은 하나의 서버에서만 접속 상태를 가지기 때문에 분산 시스템에 분리하다.
이것을 해결하기 위해 나온 것이 token

  • 세션 기반 인증 = 서버(or DB)에 유저를 담는 방식
  • 매 요청마다 서버에서 확인하는 불편함을 덜고 클라이언트에게 일을 넘겨 줌
  • JWT(Jason Web Token) 대표적인 토큰 기반 인증 => Json포맷으로 사용자에 대한 속성을 저장하는 웹 토큰

토큰을 클라이언트에 저장하는 해도 괜찮은 이유는 유저 정보를 암호화한 상태로 담겨져 있기 때문이다.

JWT 구조

  1. 헤더(header)
    1-1) 어떤 종류의 토큰인가
    1-2) 어떤 알고리즘으로 암호화 했는가
//json형태로 작성
{
  "alg": "HS256",
  "typ": "JWT"
}
  1. 내용(payload)
    2-1) 유저의 정보
    2-2) 권한을 부여 받았는가?
    2-3) 기타 필요한 정보
{
  "sub": "someInformation",
  "name": "phillip",
  "iat": 151623391
}
  1. 서명(signature)
    3-1) header, payload를 base64인코딩한 값과 salt값의 조합으로 암호화된 값
//HMAC SHA256알고리즘 사용 예시
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);

Note: JWT는 권한 부여에 굉장히 유용.
새로 다운받은 A라는 앱이 Gmail과 연동되어 이메일을 읽어와야 한다고 생각해 보자 유저는 Gmail 인증서버에 로그인정보(아이디, 비밀번호)를 제공하고,
성공적으로 인증시 JWT 를 발급받는다
A앱은 JWT를 사용해 해당 유저의 Gmail 이메일을 읽거나 사용할 수 있다

  1. 클라이언트는 서버에 아이디 비밀번호 담아서 POST /login 전송

  2. 서버는 DB에 아이디 비밀번호가 일치하는지 확인 후 jwt토큰을 생성(access/refresh 토큰을 모두 생성)해서 응답

  3. 클라이언트는 jwt 토큰을 저장(in local stroage or react state or cookie and etc..)
    ->local stroage = 브라우저에 생긴 작은 저장공간?(쿠키저장가능)
    ->Refreshtoken은 서버에 저장

  4. 이후 클라이언트가 GET요청으로 내정보보 보기 처럼 서버에 정보를 요구함
    =>http 헤더(authorization 헤더)에 토큰을 담아서 보냄
    => bearer authentication을 이용 (참고 링크)
    4-1) https://learning.postman.com/docs/sending-requests/authorization/#bearer-token
    4-2) https://datatracker.ietf.org/doc/html/rfc6750

  5. 토큰을 해독 후 자신들의 서버에서 보낸 값이 맞다면 요구한 정보에 대한 응답을 보내게 됨


토큰기반 인증의 장점

  1. Statelessness & Scalability (무상태성 & 확장성)
    서버는 클라이언트에 대한 정보를 저장할 필요 없다. (토큰 해독이 되는지만 판단합니다) => 클라이언트에게 이 일을 맡길려고 토큰 기반을 쓰는 것
    클라이언트는 새로운 요청을 보낼때마다 토큰을 헤더에 포함시키면 됩니다
    (bearer authentication을 이용)
    서버를 여러개 가지고 있는 서비스라면 더더욱 빛을 발휘합니다 (같은 토큰으로 여러 서버에서 인증 가능)
    => 기존 세션 방식의 한계 점을 극복

  2. 안전하다
    암호화 한 토큰을 사용하고, 암호화 키를 노출 할 필요가 없기 때문에 안전합니다.
    => 클라이언트에서 토큰을 저장해도 괜찮은 이유

  3. 어디서나 생성 가능하다
    토큰을 확인하는 서버가 토큰을 만들어야 하는 법이 없습니다
    토큰 생성용 서버를 만들거나, 다른 회사에서 토큰관련 작업을 맡기는 것 등 다양한 활용이 가능합니다.

  4. 권한 부여에 용이하다
    토큰의 payload(내용물) 안에 어떤 정보에 접근 가능한지 정할 수 있습니다
    ex) 서비스의 사진과 연락처 사용권한만 부여
    =>토큰의 가장 큰 장점


세션과 토큰의 차이

고급 빌딩의 사무실과 조금 시설이 낙후된 사무실을 비교하면
전자는 입구에서 부터 시작해서 각 방을 오다닐 때마다 항상 카드 키를 소지하면서 인식을 해야한다. (토큰jwt)

후자는 입구에서 한번 인증을 성공했다면 그 뒤로 건물안에 어디든 마음껏 돌아 다닐 수 있다.(세션)


추가 내용

bcrypt is a password-hashing function designed by Niels Provos and David Mazières
https://www.npmjs.com/package/bcrypt


profile
til' CTF WIN

0개의 댓글