HTTPS - 인증 / 보안

개발 공부 기록·2021년 8월 7일
0

Network

목록 보기
5/9
post-thumbnail

HTTPS

HTTPSHyper Text Transfer Protocol Secure Socket layer의 약자

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

HTTPS(HTTP + Secure): HTTP 프로토콜 내용을 암호화

인증에서 HTTPS 프로토콜을 사용해야만 하는 이유는 HTTP보다 상대적으로 안전한 방법이고, 데이터 제공자의 신원을 보장받을 수 있기 때문

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

  • 웹사이트의 무결성을 보호 => 전송되는 웹사이트 정보가 변조되지 않음
  • 사용자의 개인 정보를 보호 (기밀성)

암호화

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

비대칭 키 암호화: 키 A로 암호화 => 키 B로 복호화

인증서

HTTPS 프로토콜의 또 다른 특징 중 하나는 브라우저가 응답과 함께 전달된 인증서 정보를 확인할 수 있다는 점

브라우저는 인증서에서 해당 인증서를 발급한 CA 정보를 확인하고 인증된 CA가 발급한 인증서가 아니라면 화면에 경고창을 띄워 서버와 연결이 안전하지 않다는 화면을 보여줌

브라우저는 인증서의 도메인과 데이터를 제공한 제공자의 도메인을 비교할 수 있기 때문에 인증서의 도메인 정보와 데이터 제공자의 도메인 정보가 다른 '중간자 공격'을 감지하여 보안 위협으로부터 사용자 및 사용자의 데이터를 보호해줌

경고를 직접 보여줌으로써 브라우저들은 인증된 CA가 발급한 인증서를 이용하여 데이터를 제공하는 안전한 서버를 사용할 수 있게 사용자를 유도함

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

CA(Certificate Authority): 공인 인증서 발급 기관

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

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

설치 (Ubuntu)

$ sudo apt install libnss3-tools
$ wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64
$ chmod +x mkcert
$ sudo cp mkcert /usr/local/bin/

인증서 생성

$ mkcert -install

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

인증서는 공개키, 그리고 인증기관의 서명을 포함하고 있으므로 공개되어도 상관이 없지만, key.pem의 경우 개인 키이므로 git에 커밋하지 않고, 암호처럼 다루어야 한다

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'),
    },
    (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

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

  1. 모든 값에 대해 해시 값을 계산하는데 오래걸리지 않아야 한다.

  2. 최대한 해시 값을 피해야 하며 모든 값은 고유한 해시 값을 가진다.

  3. 아주 작은 단위의 변경이라도 완전히 다른 해시 값을 가져야 한다.

  • 대칭 키 암호화
    해시 => 암호화(해시 함수) / 복호화
    ex) SHA256

Salting

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

  1. 암호화만 해놓는다면 해시된 결과가 늘 동일
    해시된 값과 원래 값을 테이블(레인보우 테이블)로 만들어서 decoding 해버리는 경우도 생긴다.

  2. 원본 값에 임의로 약속된 별도의 문자열을 추가하여 해시를 진행한다면 기존 해시 값과 전혀 다른 해시 값이 반환되어 알고리즘이 노출되더라도 원본 값을 보호할 수 있도록 하는 안전 장치

  3. 기존(hasing): 암호화 하려는 값 => hash 값
    salting: 암호화 하려는 값 + salt용 값 => hash + salt 값

salting 주의점

  1. salt는 유저와 패스워드 별로 유일한 값을 가져야함

  2. 사용자 계정을 생성할 때 와 비번번호를 변경할 때마다 새로운 임의의 salt를 사용해서 해싱해야함

  3. salt는 절대 재사용 금지

  4. salt는 DB의 유저 테이블에 같이 저장되어야 함

  • 비대칭 키 암호화
    공개 키: 클라이언트가 갖고 있음 ex) cert.pem
    개인 키: 서버에서 주는 키 ex) key.pem

Cookie

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

해당 도메인에 대해 쿠키가 존재하면 웹 브라우저는 도메인에게 http 요청 시 쿠키를 함께 전달

쿠키: HTTP 무상태성을 보완 (HTTP의 상태를 저장)

ex) 사용자 선호, 테마, 로그인 상태 유지 등 장기간 보존해야하는 정보 저장에 적합

쿠키 옵션

1. Domain

서버와 요청의 도메인이 일치하는 경우 쿠키 전송

2. Path

서버와 요청의 세부 경로가 일치하는 경우 쿠키 전송

3. MaxAge or Expires: 쿠키의 유효기간 설정

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

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

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

4. HttpOnly: 자바스크립트에서 쿠키 접근 가능 여부 결정

만약 해당 옵션이 true로 설정된 경우, 자바스크립트에서는 쿠키에 접근이 불가
명시되지 않는 경우 기본으로 false
false인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 XSS 공격에 취약

5. Secure

HTTPS 프로토콜에서만 쿠키 전송 여부 결정

6. SameSite: 같은 사이트에서만 쿠키를 사용할 수 있게 하는 설정

CORS 요청의 경우 옵션에 및 메소드에 따라 쿠키 전송 여부 결정

