CSRF(Cross Site Request Forgery) - 사이트 간 공격 위조
XSRF라고도 부른다.
CSRF 공격 방법
1. 공격자는 본인의 사이트에 일반 사용자가 접근하도록 유도한다(메일 등의 발송으로)
2. 공격자의 사이트에 접근한 사용자는 접근한 페이지 내에서 img 태그(get 방식)나 form 태그(post 방식)에 공격자의 의도가 담긴 Parameter를 담아 공격할 서버에 전송하도록한다. img 태그와 link 태그는 CORS가 적용되는 대표적 태그다.
참고 - XSS와 다른 점은 XSS는 클라이언트를 공격하는 방식이라면 CSRF는 서버를 공격하는 방법이다.
예시 및 사례
예를 들어 공격자는 사용자의 비밀번호를 바꿀 수 있다.
대표적으로 2008년 옥션 개인정보 유출 사건이 존재한다.
비밀번호 바꾸는 API를 요청해서 1234로 초기화 시키는 방식이다.
https://test.com/PWchange?password=1234
/PWchange는 password를 값으로 파라미터로 받고 id는 session에 있는 값을 이용해서 password를 1234로 변경하는 방식이다.
보통 세션 구현 방식에서는 위와 같이 하는 것이 일반적이다.
하지만 아래와 같이 id와 password를 둘다 요청 값으로 날리면 CSRF 방어고 뭐고 누구나 접근 가능한 요청이 될 수 있어서 이건 보안에 더 취약하니 논외로 하자.
https://test.com/PWchange?id=admin&password=1234
그 외에도 CSRF를 이용한 공격방법 중 하나는 은행 사이트에서 현금을 이체할 때 이체 대상을 공격자에게 전송하기 등을 예로 들 수 있다.
CSRF 방어 방법
일반적으로 많이 선택하는 방법은 쿠키를 사용하지 않는 것이라고 볼 수 있는데, 그 외에도 다른 방법을 한번 알아보자.
referrer
referrer와 host의 값이 다르다라는 점을 이용해서 막는 방식이다.
referrer는 공격자의 사이트이기 때문에 host인 서버의 주소와 다를 것이니 외부에서의 호출을 차단하게 된다.
하지만 이렇게 하면 어떤 사이트에서든 서버 호출이 불가능해질 것이고 MSA의 경우 모든 서비스에 대한 host ip를 따로 관리해줘야하는 한계점이 보일 것 같다.
토큰
서버에서 토큰을 발행하여 클라이언트의 화면에 hidden 값으로 박아넣는다.
그리고 요청 시 해당 값과 비교하여 진행하게 되는데, 공격자는 해당 값을 알 수 없으니 방어가 가능하다.
보통 금융권이라면 원장에 대한 랜덤한 값을 부여하는데 이것이 토큰의 성질을 이용한 방어 형태라고 볼 수 있다.
쿠키 사용 안함(REST API와 JWT)
일단 위에 사례에서 설명을 했을 때는 세션을 이용한 로그인 방식에서의 CSRF 공격을 설명했었다.
결론부터 말하면 CSRF는 세션을 이용한 방식에서만 공격이 가능하다.
세션은 서버에 세션 정보를 저장할 뿐만 아니라 해당 세션 정보를 식별 하기 위해 사용자 브라우저에 세션 쿠키(세션 정보를 담은 쿠키) 형태로 저장되어 있다.
세션 쿠키를 가지고 있는 사용자의 브라우저는 공격자의 유도된 페이지 내에서 호출한 링크도 사용자가 갖고 있던 세션 쿠키를 기반으로 작동하기 때문에 서버에는 세션에 저장된 ID를 참조해서 비밀번호 변경 요청이 자연스럽게 이루어지는 것이다.
한마디로 CSRF는 서버가 클라이언트 상태를 저장하고 있는 것을 이용한 취약점이라고 볼 수 있다.
그래서 결론적으로 로그인 방식에서 세션 쿠키를 사용하지 않으면 CSRF는 자동으로 방어된다.
보통 서버가 클라이언트 상태를 저장하지 않는다라는 뜻은 보통 무상태성(Stateless)를 뜻하며 REST API에서 요구되는 조건 중 하나다. 이 무상태성을 지키는 로그인 방식이 JWT나 OAuth2 토큰 방식 로그인이다.
JWT와 OAuth2가 왜 무상태성인지에 대한 얘기는 주제를 벗어나니 패스하겠다.
그럼 다시 근원적인 질문으로 돌아가서 이제 왜 무상태성을 지키는 로그인 방식에서는 CSRF가 방어가 되는지 알아보자.
JWT 토큰 방식은 보통 HTML5에 존재하는 Web Storage중 Local Storage 영역에 토큰을 저장을 한다.
그리고 서버에 요청시에 Request Header에 Local Storage 영역에 있는 토큰을 담아서 요청한다.
let token = localStorage.getItem("token");
//localStorage 영역에 저장된 token을 Request header에 담기 위해 가져온다.
Local Storage에 토큰을 보관하면 CSRF가 방어되는 이유는 쿠키와 다르게 서버에 자동으로 전송되지 않고 항상 수동으로 Header에 담아줘야 하기 때문이다.
그리고 도메인 별로 Local Storage가 구분되기 때문에 공격자가 만들어 놓은 사이트에서는 사용자의 Local Storage를 참조해서 사용할 수가 없다.(마치 위에서 설명한 referrer와 비슷한 기능을 한다고 볼 수 있다.)
여담으로 Spring Security 관점에서 본다면 JWT로 로그인 방식을 구현할 때는 쿠키를 쓰지 않을테니 CSRF가 자동으로 방어되므로 Spring Security에서 http.csrf.disabled()로 설정해도 상관이 없다.
하지만 Session, Cookie 방식의 로그인을 쓴다면 CSRF 방어를 고려하면서 만들어줘야 한다.
Axios 사용하기
프론트엔드에서 Axios를 사용하면 CSRF 토큰 값을 넣어서 보내기 때문에 CSRF Protection 기능을 제공한다.
'🗂️ Etc' 카테고리의 다른 글
Hosts 파일 경로, 형식, 원리 (0) | 2022.12.14 |
---|---|
Netty 아키텍처 기초 소개 및 사용법 (0) | 2022.12.01 |
Windows에 Redis 설치하기 (0) | 2022.10.03 |
다이나믹 쿼리 사용 시 간과할 수 있는 개인정보 유출 문제(Feat. iBatis, myBatis, QueryDsl) (0) | 2022.09.08 |
Static Method를 테스트 하기 어려운 이유 (0) | 2022.09.04 |