인증 보안

황지웅·2022년 5월 3일
0

인증보안

목록 보기
1/1
post-thumbnail

Achievement Goals

  • HTTP와 HTTPS의 차이점을 이해할 수 있다.
  • 암호화와 hashing, salting 등의 개념을 이해할 수 있다.
  • 권한 부여(Authorization)와 인증(Authentication)에 대해 이해할 수 있다.
  • 쿠키의 작동 원리를 이해할 수 있다
  • 세션 및 쿠키 / 토큰 / OAuth를 통해 인증 구현을 할 수 있다.
  • 클라이언트, 서버, 데이터베이스의 전체 동작을 이해할 수 있다.
  • 회원가입 및 로그인 등의 유저 인증에 대해 구현하고 이해한다.
  • 서비스의 보안과 관련된 방법을 알아보고 원리 및 장점 및 단점을 이해한다.

chapter: HTTP/HTTPS/SSL(TLS)


HTTP와 HTTPS의 개념

  • HTTP(Hypertext Transfer Protoco)
    : HTML을 전송하기위한 통신규약이다
  • HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)
    :SSL(Secure Socket Layer)위에서 이루어진, 상대적으로 보안이 강화된 HTTP 통신규약이다
    • SSL과 TLS는 같다고 생각해도 무관하다
    • 만약 FTP위에서 HTTP가 동작하면 SFTP가 된다
    • 좀더 풀어말하면 내가 사이트에 보내는 정보들을 제 3자로부터 보호(암호화)고 또 접속한 사이트가 믿을만한 곳인지(인증서)를 알려준다(SSL의 일련의 과정을통해). 덕분에 http보다 안전하다

암호화

  • HTTPS 프로토콜의 특징 중 하나는 암호화된 데이터를 주고받기 때문에, 중간에 인터넷 요청이 탈취되더라도 그 내용을 알아볼 수 없습니다

1.대칭키의 암호화와 복호화

  • 하나의키로 암호화하고 암호화한 코드를 복호화 할 수 있다
  • 만약 클라이언트와 서버측에 각대칭키를 가지고 암호화해 데이터를 주고받는다면 제3자는 암호화가되어있는 데이터에 접근할 수 없다
  • 문제는 어떻게 애초에 대칭키를 양 쪽이 공유하느냐이다
    결국 한쪽이 한쪽에게 키를 전송해야하는데 이과정중 키를 누군가가 탈취하게되면 암호화의 의미가 없어진다

2.비대칭키(공개키,비공개키)

  • 2개의 쌍을이룬 키가 사용되고 하나의키로 암호화하면 다른 하나의키만으로 복호화가 가능하다
  • 서버측은 개인키를 비밀리에관리하고 대중에게는 공개키를 오픈한다. 사용자는 오픈되어있는 공개키를이용해 데이터를 암호화해 데이터를 서버측에 보낸다.비대칭키를 이용한 복호화는 서버측소유의 개인키만이 복호화 가능하기에 이는 안정성이 보장된다

3.실제 웹사이트의 보편적인 암호화방식

  • 공개키와 비공개키를통해 해당서버와 데이터를 주고받을수있지만,
    이는 너무많은 비용을 요구한다. 해서 우리는 비대칭키와 대칭키를 혼합하여 사용한다

SSL 인증서

  • HTTPS 프로토콜의 또 다른 특징 중 하나는 브라우저가 응답과 함께 전달된 ssl인증서 정보를 확인할 수 있다는 점입니다. 이를 통해 신뢰할수있는 사이트인지 확인할 수 있다
  • ssl 인증서는 클라이언트와 서버간의 통신을 제 3자가 보증해주는 전자화된 문서이다
  • 이 ssl 인증서는 ca라는 기관을통해 허가를 받는다
  • 유저가사용하는 브라우저는 서버로부터 받은 인증서를 확인하고 인증서를 발급한 ca정보를 체크한다. ca가 발급한 인증서가 아니라면 아래와 같이 경고창을 띄워 서버와 연결이 안전하지 않다는 화면을 보여준다
    검증이 완료되면 다음절차를 수행한다
    • 브라우저에는 certificate authority(ca)라는 회사들의 목록이 내장되어있다

