참고 자료
쿠키와 document.cookie
ko.javascript.info
🌐 악명 높은 CORS 개념 & 해결법 - 정리 끝판왕 👏
악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이
inpa.tistory.com
🍪 CORS 쿠키 전송하기 (withCredentials 옵션)
🤬 CORS를 허용했는데도 쿠키가 넘어가지 않는 현상 보통 웹을 구성할때 리액트(React)나 뷰(Vue)와 같은 라이브러리 / 프레임워크를 사용한다면 따로 프론트 서버를 실행하여 개발하게 된다. 만일
inpa.tistory.com
🌐 JWT 토큰 인증 이란? (쿠키 vs 세션 vs 토큰)
Cookie / Session / Token 인증 방식 종류 보통 서버가 클라이언트 인증을 확인하는 방식은 대표적으로 쿠키, 세션, 토큰 3가지 방식이 있다. JWT를 배우기 앞서 우선 쿠키와 세션의 통신 방식을 복습해
inpa.tistory.com
과정
지난 글에서 볼 수 있듯이, 로그인은 잘 되는 것을 확인했다.
이제 api를 통해서 내 정보를 가져오면 그만이지! 라는 행복한 상상을 했었다...
난 Spring을 아직 공부하지 않았다.
그래서 Github의 여러 Springboot 프로젝트를 뜯어보며 대충 먹고 있었다.
그런데 Spring에서 RunTime중에 intercept해서 정보를 얻을 수 있는걸 발견했다.
간단하게 구현할 수 있는 코드를 보고 충격을 금치 못했다...
@Target({ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal(expression = "#this == 'anonymouseUser' ? null : user")
public @interface ReqUser {
}
이거를 써서 내 정보를 가져오면 생각보다 일이 쉽겠는데~ 라고 생각 했었다...
로그인을 했는데, 로그인을 하라고?
api요청을 하니, 권한이 없다고 spring-security가 login창으로 redirection 시켰다.
그래서 request를 열어 봤더니, Cookie가 전송이 되지 않는다..?!
Postman으로 테스트를 해보니, Cookie를 안 보내서 문제가 생긴게 맞다는 걸 인지 했다.
이전에 Web-crawling을 할 적에 Cookie를 넣어서 bot을 보냈던 기억을 되살려 쿠키를 넣어서 보내기로 했다.
Cookie는 속성이 참 많았군요?
기억을 되살려 cookie를 박아서 Request를 보내기로 했다.
그래서 Cookie를 브라우저에서 뽑아서 박으려고 했는데, Cookie가 없다고 나온다.
Cookie에 대해 조사를 하다 보니 다음 속성을 발견했다.
- Cookie에 http-only 속성을 허용하면, 직접 뽑아낼 수 없게 된다.
- Cookie를 발급받았던 곳에 요청을 하면, 자동으로 Cookie가 실린다.
하긴, XSS(cross-site scripting)으로 내 Cookie를 가져가버리면 사고가 나니,
Cookie 척출을 못 하게 막는게 당연하긴 하다.
그러면 자동으로 Cookie가 왜 안 보내지는데?
하고 좀 더 글을 읽다보니 또 다른 특성을 발견했다.
- withCredentials 속성을 통해 Cookie 전송을 허용할 수 있다.
Flutter와 Spring에 바로 적용을 했다.
final Dio dio = Dio();
dio.options.extra['withCredentials'] = true;
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("http://localhost:8081");
configuration.addAllowedOrigin("${server_url}");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
아아 드디어, 로그인 인증이 되었따!
쿠키를 이해하지 않고 개발을 한 내 잘못이지!
좋아, 이제는 동작하겠지? 라고 생각 했었다...
쿠키의 특성 정리
- secure 속성 : https로만 주고 받을 수 있게 된다.
- http-only 속성 : document에서 cookie를 척출 할 수 없게 된다.
- withCredentials 속성 : 인증을 위한 cookie를 전송할 수 있게 된다.
- 발급받은 곳에 자동으로 Cookie를 보낸다.
쿠키를 보냈는데, 왜 무시해?
localhost에서 api 호출을 하니 정상적으로 잘 동작한다..!
프론트 배포서버에서 api 호출을 하니 또 권한이 없다고 login 페이지로 reidirect를 시켜버렸다.
뭐지 Cookie가 또 안갔나 하고 request를 다시 뜯어 봤다.
HTTP/1.1 302
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: ${MY_SITE}
Access-Control-Allow-Credentials: true
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://localhost:8080/api/v1/login/kakao
Content-Length: 0
Date: Mon, 12 Feb 2024 05:21:46 GMT
Keep-Alive: timeout=60
Connection: keep-alive
----------------------------------------------------------------------------------------------------------------
GET /api/v1/user/me HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0
Accept: */*
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Origin: ${MY_SITE}
DNT: 1
Sec-GPC: 1
Connection: keep-alive
Cookie: JSESSIONID=${hideen_value}
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Cookie 잘 간다, CORS 설정도 잘 되어있다. 어째서..?
Session-fixation-attack?
Session ID를 가로채면, Session이 유지되는 동안 계속하여 Attacker가 악용할 수 있다.
일반적으로 두 가지 방식으로 대응을 한다.
- Login할 때마다 Session-id를 재발급 한다. (먼저 발급 받은 Attack의 Session-id는 폐기된다.)
- Session에 동시 접속을 하나만 되도록 막는다.
Spring-security에서는 Session을 완전 새로 생성하지는 않고, SessionId의 값을 바꾼다고 한다.
Session 설정 건드리기
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.sessionManagement(session -> session
.sessionFixation().none()
.maximumSessions(-1)
)
session Management에다가 저 옵션을 주면 끌 수 있다고 해서 꺼봤지만,
여전히 문제가 발생한다. Debuger를 쓸 줄 알았다면 뭐가 문제인지 알겠으나 구조가 복잡해 이해하지 못 했다.
가능성은 다음과 같다고 생각한다.
- SecurityFilterChain 들어오기 전에 Session을 잡는거라 저기선 의미가 없다.
- 다른 문제가 생겼다.
피드백
Spring을 똑바로 이해 못 한채로, 만들다가 문제에 봉착했다.
Debuger에 대한 이해, Spring-security에 대한 이해도가 부족해서 명확하게 밝히지 못 했다.
1번 문제를 검증하기 위해, nginx를 이용해서 reverse-proxy를 만들어 테스트 할 예정이다.
'프로젝트 > Beatn-beat [비튼 - 비트]' 카테고리의 다른 글
13. S3 bucket에 Image 위임하기 (0) | 2024.02.15 |
---|---|
12. Nginx로 같은 host에서 운영하기 (0) | 2024.02.14 |
10. Spring-boot와 OAuth2.0 (0) | 2024.02.11 |
9. Flutter web CI/CD 적용 (0) | 2024.02.08 |
8. Positioned.fill 버그 발견 및 해소 (1) | 2024.01.29 |