본문 바로가기

개발인생다반사/TIL(Today i learned)

TIL - 211122 [인증/보안] 기초

Achievement Goals

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

Sprint - HTTPS

1. HTTPS

HTTP + Secure : Hyper Text Transfer Protocol Secure Socket layer

HTTP : 인터넷에서 데이터를 송수신할 수 있는 통신 규약

HTTPS : HTTP 프로토콜 내용을 암호화 (암호화 방법 : 인증서, CA, 비대칭 키 암호화)

 

HTTPS 구현 수단

인증서: 데이터 제공자 신원 보장, 도메인 종속

CA : Certifacate Authority, 공인 인증서 발급 기관

비대칭 키 암호화 : 키 A로 암호화, 키 B로만 복호화 가능

인증서 

- 클라이언트가 가지고 있는 인증서에 있는 도메인과 서버가 보내 준 응답객체(response)에 있는 도메인의 동일 여부 확인

CA

- 각 브라우저는 공인된 CA의 리스트를 가지고 있다.

비대칭 키 암호화

- https 이용하는 서버는 한 쌍의 키 중에서 하나는 비밀로 숨겨두고 다른 하나는 클라이언트에 공개

- 공개키 방식은 많은 클라이언트를 대상으로 많은 연산이 필요한 방식이기 때문에 통신의 초창기에 비밀 키를 생성할 때 주로 사용

 

HTTPS 연결 순서

Hand Shake(서버는 공개키, 인증서 정보 전달) 

==> 비밀키 생성(키 제작용 스트링 전송)

==> 상호 키 검증 --> https 연결 성립

 

2. Learn About HTTPS

HTTPS 프로토콜

HTTP over SSL(TLS), HTTP over Secure이라고 부르기도 함

HTTP 요청을 SSL 혹은 TLS라는 알고리즘을 이용하여 HTTP 통신을 하는 과정에서 내용을 암호화하여 데이터를 전송하는 방법

  • 질문: SSL, TLS는 무엇인가요? SSL과 인증기관(CA)은 어떤 관계가 있나요?
  • 답변: 서버가 보유한 SSL 인증서는 인증기관(CA)이 발행한다
  • SSL(Secure Sockets Layer) : 클라와 서버간의 통신 데이터를 안전하게 보장하는 보안 표준 기술. SSL 3.0 넷스케이프 99년에 발표
  • TLS(Transport Layer Security) : 애플리케이션의 안전한 통신을 위한 프로토콜. 이메일, 웹브라우징, 메세징, 타 프로토콜의 감청을 통한 데이터 변형 방지. 모든 모던 브라우저들은 TSL을 지원하기 위해 서버가 유효한 digital certificate(en-US)를 제공하기를 요구한다.
  • 클라이언트와 서버 둘다 각자의 dc를 제공하면 인증해 줄 수 있다.
  • 디지털 인증서는 가장 일반적으로 인증서의 신뢰성을 증명하는 인증 기관에서 서명합니다.

인증에서 HTTPS 프로토콜을 사용해야만 하는 이유는 HTTP보다 상대적으로 안전한 방법이고,

데이터 제공자의 신원을 보장받을 수 있기 때문이다. (중간자 공격 파악)

 

암호화

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

HTTP는 요청과 응답을 탈취한다면 프로그램을 이용하여 해당 요청으로 전달되는 데이터의 내용을 확인할 수 있다. 그러나 데이터를 암호화하여 전송하는 HTTPS 프로토콜을 사용한다면 데이터가 유출될 가능성이 현저히 저어지게 된다.

 

인증서

HTTPS 프로토콜의 특징은 브라우저가 응답과 함께 전달된 인증서 정보를 확인할 수 있다는 점이다. 브라우저는 인증서에서 해당 인증서를 발급한 CA정보를 확인하고 인증된 CA가 발급한 인증서가 아니라면 화면에 경고차를 띄워 서버와 연결이 안전하지 않다고 설명한다.

 

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

mkcert 패키지를 통해 로컬 환경에서 인증서를 제작하였다.

key와 cert를 쿠키, 세션, 토큰에서 활용한다.

  • 질문: key와 cert의 다른 점은 무엇일까요?
  • 답변: key는 사설키. cert는 표준 인증서. 인증서 안에는 public key가 포함
    $ mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1

옵션으로 추가한 local host, 127.0.0.1(IPv4), ::1(IPv6)에서 사용할 수 있는 인증서 완성 cert.pem, key.pem

cert.pem은 공개키, key.pem은 개인키이므로 커밋 금지

HTTPS 서버 작성

Node.js 환경에서 HTTPS 서버를 작성하기 위해서는 https 내장 모듈을 이용할 수 있다.

아니면 express.js를 이용해 https 서버를 만들 수 도 있다.

 