SSL 통신과정(HTTPS를 위한)

1.hand shake

  • 클라이언트는 서버측에 임의의 데이터를 보낸다(client hello)
  • 서버는 클라이언트측에 임의의 데이터와 함께 인증서를 보낸다(server hello)
  • 인증서 확인
    • 클라이언트는 브라우저를 통해 서버측으로 전달받은 인증서가 정품인지를 체크한다
      (ca에인증을받은 인증서라면 브라우저가 정품임을 인증을 해줄것이다)
    • 정품이인증되면 공개키(비대칭키)를 확인할 수 있다
  • 비밀 키생성(대칭키)
    • 클라이언트는 Hand SHake시 주고받은 데이터를 통해 새로운 키(session key/대칭키)를 생성하고, 이 키를 서버측의 공개키(비대칭키)로 암호화해 서버측에 전달한다
    • 서버측은 개인키로 복호화해 클라이언트측에서 보낸 키를 얻을 수 있고, 이제 대칭키 시스템을 통해 데이터를 안전하게 주고받을 수 있다

2.Session

  • session key(대칭키)를 이용한 암호화 방식 통신

3.End Session

  • seesion key(대칭키) 폐기(브라우저종료시)

사설 인증서 발급 및 HTTPS 서버 구현

0. 개요

  • HTTPS를 사용하여 브라우저에서 로컬로 실행되는 사이트를 열면 브라우저가 로컬 개발 서버의 인증서를 확인합니다.
  • 인증서가 mkcert 생성 인증 기관에 의해 서명되었음을 확인하면 브라우저는 해당 인증서가 신뢰할 수 있는 인증 기관으로 등록되었는지 확인합니다.
  • mkcert는 신뢰할 수 있는 기관으로 등재되어 있으므로 브라우저가 인증서를 신뢰하고 HTTPS 연결을 만듭니다.

1.로컬 환경(localhost)에서 인증서를 생성하고, 인증서를 이용해 HTTPS 서버를 만들기

  • mkcert설치
$ brew install mkcert
(macOS 사용자의 경우, Homebrew를 통해 mkcert를 설치할 수 있습니다.)
  • 인증서 생성

    • 로컬을 인증된 발급기관으로 추가
      $ mkcert -install
    • localhost로 대표되는 로컬 환경에 대한 인증서를 만들기
    $ mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1
  • cert.pem, key.pem 이라는 파일이 생성됨

    • key.pem의 경우 개인 키이므로 git에 커밋하지 않고, 암호처럼 다루어야 합니다
    • cert.pem은 공개키

3. 💻 React 개발 서버
package.json을 다음과 같이 편집하고 {PATH}를 대체합니다.

