작성
·
1.6K
·
수정됨
0
안녕하세요 정수원 선생님 질문이 있습니다.
httpSecurity
.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
처럼 세션에 저장하는 경우에 응답으로 csrf token 관련 정보들이 response에 존재하지 않는데
해당 경우에는 custom하게 필터를 만들어서 세션에 저장후 응답에 적절한 id(csrfJSessinID?) 를 넣어줘서
나중에 검증할 수 있도록 해야하나요?
client 입장에서는 어떻게 csrf token을 http 요청헤더에 넣을 수 있나요?
답변 3
0
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// .csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
// .csrf();
httpSecurity
.formLogin();
httpSecurity
.authorizeRequests()
.antMatchers("/login","/").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/admin/pay").hasRole("ADMIN")
.antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")
.anyRequest().authenticated();
return httpSecurity.build();
}
cookierepo 설정으로 하면 cookie로 잘보내주지만
session repo로 설정하게 되면
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
// .csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
// .csrf();
httpSecurity
.formLogin();
httpSecurity
.authorizeRequests()
.antMatchers("/login","/").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/admin/pay").hasRole("ADMIN")
.antMatchers("/admin/**").access("hasRole('ADMIN') or hasRole('SYS')")
.anyRequest().authenticated();
return httpSecurity.build();
}
response header에 아무것도 넘어오지 않습니다.
0
제가 궁금했던것은 spring security 기본 login 페이지를 이용하면 말슴해주신것처럼 csrf token 을 넣는 tag가 존재하고 실제로 chrome debugger를 통해서 봐도 실제 값이 들어가 있는것을 확인 할 수 있었습니다.
제가 궁금한것은 client app 과 spring security server가 따로 떨어져있을 때 client app은 csrf token을 어떻게 받아오는가에 대한 궁금함 이었습니다.
chrome debugger나 postman으로 요청을 보냈을 때 response로 JessionId만 쿠키로 받아오지 csrf에 관련된것은 응답으로 받지 못했는데 client 단에서는 어떻게 넣어서 보내는가 가 궁금한것이었습니다.
안녕하세요. dohyun_lim 님
제가 갖고있는 지식이 도움 되고자 답변 드립니다.
spring security
는 크게 2가지 방법으로 csrf 토큰
을 제공하는 것으로 알고 있습니다.
HttpSessionCsrfTokenRepository
방법
csrf 토큰을 유저의 세션에 저장
CookieCsrfTokenRepository
방법
csrf 토큰을 브라우저 쿠키에 저장
client app
은 server
에 GET
요청을 하게되면 서버에서 csrf토큰
을 헤더
에 담아서 client app
에 응답
으로 줄것 입니다.
이때 서버 측에서 HttpSessionCsrfTokenRepository
을 사용하면 헤더는 X-CSRF-TOKEN
이고,
CookieCsrfTokenRepository
를 사용하면 X-XSRF-TOKEN
인것으로 알고 있습니다.
-> 수정: HttpSessionCsrfTokenRepository
을 사용하면 서버쪽에서 따로 csrf 토큰을 제공하는 로직을 구현해야 합니다.
public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {
private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN";
private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName()
.concat(".CSRF_TOKEN");
//...//
}
public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN";
static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN";
//...//
}
해당 방법은 쿠키에 csrf
를 담아서 보내기 때문에 안전한 방법은 아닌것으로 생각됩니다.
그리고 기본적으로 아무설정하지 않으면 spring security
는 HttpSessionCsrfTokenRepository
정책을 사용합니다.
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
// default HttpSessionCsrfTokenRepository임을 알수 있다.
private CsrfTokenRepository csrfTokenRepository = new LazyCsrfTokenRepository(new HttpSessionCsrfTokenRepository());
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<>();
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private final ApplicationContext context;
public CsrfConfigurer(ApplicationContext context) {
this.context = context;
}
public CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.csrfTokenRepository = csrfTokenRepository;
return this;
}
//...//
}
감사합니다.
0
일단 csrf 토큰을 생성해서 저장하는 부분은 시큐리티가 기본적으로 해 주고 있습니다.
제가 정확하게 질문을 이해하지 못해서 그럴 수 있는데 조금 더 상세한 설명 가능할까요?
그리고 클라이언트에서 csrf token 을 담아서 보내는 부분은 서버에서 csrf token form 태크에 넣든지 아니면 스크립트를 사용해서 헤더에 넣든지 구현해 주어야 합니다.
<input type="hidden" name="${ csrf.parameterName }" value="${ csrf.token }">
이부분은 실전프로젝트 편에서 csrf 를 설정하는 챕터가 있으니 참고해 주시기 바랍니다.
안녕하세요. dohyun_lim님
앞서 부정확한 정보드려서 죄송합니다.
server에서 csrf 토큰 관리 방식을
HttpSessionCsrfTokenRepository
으로 설정한다면client app에서 csrf 토큰을 받기위해서는
server
에서 구현이 필요할 것으로 보입니다.스프링 시큐리티는
CsrfFilter
의doFilterInternal()
을 통해서 토큰을 생성하고repository
에 해당 토큰을 저장합니다.repositroy
의 구현체인HttpSessionCsrfTokenRepository
의saveToken()
을 통해
HttpSession
에csrf 토큰
을 저장합니다.추후
HttpSession
에서csrf 토큰
값을 꺼내어client app
에 제공하면 됩니다.(개인적인 의견으로 쿠키를 생성하여 제공하는 것 보다는 헤더를 통해서 제공하는 것이 보안측에서 안전할 것으로 판단 됩니다.)
테스트 코드는 다음과 같습니다.
MySecurityConfig
TestController
결과
헤더
에X-CSRF-TOKEN
로 토큰(cf2db3f2-fbfe-4fcd-89e4-b2afce2c5185
)값이 응답으로 오는 것을 확인 할 수 있습니다.감사합니다.