Node.js https 모듈 이용

mckert로 생성한 인증서 파일들을 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 이용

https.createServer의 두 번째 파라미터에 들어갈 callback 함수를 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);

Chapter - Hashing

DB에 서버의 접속을 위한 개인의 아이디와 비밀번호가 저장되어 있고 서버에 접속할 때마다 비밀번호를 입력하게 되어 있다고 가정.

DB에 저장되어 있는 비밀번호가 해커에 의해 유출되는 경우가 발생하여 추가적인 피해가 나올 수 있음

 

DB에 비밀번호를 저장할 때 암호화해서 저장한다면 위와 같은 문제가 발생할 가능성이 낮아짐

 

알고리즘을 활용한 암호화의 예

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

 

salt : 암호화해야 하는 값에 어떤 '별도의 값'을 추가하여 결과를 변형하는 것

해시된 값과 원래 값을 테이블로 만들어서 암호화된 값을 풀어버릴 수도 있다.(복호화, decoding, 레인 보우 테이블)

레이보우 테이블 : 해시 함수를 사용하여 변환 가능한 모든 해시값을 저장시켜 놓은 표. 해시함수를 이용하여 저장된 비밀번호로부터 원래의 비밀번호를 추출해내는데 사용  

 

이처럼 해시함수를 써서 암호화만 해놓는다면 해시된 결과가 늘 동일하기 때문에 원본값에 임의로 약속된 별도의 문자열을 추가하여 해시 진행. 그래서 기존 해시값과 전혀 다른 해시값이 반횐도어 알고리즘이 노출되더라도 원본값을 보호할 수 있다.

기존 : (암호화 하려는 값) ==> (해시값)
salt 사용 : (암호화 하려는 값) + (salt 용 값) ==> (해시값)

주의사항

: 유저와 패스워드별로 유일한 값

: 사용자 계정 생성시 비밀번호 변경하면 새로운 임의의 salt사용

: 재사용 불가

: DB에 저장 필요

 

Chapter - Cookie

쿠키: 서버가 일방적으로 클라이언트에 전달하는 작은 데이터. 의사를 물어 보지 않고 강제 전달한다.

쿠키를 이용하는 것은 1) 단순히 서버에서 클라이언틍 쿠키를 전송하는 것

2) 클라이언트에서 서버로 쿠키를 전송하는 포함

그래서 서버가 웹 브라우저에 정보를 저장하고 불러올 수 있는 수단

사용자 선호, 테마 등 장시간 보존해야 하는 정보 저장에 적합

 

하지만 데이터를 저장한 이후 아무 때나 데이터를 가져올 수 없다. 데이터를 저장한 이후 특정 조건들이 만족하는 경우에만

다시 가져올 수 있다.

도메인: www.google.com과 같은 서버에 접속할 수 있는 이름. 쿠키 옵션에서 도메인은 포트, 서브 도메인, 세부 경로는 미포함

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

 

Path : Path옵션의 특징은 설정된 Path를 전부 만족하는 경우 요청하는 Path가 추가로 더 존재하더라도 쿠키를 서버에 전송할 수 있다.

 

MAxAge or Expires : MaxAge는 앞으로 몇 초 동안 쿠키가 유효한지 설정하는 옵션. Expires는 언제까지 유효한지 Date 지정.

시간은 클라이언트가 기준이며, 지정된 시간, 날짜를 초과하게 되면 쿠키는 자동으로 파괴

하지만 두 옵션이 모두 지정되지 않은 경우에는 브라우저의 탭을 닫아야만 쿠키가 제거

 

HttpOnly : 자바스크립트에서 부라우저의 쿠키에 접근 여부 결정. 해당 옵션이 true인 경우, 자바스크립트에서 쿠키에 접근 불가.

명시되지 않은 경우 기본으로 false. 이 옵션이 false인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 XSS 공격에 취약

 

SamSite : Cross-origin 요청을 받은 경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부 결정

옵션에 따른 서버의 쿠키 전송 여부

Lax : Cross-origin 요청이면 GET 메소드에 대해서면 쿠키 전송 가능

Strict : Cross-origin이 아닌 same-site인 경우에만 쿠키 전송 가능

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

 

"위의 옵션들을 지정한 다음 서버에서 클라이언트로 쿠키를 처음 전송하게 된다면 헤더에 Set-Cookie라는 속성에 쿠키를 담아 전송"

이후 클라이언트 혹은 서버에서 쿠키를 전송해야 한다면 클라이언트는 헤더에 Cookie라는 속성에 쿠키를 담아 서버에 쿠기 전송

 