사용 가능한 옵션

  • Lax: GET 메소드 요청만 쿠키 전송 가능
  • Strict: 쿠키 전송 불가 => same-site 인 경우(요청을 보낸 Origin과 서버의 도메인이 같은 경우)에만 쿠키를 전송 가능
  • None: 모든 메소드 요청에 대해 쿠키 전송 가능 (Secure 쿠키 옵션이 필요)

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

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

서버가 클라이언트에게 일발적으로 쿠키 전달
set-cookie: key=value; session_id = 암호화된 로그인 정보

로그인후 클라이언트에 있는 쿠키 저장소에서 서버로 유저정보 요청
cookie: session_id=암호화된 로그인 정보

withCredentials: AJAX 요청시 쿠키를 같이 보낼건지 결정하는 옵션

쿠키를 사용한 상태 유지

쿠키의 특성을 이용하여 서버는 클라이언트에 인증정보를 담은 쿠키를 전송하고, 클라이언트는 전달받은 쿠키를 요청과 같이 전송하여 Stateless 한 인터넷 연결을 Stateful 하게 유지 가능

하지만 기본적으로는 쿠키는 오랜 시간 동안 유지될 수 있고, 자바스크립트를 이용해서 쿠키에 접근할 수 있기 때문에 쿠키에 민감한 정보를 담는 것은 위험

이런 인증정보를 탈취하여 서버에 요청을 보낸다면 서버는 누가 요청을 보낸 건지 상관하지 않고 인증된 유저의 요청으로 취급하기 때문에, 개인 유저 정보 같은 민감한 정보에 접근이 가능

쿠키는 서버가 클라이언트에게 응답을 통해서 일방적으로 주는것 : 헤더 - Set-Cookie

클라이언트가 해당 사이트에 대해서 쿠키가 있으면 요청을 통해 자동적으로 서버에게 보낸다. HTTP 무상태성 극복

서버가 클라이언트의 쿠키를 지우지는 못하고 덮어씌우기만 가능하다.

인증 과정에서 쿠키는 세션 아이디나 토큰 전달 용도로 사용한다.

Session

Session: 서버가 클라이언트에 유일하고 암호화된 ID를 부여

중요 데이터는 서버에서 관리

사용자가 인증에 성공한 상태는 세션

Session의 단점

  1. 사용자가 많으면 서버 메모리에 세션 데이터가 많이 차지하게 되므로 서버의 성능이 저하됨

  2. XSS 공격으로 인해 세션 쿠키가 탈취될 수 있다.

로그인

인증에 따라 리소스의 접근 권한이 달라짐

전제 조건

  • 서버는 사용자가 인증에 성공했음을 알고 있어야함
  • 클라이언트는 인증 성공을 증명할 수단을 갖고 있어야함

웹사이트에서 로그인을 유지하기 위한 수단으로 쿠키를 사용한다. 쿠키에는 서버에서 발급한 세션 아이디를 저장

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

로그아웃

  • 서버의 세션 정보를 삭제
  • 클라이언트의 쿠키를 갱신(set-cookie로 세션 아이디의 키값을 무효한 값으로 갱신)

express-session

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

필요한 경우 세션 아이디를 쿠키에 저장하고, 해당 세션 아이디에 종속되는 고유한 세션 객체를 서버 메모리에 저장

세션 객체는 서로 독립적인 객체이므로 각각 다른 데이터를 저장 가능

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

express-session - GitHub

설명접속 상태 저장 경로장점단점
쿠키쿠키는 그저 http의 stateless한 것을 보완해주는 도구클라이언트 / 서버에 부담을 덜어줌쿠키 그 자체는 인증이 아님
세션접속 사태를 서버가 가진(stateful) 접속 상태와 권환 부여를 위해 세션 아이디를 쿠키로 전송서버가 신뢰할 수 있는 유저인지 서버에서 추가로 확인 가능하나의 서버에서만 접속 상태를 가지므로 분리 사용에 불리

Token

세션 기반 인증: 서버/DB에 유저 정보를 담는 방식
<=> 토큰 기반 인증: 클라이언트에서 유저 정보를 담는 방식

인증 정보 저장 장소

  • 세션: 서버(세션 스토어) + 클라이언트(세션id가 담긴 쿠키)
  • 토큰: 클라이언트(jwt 토큰)

토큰은 유저 정보를 암호화한 상태로 담을 수 있고, 암호화했기 때문에 클라이언트에 담을 수 있다.

대표적인 토큰 기반 인증 JWT (JSON Web Token)

JWT의 종류

1. Access Token

Access token은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근할 수 있는 권한 부여에 사용
권한을 부여 받는데엔 access token

2. Refresh Token

Access token의 유효기간이 만료된다면 refresh token을 사용하여 새로운 access token을 발급

JWT 구조

1. Header

Header는 이것이 어떤 종류의 토큰인지(지금의 경우엔 JWT), 어떤 알고리즘으로 sign(암호화) 할지가 적혀있다.

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

2. Payload

어떤 정보에 접근 가능한지에 대한 권한을 담을 수도 있고, 사용자의 유저 이름 등 필요한 데이터는 이곳에 담아 암호화 시킴

