서버는 특정 유저의 정보를 담은 세션을 생성한다
1. 유저가 로그인할 때
2. 서버는 세션을 생성한 후
3. 그 세션의 id를 클라이언트에 보내주고
4. 클라이언트는 이 id를 클라이언트에 저장해두었다가
5. 인증이 필요한 데이터를 가져올때 서버에 id값을 보내면
6. 서버는 그 id를 통해 세션을 불러와 유효한지 확인한다
실질적인 인증 정보는 accessToken이다 이는 일정 시간이 지나면 만료되는 특징을 가지는데
로그인하면 빠질 수 없는 것이 보안문제이다
보안 공격의 종류와 특징에 대해 살펴보자
해커가 클라이언트 브라우저에 JavaScript를 삽입해 실행하는 공격으로써 url에 javascript를 적어 사이트에 스크립트를 삽입하거나 input 태그를 통해 javascript를 서버로 전송해 스크립트를 실행하는 식으로 마치 그 사이트인것마냥 API 요청을 한다
해커가 다른 사이트에서 우리 사이트의 API를 요청하는 공격
API를 요청할 수 있는 클라이언트 도메인을 서버에서 통제하고 있지 않다면 가능한 일인데
해커가 클라이언트에 저장된 유저 인증정보를 서버에 보낼 수 있다면 제대로 로그인한 것처럼 유저의 정보를 변경하거나 유저만 가능한 일들을 수행할 수 있다
브라우저 저장소에 저장하며 Javascript내 글로벌 변수로 읽기 / 쓰기 접근이 가능하다
브라우저에 쿠키로 저장하며 클라이언트가 HTTP 요청을 보낼 때마다 자동으로 쿠키가 서버에 전송된다
Javascript내 글로벌 변수로 읽기 / 쓰기 접근이 가능하다
브라우저에 쿠키로 저장되는 건 같지만 Javascript내에서 접근이 불가능하다
secure을 적용하면 https 접속에서만 동작한다
사실 어떠한 저장 방식을 택해도 XSS 취약점이 있다면 보안 이슈가 존재하므로 추가적으로 XSS 방어 처리가 필수다
React는 해커가 string에 html/Javascript를 담아 JSX에 삽입할 경우 자동으로 escape 처리한다고 한다
최상단 index.js에 다음과 같이 설정해주면 refreshToken cookie를 주고 받을 수 있다
axios.defaults.baseURL = 'https://www.kaen.com';
axios.defaults.withCredntials = true;
아래는 로그인 로직입니다
onLogin = (email, password) => {
const data = {
email,
password,
};
axios.post('/login', data).then(response => {
const { accessToken } = response.data;
// API 요청하는 콜마다 헤더에 accessToken 담아 보내도록 설정
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
// accessToken을 localStorage, cookie 등에 저장하지 않는다!
}).catch(error => {
// ... 에러 처리
});
}
// 개인적으로 사용했던 로그인 코드
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import config from "../../config";
const ClickSignIn = ({ loginHandler }) => {
const [loginInfo, setLoginInfo] = useState({
email: "",
password: "",
});
const [errorMessage, setErrorMessage] = useState("");
const onSignIn = () => {
axios
.post(`${config.serverUrl}/users/signin`, loginInfo, {
withCredentials: true,
})
.then((res) => loginHandler(res.data));
if (!loginInfo.email || !loginInfo.password) {
setErrorMessage("이메일과 비밀번호를 입력하세요");
return;
}
};
return (
<>
<div>
<button className="btn" onClick={onSignIn}>
LogIn
</button>
<button className="btn signup-btn">
<Link to="/signup" className="signup-link">
SignUp
</Link>
</button>
<div className="alert-box">{errorMessage}</div>
</div>
</>
);
};
export default ClickSignIn;
아직 이해하지 못한 부분이므로 추후에 수정해서 포스팅할 예정