https에서 서버는 클라이언트를 식별할 수 없다(무상태성, stateless). 쿠키에 담기 인증정보를 가지고 서버와 클라이언트가 상호 전송하여 https의 무상태성을 stateful하게 유지할 수 있다. 히자만 쿠키는 오랜 시간 유지되기 어렵고, JS를 통한 쿠키 접근이 가능하기 때문에 쿠키에 민감한 정보를 담는 것은 위험. 쿠키에 만일 인증정보를 담고 그것이 탈취되어서 서버의 리소스 접근이 가능하게 된다.

 

쿠키와 세션 방식의 인증에 대한 참고자료

지훈님의 블로그

https://cjh5414.github.io/cookie-and-session/

Chapter - Seesion

세선 기반 인증

로그인

Session : 사용자가 서버에서 인증된 사용자라고 인증에 성공한 상태를 세션이라고 한다.

서버는 일종의 저장소에 세션을 저장한다. 주로 in-memory(자바스크립트 객체), 세션 스토어(redis 등과 같은 트랜잭션이 빠른 DB)

 

세션이 만들어지면 각 세션을 구분할 수 있는 세션 아이디로 만들어지는데 보통 클라이언트에서 세션 성공을 증명할 수단으로 서버는 클라이언트에 세션 아이디를 전달한다.(쿠키에 세션 아이디를 담아서 전송)

로그인 상태에서 쿠키를 통해 유효한 세션 아이디 서버에 전달 -> 세션 스토에 해당 세션 존재 확인 => 서버는 리소 접근 가능하다고 판단

그러나 쿠키에 세션 아이디 정보가 없는 경우, 서버는 해당 요청이 인증되지 않았음을 알려줌

로그인

데이터를 서버에 저장하고 쿠키에는 그 데이터에 대한 ID를 암호화된 상태로 저장

Session도 결국은 Cookie에 유저의 정보를 담는다.

- 서버가 세션_id를 암호화하고 쿠키를 통해서(Set-Cookie 메소드) 암호화된 세션_id만 클라이언트에 반환

- Set-Cookie 이후 저장된 세션 아이디로 필요한 작업 수행

- 쿠키를 통한 인증방법과 비교를 한다면 쿠키는 때에 따라 외부의 서버에서 쿠키 전송을 요청할 수 있고, 유저가 삭제하지 않고 유효기간도 정해지지 않는다면 영원히 존재할 수 있음. 그래서 인증 정보를 담아 보관하기에 좋지 않음 

로그아웃

공공PC에서 로그아웃해야 하는 이유: 쿠키는 세션 아디를 가지고 있고 서버는 해당 요청이 인증된 사용자의 요청이라고 판단함

로그아웃을 위한 작업

  • 서버의 세션 정보 삭제
  • 클라이언트 쿠키 갱신

서버가 클라이언트 쿠키를 임의로 삭제할 수 없음. 대신 set-cookie로 세션 아이디 값을 무효한 값으로 갱신

 

express-session

express-session : 세션을 대신 관리해주는 express 모듈

express-session은 세션을 위한 미들웨어로 'Express'에서 세션을 다룰 수 있는 공간을 쉽게 만들어 준다.

request.session이 바로 세션 객체이며 세션 객체에 세션 데이터를 저장하거나 불러오기 위해 사용

GitHub: express-session

Chapter 3 - CSRF

Web application Security

- 개발자들이 웹사이트, 모바일 어플, 웹 API 등을 만들 때에 해커들의 공격을 막기 위해 보안(security)은 필수 사항

- 공격 : SQL injection, XSS, CSRF

 

CSRF (Cross Site Request Forgery)

- 주소가 다른 사이트에서 유저가 보내는 요청을 조작(forgery)하는 것 ex) 이메일에 첨부된 링크를 누르면 은행계좌의 돈이 빠져나감

 

CSRF 공격을 하기 위한 조건

- 쿠기를 사용한 로그인 : 유저가 로그인 했을 때, 쿠키로 어떤 유저인지 알 수 있어야 함

- 예측할 수 있는 요청/parameter를 가지고 있어야 함 : request에 해커가 모를 수 있는 정보가 담겨있으면 안됨

 

GET 요청으로 CSRF 공격하기

계좌이체에 사용되는 GET 요청

https://wooribank.com/transfer?account_number=username&amount=20000

 

해커의 GET 요청

https://wooribank.com/transfer?account_number=해커계좌번호&amount=20000

 

POST 요청으로 CSRF 공격하기

비밀번호 변경에 사용되는 POST 요청