"scripts": {
"start": "HTTPS=true SSL_CRT_FILE={PATH} SSL_KEY_FILE={PATH} react-scripts start"

2.HTTPS 서버 작성(express 이용)

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

0. 개요

  • 암호화는 일련의 정보를 임의의 방식을 사용하여 다른 형태로 변환하여 해당 방식에 대한 정보를 소유한 사람을 제이하고 이해할 수 없도록 알고리즘을 이용해 정보를 관리하는 과정이다
  • hashing은 암호화의 기본이다
  • 우리는 남이만들어놓은 훌륭한 hash함수(배포된)를 이용하면된다 (sha1 ,sha256)

1.plaintext

  • 데이터베이스에 비밀번호를 있는 그대로 저장해놓으면, 데이터베이스에 접근만할 수 있다면 누구든지 비밀번호를 알수있다. 이는 보안적으로 매우 좋지않다

2.hashing

  • hashing은 어떠한 문자열에 '임의의 연산'을 적용하여 다른 문자열로 변환하는 것이다
    • 모든 값에 대해 해시 값을 계산하는데 오래 걸리지 않아야 한다
    • 동일한 입력값(input)에대한 동일한 출력값(output)을 갖고있다 => 입력값이 바뀌지 않으면, 출력값도 바뀌지 않는다
    • 입력값(input)이 아주 살짝 변경되어도, 이에대한 출력값은 어마무시하게 바뀐다
    • 해쉬함수는 항상 같은 방향,으로만 움직인다
      1234 => hash f(x) => adfg823n48139(r38(@
      adfg823n48139(r38
      (@ => hash f(x) !== 1234
  • hash로 비밀번호를 암호화하면 어느정도 보안이 되지만, 레인보우 테이블이라는 것 때문에 이마저도 문제가된다
    • 레인보우 테이블은 해쉬값을 입력값이랑 매칭해놓은 테이블이다. 이를통해 비밀번호에 접근할 수 있다

3.salt

  • salt는 아주 조그만 랜덤 텍스트이다. 유저의 패스워드를 salt와 함께 해쉬(암호화)하면 이는 아주 랜덤한 결과값으로 이루어지고 레인보우 테이블에서 찾을수 없을 것이다
    • salt는 유저와 패스워드 별로 유일한 값을 가져야 한다
    • 사용자 계정을 생성할 때와 비밀번호를 변경할 때마다 새로운 임의의 salt를 사용해 해싱해야힌디
    • salt는 절대 재사용하지 말아야한다
    • salts는 db의 유저테이블에 같이 저장되어야 한다

1.개요

  • http요청은 statelsee(무상태성)인데 어떻게 우리의 정보가 유지가 될까? 쿠키를 이용한다면 마치 이용자를 기억하는 것 처럼 동작하게 할 수 있다.
  • 쿠키란 어떤 웹사이트에 들어갔을때 서버가 일방적으로 클라이언트에 전달하는 작은 데이터 조각(보통은 의사를 묻지않지만 묻는경우도 있음)이다.
    • 쿠키에는 Token,Seesion Id 기타 등등 여러가지의 데이터를 담을 수 있고 이를통해 인증과 인가를 구현 할 수 있다
      (또 다른 기능을 생각해 볼 수도 있다
      ex: 비로그인 유저의 장바구니 목록 같은 것)
    • 쿠키에 직접적으로 인증,인가관련 데이터를 저장해 전달할수있지만 이는 매우 위험하다.
      (쿠키에 seesionId나 token을 담아 인증,인가를 구현하는 방법은 아래에서 후술하겠음)
    • 한마디로 데이터를 저장(중요하지 않은)하거나 전달(sessionId or token을)하는 매개체인 셈이다.
  • 쿠키가 브러우저에 저장된후,브라우저는 쿠키를 자동으로 요청과 함께 서버측으로 보낸다

2. 쿠키옵션

  • Domain

    • 서버와 요청의 도메인이 일치하는 경우 쿠키전송
    • 요청해야 할 URL이 http://www.localhost.com:3000/users/login 이라 하면 여기에서 Domain은localhost.com이 됩니다
  • Path

    • 서버와 요청의 세부경로가 일치하는 경우 쿠키 전송(세부 경로는 서버가 라우팅할 때 사용하는 경로입니다.)
    • 만약 요청해야 하는 URL이 http://www.localhost.com:3000/users/login 인 경우라면 여기에서 Path, 세부 경로는 /users/login이 됩니다.
  • MaxAge or Expires

    • 쿠키의 유효기간 설정하는 옵션
    • MaxAge : 앞으로 몇 초 동안 쿠키가 유효한지 설정하는옵션
    • Expires :언제까지 유효한지 `Date 지정
    • 두 옵션이 모두 지정되지 않는 경우에는 브라우저의 탭을 닫아야만 쿠키가 제거될 수 있습니다.
  • HttpOnly

    • 자바스크립트에서 브라우저의 쿠키에 접근 여부를 결정합니다.
    • 만약 해당 옵션이 true로 설정된 경우, 자바스크립트에서는 쿠키에 접근이 불가합니다.
    • 명시되지 않는 경우 기본으로 false로 지정되어 있습니다.
    • 만약 이 옵션이 false인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 'XSS' 공격에 취약합니다.
  • Secure

    • HTTPS 프로토콜에서만 쿠키 전송 여부 결정
    • 만약 해당 옵션이 true로 설정된 경우, 'HTTPS' 프로토콜을 이용하여 통신하는 경우에만 쿠키를 전송할 수 있습니다
  • SameSite

    • CORS 요청을 받은경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부를 결정하게 됩니다
    • CSRF공격을 막는데 효과적이다
    • Lax :Cross-Origin 요청이면 'GET' 메소드에 대해서만 쿠키를 전송할 수 있습니다.
    • Strict : Cross-Origin이 아닌 same-site(요청을 보낸 Origin과 서버의 도메인이 같은 경우) 인 경우에만 쿠키를 전송 할 수 있습니다.
    • None: 항상 쿠키를 보내줄 수 있습니다. 다만 쿠키 옵션 중 Secure 옵션이 필요합니다.

3. node에서 쿠키설정 코드


chapter: SessionId-based Authentication


session? sessionId? SessionId-based Authentication ?

  • 사용자가 브라우저를 닫아 서버와의 연결을 끝내는 시점까지를 session이라고 한다 또 사용자가 인증에 성공한 상태도 세션이라고 부릅니다
  • 유저가 로그인시 세션 Db에는 해당유저의 세션을 생성하고
    seesionId를 부여한다.
    이 seesionId는 쿠키를통해 전달되며 이를 통해 로그인상태를 기억 할 수 있다
    • 쿠키에는 오직 SessionId만 저장되고 데이터는 세션 DB에 저장된다
  • 사용자에 대한 정보를 서버에 두기 때문에 보안에 좋지만, 사용자가 많아질수록 서버 메모리를 많이 차지하게 됩니다.

세션기반 인증 (Session-based Authentication)

1.로그인

  • 인증에 따라 리소스의 접근 권한(Authorization)이 달라집니다
  • 인증(Authentication)된유저에게 권한을 인가(authorization)하려면 서버는 사용자가 인증에 성공했음을 알고 있어야하고, 클라이언트는 인증 성공을 증명할 수단을 갖고 있어야 합니다.
  • 클라이언트가 로그인정보를 담은 Request를 보내면, 해당 서버의 엔진이 클라이언트에게 유일한 ID를 부여하는 데 이것이 세션 ID라하고 이는 쿠키를통해 다시 클라이언트에게 전달되며 또 매요청마다 쿠키에 담겨져 함께 보내진다
  • 만약 클라이언트의 요청의 쿠키에 세션ID가 있다면,이를통해 클라이언트를 구분하고 해당 클라이언트를 인증되었다고 판단한다.
  • 정리하면 세션 아이디가 담긴 쿠키를 주고받으며 로그인을 유지하고, 실제 데이터의 저장은 세션DB에서 이루어 집니다.


2.로그아웃

  • 로그아웃은 다음 두 가지 작업을 해야 합니다.
    • 서버의 세션 정보를 삭제해야 합니다.
    • 클라이언트의 쿠키를 갱신해야 합니다
  • 서버가 클라이언트의 쿠키를 임의로 삭제할 수는 없습니다. 대신, set-cookie로 세션 아이디의 키값을 무효한 값으로 갱신해야 합니다.

chapter: Token-based Authentication


JWT

0. token? jwt?

  • token은 이상하게 생긴 긴~~텍스트 덩어리이다.
  • jwt는 대표적인 토큰중 하나이다

1. JWT의 종류

  • Access Token

    • access token은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근할 수 있는 권한부여에 사용합니다. 클라이언트가 처음 인증을 받게 될 때(로그인 시), access, refresh token 두 가지를 다 받지만, 실제로 권한을 얻는 데 사용하는 토큰은 access token입니다.
    • access token에는 비교적 짧은 유효 기간 을 주어 탈취되더라도 오랫동안 사용할 수 없도록 하는 것이 좋습니다.
  • Refresh Token

    • Access token의 유효기간이 만료된다면 refresh token을 사용하여 새로운 access token을 발급받습니다. 이때문에, 유저는 다시 로그인할 필요가 없습니다.
    • 유효기간이 긴 refresh token 마저 악의적인 유저가 얻어낸다면 큰 문제가 될 것입니다
      그렇기 때문에 유저의 편의보다 정보를 지키는 것이 더 중요한 웹사이트들은 refresh token을 사용하지 않는 곳이 많습니다.

3.JWT의 구조


  • Header
    어떤 종류의 토큰인지,어떤 알고리즘으로 sign(암호화)할지가 적혀있습니다.
{
  "alg": "HS256",
  "typ": "JWT"
}

JSON 객체를 base64 방식으로 인코딩하면 JWT의 첫 번째 부분이 완성됩니다.

  • Payload
    Payload에는 정보가 담겨 있습니다
    어떤 정보에 접근 가능한지에 대한 권한을 담을 수도 있고, 사용자의 유저 이름 등 필요한 데이터는 이곳에 담아 Sign 시킵니다. Payload 에는 민감한 정보(비밀번호같은)는 되도록 담지 않는 것이 좋습니다.
{
  "sub": "someInformation",
  "name": "phillip",
  "iat": 151623391//iat: 토큰이 발급된 시간 (issued at)
// 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단 할 수 있습니다.
}

JSON 객체를 base64로 인코딩하면 JWT의 두 번째 블록이 완성됩니다.

  • Signature
    Hather,Payload를 base64인코딩한 값과 salt값의 조합으로 함호화된값임
    비밀키를 알지못하면 해독하는데 매우 힘듬
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);

Token-based Authentication(토큰기반인증)

0. 개요

  • token방식은 유저를 인증하는데 필요한 정보를 토큰에 담고 클라이언트에게 전달한다. 요청과 동시에 토큰이 서버에게 전달되고 서버는 해당 토큰이 유효한지만 체크한다
  • 매요청마다 데이터베이스를 접근하는 세션기반 방식에비해 서버측에 부담을 덜수있다
  • 유저를 인증하는데 필요한 정보는 민감한 정보이기때문에 클라이언트측에 저장하는게 기본적으로는 위험하다. 해서 토큰을 암호화 되어있다(비밀번호 정보도 포함하지 않음)

1.토큰기반 인증 절차

  • 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보낸다.
  • 아이디/비밀번호가 일치하는지 확인하고, 클라이언트에게 보낼 Signature 된 토큰을 생성한다.
    • 비밀번호 정보는 뺴도록 한다 가급적
    • 이때 access/refresh 토큰을 모두 생성한다.
    • 두 종류의 토큰이 같은 정보를 담을 필요는 없다
  • 토큰을 클라이언트에게 보내주면, 클라이언트는 토큰을 저장한다.(정답은 없다)
    • 서버가 토큰을 쿠키에 담아 보낼 수 있다.
      클라이언트는 쿠키에 접근 불가하다(httpOnly).따로 저장하는 일련의 코드를 작성하지 않아도 된다
    • 서버가 토큰을 바디의 페이로드로보낸다면 클라이언트는 저장하는 위치를 생각 해 볼 필요가 있다. local storage,react의 state 등 다양하다.
  • 클라이언트는 서버에 요청시 토큰을 같이 보낸다
    • cookie 에 저장했다면 자동으로 감
    • 일반적으로 토큰은 요청 헤더의 Authorization 필드에 담아져 보내집니다.
      Authorization: <type> <credentials>
      토큰에는 많은 종류가 있고 서버는 다양한 종류의 토큰을 처리하기 위해 전송받은 type에 따라 토큰을 다르게 처리합니다.
      JWT 혹은 OAuth에 대한 토큰을 사용하는경우 type은 Bearer
  • 서버는 토큰을 해독하여 "아 우리가 발급해 준 토큰이 맞네!"라는 판단이 될 경우, 클라이언트의 요청을 처리한 후 응답을 보내준다.

3. 코드 보기

  • 서버(노드)가 클라에게 토큰을 보낼때


  • 클라(리액트)가 서버에게 토큰을 보낼때
    쿠키가 있다면 자동으로 전달 된다 대신 아래와같은 처리를 해주어야함

0개의 댓글