암호화(헤더에서 정의한)가 될 정보지만, 민감한 정보는 되도록 담지 않는 것이 좋다.(비밀번호는 담지 않는것이 일반적)

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

3. Signature

base64로 인코딩된 첫번째, 그리고 두번째 부분이 완성 되었다면, 원하는 비밀 키(암호화에 추가할 salt)를 사용하여 암호화

HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);

HMAC SHA256 알고리즘(암호화 방법중 하나)을 사용한 signature

JWT 공식 문서

토큰기반 인증 절차

1. 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보낸다.

2. 아이디/비밀번호가 일치하는지 확인하고, 클라이언트에게 보낼 암호화된 토큰을 생성한다.

access/refresh 토큰을 모두 생성한다.

토큰에 담길 정보(payload)는 유저를 식별할 정보, 권한이 부여된 카테고리(사진, 연락처, 기타등등)이 될 수 있다.

두 종류의 토큰이 같은 정보를 담을 필요는 없다.

3. 토큰을 클라이언트에게 보내주면, 클라이언트는 토큰을 저장한다.

저장하는 위치는 local storage, cookie, react의 state 등 다양하다.

4. 클라이언트가 HTTP 헤더(authorization 헤더)에 토큰을 담아 보낸다.

bearer authentication을 이용한다.

5. 서버는 토큰을 해독하여 발급해준 토큰이 맞는지 판단이 될 경우, 클라이언트의 요청을 처리한 후 응답을 보내준다.

토큰 기반의 장점

1. Statelessness & Scalability (무상태성 & 확장성)

서버는 클라이언트에 대한 정보를 저장할 필요 없다 (토큰 해독이 되는지만 판단)
클라이언트는 새로운 요청을 보낼때마다 토큰을 헤더에 포함시킴
서버를 여러개 가지고 있는 서비스라면 더더욱 빛을 발휘(같은 토큰으로 여러 서버에서 인증 가능)

2.안전하다

암호화 한 토큰을 사용하고, 암호화 키를 노출 할 필요가 없기 때문에 안전

3. 어디서나 생성 가능하다

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

4. 권한 부여에 용이하다

토큰의 payload(내용물) 안에 어떤 정보에 접근 가능한지 정할 수 있다
ex) 서비스의 사진과 연락처 사용권한만 부여

OAuth 2.0

전통적으로 직접 작성한 서버에서 인증을 처리해주는 것과는 달리, OAuth는 인증을 중개해주는 메커니즘

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

이미 사용자 정보를 가지고 있는 웹 서비스(GitHub, google, facebook 등)에서 사용자의 인증을 대신해주고, 접근 권한에 대한 토큰을 발급한 후, 이를 이용해 내 서버에서 인증이 가능

OAuth에서 꼭 알아야 할 용어

  • Resource Owner : 액세스 중인 리소스의 유저
  • Client : Resource owner를 대신하여 보호된 리소스에 액세스하는 응용프로그램
  • Resource server : client의 요청을 수락하고 응답할 수 있는 서버
  • Authorization server : Resource server가 액세스 토큰을 발급받는 서버 (클라이언트 및 리소스 소유자를 성공적으로 인증한 후 액세스 토큰을 발급하는 서버)
  • Authorization grant : 클라이언트가 액세스 토큰을 얻을 때 사용하는 자격 증명의 유형
  • Authorization code : access token을 발급받기 전에 필요한 code
  • Access token : 보호된 리소스에 액세스하는 데 사용되는 credentials(Authorization code와 client secret을 이용해 받아옴)

  • Scope : scope는 토큰의 권한을 정의( 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위)

Grant Type: Client가 액세스 토큰을 얻는 방법

1. Authorization Code Grant Type

액세스 토큰을 받아오기 위해서 먼저 Authorization Code를 받아 액세스 토큰과 교환하는 방법

클라이언트에서 Authorization Code 받고 서버에서 client-secret을 포함한 액세스 토큰 요청을 진행한다. ( 보안성 강화 목적 )

2. Refresh Token Grant Type

일정 기간 유효 시간이 지나서 만료된 액세스 토큰을 편리하게 다시 받아오기 위해 사용하는 방법

CSRF

Wep application Security

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

여러 가지 공격들

  • SQL injection
  • XSS
  • CSRF(Cross Site Request Forgery)
    다른 사이트에서 유저가 보내는 요청을 조작하는 것
    다른 사이트이기 때문에 응답에 직접 접근 불가라 해커가 직접 데이터에 접근 불가능하다.

CSRF 공격을 하기 위한 조건

  • 쿠키를 사용한 로그인
  • 예측할 수 있는 요청/parameter를 가지고 있어야함

CSRF 예방

  • CSRF 토큰 사용 => 서버측에서 CSRF 공격에 보호하기 위한 문자열을 유저의 브라우저와 웹앱에만 제공
  • Same-site cookie 사용 => 같은 도메인에서만 세션/쿠키를 사용할 수 있다.
profile
둔필승총(鈍筆勝聰) - 기억보다는 기록을

0개의 댓글