POST https://wooribank.com/password/change 

                        body:

     { password: user's-new-password }

 

막는 방법

- CSRF 토큰 사용하기 : 서버측에서 CSRF 공격을 보호하기 위한 문자열을 유저의 브라우저와 웹 앱에만 제공

- Same-site cookie 사용하기 : 같은 도메인에서만 세션/쿠기를 사용할 수 있다

 

Chapter 4 - Token-based Authentication

토큰 기반 인증

토큰 기반 인증 왜, 그리고 언제 쓸까요?

세션 기반 인증은 DB에 유저 정보를 담는 방식. 데이터 요청시 마다 DB를 확인하는 불편.

이러한 불편을 덜어내기 위해 토큰 기반 인증. JWT(JSON Web Token)

 

클라이언트에서 인증 정보 보관하기

일반적으로 클라이언트는 XSS, CSRF 공격에 노출이 될 위험이 있기 때문에 민감한 정보를 담아서 클라이언트에 보내서는 안된다고 알고 있다. 그래서 Session기반의 인증방식에서도 쿠키에는 비밀번호 같은 민감한 정보는 담지 않는다. 그런데 토큰 기반 인증 방식은 민감한 정보인 토큰을 클라이언트에 보낸다.

 

그런데 보안성을 강화하기 위해 토큰은 유저 정보를 암호화한 상태로 담아서 클라이언트에 보낸다.

 

JWT 종류

1. Access Token

보호된 정보들(유저의 이메일, 연락처, 사진)에게 접근할 수 있는 권한 부여에 사용.

클라이언트가 처음 인증을 받을 때(로그인시) access, refresh token 두가지를 모두 받지만 실제로 권한을 얻는데 사용하는 토큰은

access token입니다. (* 권한 부여 받는데 access Token 만 있으면 된다. access Token은 유효기간이 짧다)

 

2.Refresh Token

Access Token의 유효기간이 만료되면 refresh Token. (* 유저는 다시 로그인할 필요 없음)

유저의 편의보다 정보를 지키는 것이 더 중요한 웹사이트들은 refresh Token을 사용하지 않는다.

 

JWT 구조

(1) header : 토큰 종류(JWT), 암호화 알고리즘 종류. 이 JSON 객체를 base64 방식으로 인코딩하면 Header 완성

(2) payload : 정보 접근 권한, 유저 이름. 민감한 정보는 되도록이면 담지 않는다.

이 JSON 객체를 base64 방식으로 인코딩하면 payload 완성

(3) signature : 원하는 비밀키(암호화에 추가할 salt)를 사용하여 암호화.

base64 인코딩을 한 값은 누구나 쉽게 디코딩할 수 있지만 서버에서 사용하고 있는 비밀키를 보유한게 아니라면

해독해 내는데 엄청난 시간과 노력이 필요하다.

 

JWT  사용 예시

  1. Gmail 인증서버에 로그인 정보(아이디, 비밀번호)를 제공한다
  2. 성공적으로 인증 시 JWT를 발급받는다
  3. A 앱은 JWT를 사용해 해당 유저의 Gmail 이메일을 읽거나 사용할 수 있다

토큰기반 인증 절차

  1. 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보낸다.
  2. 아이디/비밀번호가 일치하는지 확인하고, 클라이언트에게 보낼 암호화된 토큰을 생성한다.
    • access/refresh 토큰을 모두 생성한다.
      • 토큰에 담길 정보(payload)는 유저를 식별할 정보, 권한이 부여된 카테고리(사진, 연락처, 기타 등등)이 될 수 있다.
      • 두 종류의 토큰이 같은 정보를 담을 필요는 없다 (이 스프린트에서는 같은 정보를 담아줍시다).
  3. 토큰을 클라이언트에게 보내주면, 클라이언트는 토큰을 저장한다.
    • 저장하는 위치는 local storage, cookie, react의 state 등 다양하다.
  4. 클라이언트가 HTTP 헤더(authorization 헤더)에 토큰을 담아 보낸다.
  5. 서버는 토큰을 해독하여 "아 우리가 발급해 준 토큰이 맞네!"라는 판단이 될 경우, 클라이언트의 요청을 처리한 후 응답을 보내준다.

토큰 기반 인증의 장점

1. 무상태성 & 확장성

2. 안정성

3. 어디서나 생성 가능

4. 권한 부여에 용이

 

참고자료

Myungho 님의 블로그 https://velog.io/@cada/토근-기반-인증에서-bearer는-무엇일까

Chapter 5 - OAuth 2.0

OAuth 란?

OAuth 2.0은 인증을 위한 표준 프로토콜의 한 종류

보안된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공(Authorization)하는 프로세스를 단순화하는 프로토콜

OAuth를 활용한다면 자주 사용하는 서비스들(google, github, facebook)의 ID와 PASSWORD만으로 소셜 로그인을 할 수 있다.

 

OAuth에서 꼭 알아야 할 용어

소셜 로그인 로직 플로우

Grant type 종류

Grant type은 client가 액세스 토큰을 얻는 방법