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의 유저테이블에 같이 저장되어야 한다
cookie?
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에서 쿠키설정 코드
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로 세션 아이디의 키값을 무효한 값으로 갱신해야 합니다.
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. 코드 보기
서버(노드)가 클라에게 토큰을 보낼때
클라(리액트)가 서버에게 토큰을 보낼때
쿠키가 있다면 자동으로 전달 된다 대신 아래와같은 처리를 해주어야함