access token, refresh token 처리
앨범 정보를 가져오거나 앨범을 재생하기 위해서 Spotify Web API를 사용하고 있다. 특히 앨범 재생을 위해서는, 사용자가 스포티파이 계정으로 로그인하고 해당 계정을 통해 access token을 얻어 사용한다. access token을 이용해 사용자의 스포티파이 계정 정보 등에 접근할 수 있기 때문에, access token을 안전하게 사용하는 방법에 대해 찾아보게 되었다.
로컬/세션 스토리지에 저장
스토리지는 자바스크립트 코드 내에서 글로벌 변수로 읽기 / 쓰기 접근이 가능하다.
XSS 공격에 취약하다. 접근한 값을 이용해 사용자의 정보에 접근할 수 있다.
쿠키에 저장
쿠키 역시 자바스크립트 내 글로벌 변수로 읽기 / 쓰기 접근이 가능하다.
XSS 공격에 취약하다.
쿠키는 클라이언트가 HTTP 요청을 보낼 때마다 자동으로 쿠키가 서버에 전송된다.
CSRF 공격에 취약하다. 유저가 악성 사이트를 방문하게 되어 사용자의 브라우저가 api 요청을 보내게 만든다면, 이때 쿠키가 자동으로 포함된다. 공격자는 유저 권한으로 액션을 수행할 수 있게 된다.
결론적으로, refresh token은 쿠키로 저장하고 access token은 클라이언트 변수로 저장하기로 했다. 사용자가 새로고침을 할 때마다 토큰이 사라지므로, 이를 위해 refresh token은 쿠키 저장 방식을 택했다. 대신 httpOnly 쿠키로 저장하여, 자바스크립트에서 접근할 수 없도록 하여 xss 공격을 방어할 수 있다. access token은 클라이언트에서 관리하면, JavaScript 코드에서 접근할 수 없다. 새로고침 시에는 refresh token을 사용해 새롭게 얻어오면 된다.
// src/api/spotify/auth/get-access-token/route.ts
const { token, error } = await getUserAccessToken(authorizationCode);
if (error || token === undefined) {
return NextResponse.json({
error: `fail to get access token with authorization code: ${error}`
}, { status: 500 });
}
cookieStore.set(KEY_SPOTIFY_REFRESH_TOKEN, token.refresh_token!, {
httpOnly: true,
sameSite: 'strict'
});
return NextResponse.json({ token }, { status: 200 });
sameSite: strict
외부 사이트에서 세션/인증 쿠키를 강제로 포함하는 요청을 차단 → CSRF 공격을 방어
새로고침, 즉 토큰이 없으면 자동으로 토큰을 fetch하도록 커스텀 훅을 만들어 사용했다. 그리고 토큰을 필요로 하는 컴포넌트에서는 훅을 호출해 사용하고, accessToken 값이 null인 경우 enabled를 false로 설정하여 쿼리 함수가 호출되지 않도록 방지했다.
// useSpotifyAccessToken
const useSpotifyAccessToken = () => {
const accessToken = useTypedSelector(state => state.spotify.accessToken);
const dispatch = useDispatch<AppDispatch>();
useEffect(() => {
if (accessToken === null) {
dispatch(fetchAccessToken());
}
}, [accessToken, dispatch]);
return { accessToken };
};
// 스포티파이 토큰이 필요한 컴포넌트
const Page = () => {
const { accessToken } = useSpotifyAccessToken();
const { data, isError, isFetching } = useQuery({
queryKey: [],
queryFn: async () => {},
enabled: accessToken !== null
});
...
번외
XSS(Cross-Site Scripting) 공격
사용자 브라우저에서 악성 스크립트를 실행하도록 유도하는 공격 방식
공격자가 악성 스크립트를 포함한 URL을 생성하여 사용자가 클릭하면, 피해자의 브라우저에서 실행됨
리액트에서는 JSX 내부에서 사용되는 값들은 React DOM에 의해 자동으로 이스케이프 처리(escaping)됨
<script> → <script> (실행되지 않고 문자열로 출력됨)
하지만
dangerouslySetInnerHTML
을 사용할 경우, 취약점이 발생 가능
CSRF(Cross-Site Request Forgery) 공격
사용자가 자신의 의지와 무관하게 공격자가 의도한 행동을 하도록 하는 공격 방식
사용자가 로그인된 상태에서 악성 사이트 방문 시, 자동으로 원래 사이트로 요청이 전송됨
SameSite 쿠키 속성
쿠키가 전송되는 조건을 제한 → CSRF 방어 가능
종류
None
: 모든 요청에 전송Strict
: 크로스 사이트 요청에는 전송 XLax
: 크로스 사이트 요청에서는 기본적으로 쿠키 전송 X. 같은 웹 사이트나 Top Level Navigation(웹 페이지 이동) 시에는 전송Top Level Navigation
링크(
<a>
) 클릭window.location.replace
이동302
리다이렉트를 이용한 이동
Last updated