HTTPS
는 Hyper 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): 공인 인증서 발급 기관
mkcert
라는 프로그램을 이용해서 로컬 환경(내 컴퓨터)에서 신뢰할 수 있는 인증서를 만들 수 있다.
$ 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에 커밋하지 않고, 암호처럼 다루어야 한다
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);
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: 어떠한 문자열에 임의의 연산을 적용하여 다른 문자열로 변환하는 것
모든 값에 대해 해시 값을 계산하는데 오래걸리지 않아야 한다.
최대한 해시 값을 피해야 하며 모든 값은 고유한 해시 값을 가진다.
아주 작은 단위의 변경이라도 완전히 다른 해시 값을 가져야 한다.
SHA256
Salting: 암호화해야 하는 값에 어떤 별도의 값을 추가하여 결과를 변형하는 것
암호화만 해놓는다면 해시된 결과가 늘 동일
해시된 값과 원래 값을 테이블(레인보우 테이블)로 만들어서 decoding 해버리는 경우도 생긴다.
원본 값에 임의로 약속된 별도의 문자열을 추가하여 해시를 진행한다면 기존 해시 값과 전혀 다른 해시 값이 반환되어 알고리즘이 노출되더라도 원본 값을 보호할 수 있도록 하는 안전 장치
기존(hasing): 암호화 하려는 값 => hash 값
salting: 암호화 하려는 값 + salt용 값 => hash + salt 값
salt는 유저와 패스워드 별로 유일한 값을 가져야함
사용자 계정을 생성할 때 와 비번번호를 변경할 때마다 새로운 임의의 salt를 사용해서 해싱해야함
salt는 절대 재사용 금지
salt는 DB의 유저 테이블에 같이 저장되어야 함
cert.pem
key.pem
쿠키: 서버가 웹 브라우저에 정보를 저장하고 불러올 수 있는 수단
해당 도메인에 대해 쿠키가 존재하면 웹 브라우저는 도메인에게 http 요청 시 쿠키를 함께 전달
쿠키: HTTP 무상태성을 보완 (HTTP의 상태를 저장)
ex) 사용자 선호, 테마, 로그인 상태 유지 등 장기간 보존해야하는 정보 저장에 적합
서버와 요청의 도메인이 일치하는 경우 쿠키 전송
서버와 요청의 세부 경로가 일치하는 경우 쿠키 전송
MaxAge
는 앞으로 몇 초 동안 쿠키가 유효한지 설정하는 옵션
Expires
은 MaxAge와 비슷하지만 언제까지 유효한지 Date를 지정하고 이 때 클라이언트의 시간을 기준으로 한다.
두 옵션이 모두 지정되지 않는 경우에는 브라우저의 탭을 닫아야만 쿠키가 제거 가능
만약 해당 옵션이 true
로 설정된 경우, 자바스크립트에서는 쿠키에 접근이 불가
명시되지 않는 경우 기본으로 false
false
인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 XSS
공격에 취약
HTTPS 프로토콜에서만 쿠키 전송 여부 결정
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: 서버가 클라이언트에 유일하고 암호화된 ID를 부여
중요 데이터는 서버에서 관리
사용자가 인증에 성공한 상태는 세션
사용자가 많으면 서버 메모리에 세션 데이터가 많이 차지하게 되므로 서버의 성능이 저하됨
XSS 공격으로 인해 세션 쿠키가 탈취될 수 있다.
인증에 따라 리소스의 접근 권한이 달라짐
웹사이트에서 로그인을 유지하기 위한 수단으로 쿠키를 사용한다. 쿠키에는 서버에서 발급한 세션 아이디를 저장
쿠키에 세션 아이디 정보가 없는 경우, 서버는 해당 요청이 인증되지 않았음을 알려줌
set-cookie
로 세션 아이디의 키값을 무효한 값으로 갱신)express-session
은 세션을 위한 미들웨어로, 'Express.js'에서 세션을 다룰 수 있는 공간을 보다 쉽게 만들어줌
필요한 경우 세션 아이디를 쿠키에 저장하고, 해당 세션 아이디에 종속되는 고유한 세션 객체를 서버 메모리에 저장
세션 객체는 서로 독립적인 객체이므로 각각 다른 데이터를 저장 가능
req.session
이 바로 세션 객체이며 req.session
은 세션 객체에 세션 데이터를 저장하거나 불러오기 위해 사용
설명 | 접속 상태 저장 경로 | 장점 | 단점 |
---|---|---|---|
쿠키 | 쿠키는 그저 http의 stateless한 것을 보완해주는 도구 | 클라이언트 / 서버에 부담을 덜어줌 | 쿠키 그 자체는 인증이 아님 |
세션 | 접속 사태를 서버가 가진(stateful) 접속 상태와 권환 부여를 위해 세션 아이디를 쿠키로 전송 | 서버가 신뢰할 수 있는 유저인지 서버에서 추가로 확인 가능 | 하나의 서버에서만 접속 상태를 가지므로 분리 사용에 불리 |
세션 기반 인증: 서버/DB에 유저 정보를 담는 방식
<=> 토큰 기반 인증: 클라이언트에서 유저 정보를 담는 방식
인증 정보 저장 장소
토큰은 유저 정보를 암호화한 상태로 담을 수 있고, 암호화했기 때문에 클라이언트에 담을 수 있다.
Access token은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근할 수 있는 권한 부여에 사용
권한을 부여 받는데엔 access token
Access token의 유효기간이 만료된다면 refresh token을 사용하여 새로운 access token을 발급
Header는 이것이 어떤 종류의 토큰인지(지금의 경우엔 JWT), 어떤 알고리즘으로 sign(암호화) 할지가 적혀있다.
이 JSON 객체를 base64
방식으로 인코딩하면 JWT의 첫 번째 부분이 완성됨
어떤 정보에 접근 가능한지에 대한 권한을 담을 수도 있고, 사용자의 유저 이름 등 필요한 데이터는 이곳에 담아 암호화 시킴
암호화(헤더에서 정의한)가 될 정보지만, 민감한 정보는 되도록 담지 않는 것이 좋다.(비밀번호는 담지 않는것이 일반적)
JSON 객체를 base64
로 인코딩하면 JWT의 두 번째 블록이 완성됨
base64로 인코딩된 첫번째, 그리고 두번째 부분이 완성 되었다면, 원하는 비밀 키(암호화에 추가할 salt)를 사용하여 암호화
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
HMAC SHA256 알고리즘(암호화 방법중 하나)을 사용한 signature
access/refresh 토큰을 모두 생성한다.
토큰에 담길 정보(payload)는 유저를 식별할 정보, 권한이 부여된 카테고리(사진, 연락처, 기타등등)이 될 수 있다.
두 종류의 토큰이 같은 정보를 담을 필요는 없다.
저장하는 위치는 local storage, cookie, react의 state 등 다양하다.
bearer authentication을 이용한다.
서버는 클라이언트에 대한 정보를 저장할 필요 없다 (토큰 해독이 되는지만 판단)
클라이언트는 새로운 요청을 보낼때마다 토큰을 헤더에 포함시킴
서버를 여러개 가지고 있는 서비스라면 더더욱 빛을 발휘(같은 토큰으로 여러 서버에서 인증 가능)
암호화 한 토큰을 사용하고, 암호화 키를 노출 할 필요가 없기 때문에 안전
토큰을 확인하는 서버가 토큰을 만들어야 하는 법이 없다
토큰 생성용 서버를 만들거나, 다른 회사에서 토큰관련 작업을 맡기는 것 등 다양한 활용이 가능
토큰의 payload(내용물) 안에 어떤 정보에 접근 가능한지 정할 수 있다
ex) 서비스의 사진과 연락처 사용권한만 부여
전통적으로 직접 작성한 서버에서 인증을 처리해주는 것과는 달리, OAuth는 인증을 중개해주는 메커니즘
보안된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜
이미 사용자 정보를 가지고 있는 웹 서비스(GitHub, google, facebook 등)에서 사용자의 인증을 대신해주고, 접근 권한에 대한 토큰을 발급한 후, 이를 이용해 내 서버에서 인증이 가능
Access token : 보호된 리소스에 액세스하는 데 사용되는 credentials(Authorization code와 client secret을 이용해 받아옴)
Scope : scope는 토큰의 권한을 정의( 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위)
액세스 토큰을 받아오기 위해서 먼저 Authorization Code를 받아 액세스 토큰과 교환하는 방법
클라이언트에서 Authorization Code 받고 서버에서 client-secret을 포함한 액세스 토큰 요청을 진행한다. ( 보안성 강화 목적 )
일정 기간 유효 시간이 지나서 만료된 액세스 토큰을 편리하게 다시 받아오기 위해 사용하는 방법
Wep application Security
개발자들이 웹 사이트, 모바일 어플, 웹 API 등을 만들 때에 해커들의 공격을 막기 위해서 보안은 필수 사항
여러 가지 공격들
Same-site cookie
사용 => 같은 도메인에서만 세션/쿠키를 사용할 수 있다.