보안취약점으로 Strict-Transport-Security 헤더에 오류에 대한 해결방법입니다.
일반적인 경우에는 이 문제가 나오지 않을테지만, 스프링시큐리티 사용하는 경우에 발생 합니다.
원인
HTTP 헤더를 확인해보면 HSTS 헤더값에 preload가 빠졌습니다.
preload까지 들어가야 표준에 맞나 봅니다.
정상
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
오류
Strict-Transport-Security: max-age=31536000; includeSubDomains
해결
스프링시큐리티 소스인 HstsHeaderWriter.java 에 빠뜨린 preload 값을 넣어줬습니다.
그리고 HstsHeaderWriter.java 파일을 해당 패키지 경로에 위치 시키면, 프로젝트는 이 소스를 참조하게 됩니다.
※ 스프링시큐리티 사용하면 HSTS 헤더값을 스프링시큐리티에서 생성해주니, 톰캣 설정 아무리 만져봐도 안됩니다.
package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
public final class HstsHeaderWriter implements HeaderWriter {
private static final long DEFAULT_MAX_AGE_SECONDS = 31536000L;
private static final String HSTS_HEADER_NAME = "Strict-Transport-Security";
private final Log logger = LogFactory.getLog(getClass());
private RequestMatcher requestMatcher;
private long maxAgeInSeconds;
private boolean includeSubDomains;
private boolean preload;
private String hstsHeaderValue;
public HstsHeaderWriter(RequestMatcher requestMatcher, long maxAgeInSeconds, boolean includeSubDomains, boolean preload) {
this.requestMatcher = requestMatcher;
this.maxAgeInSeconds = maxAgeInSeconds;
this.includeSubDomains = includeSubDomains;
this.preload = preload;
updateHstsHeaderValue();
}
public HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains, boolean preload) {
this(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, preload);
}
public HstsHeaderWriter(long maxAgeInSeconds, boolean includeSubDomains) {
this(new SecureRequestMatcher(), maxAgeInSeconds, includeSubDomains, true);
}
public HstsHeaderWriter(long maxAgeInSeconds) {
this(new SecureRequestMatcher(), maxAgeInSeconds, true, true);
}
public HstsHeaderWriter(boolean includeSubDomains) {
this(new SecureRequestMatcher(), DEFAULT_MAX_AGE_SECONDS, includeSubDomains, true);
}
public HstsHeaderWriter() {
this(DEFAULT_MAX_AGE_SECONDS);
}
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (this.requestMatcher.matches(request)) {
response.setHeader(HSTS_HEADER_NAME, this.hstsHeaderValue);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Not injecting HSTS header since it did not match the requestMatcher " + this.requestMatcher);
}
}
public void setRequestMatcher(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
this.requestMatcher = requestMatcher;
}
public void setMaxAgeInSeconds(long maxAgeInSeconds) {
if (maxAgeInSeconds < 0L) {
throw new IllegalArgumentException("maxAgeInSeconds must be non-negative. Got " + maxAgeInSeconds);
}
this.maxAgeInSeconds = maxAgeInSeconds;
updateHstsHeaderValue();
}
public void setIncludeSubDomains(boolean includeSubDomains) {
this.includeSubDomains = includeSubDomains;
updateHstsHeaderValue();
}
public void setPreload(boolean preload) {
this.preload = preload;
updateHstsHeaderValue();
}
private void updateHstsHeaderValue() {
String headerValue = "max-age=" + this.maxAgeInSeconds;
if (this.includeSubDomains) {
headerValue = headerValue + "; includeSubDomains";
}
if (this.preload) {
headerValue += "; preload";
}
this.hstsHeaderValue = headerValue;
}
private static final class SecureRequestMatcher implements RequestMatcher {
public boolean matches(HttpServletRequest request) {
return request.isSecure();
}
}
}
'개발' 카테고리의 다른 글
자바 JNI 사용시 이클립스 스프링 톰캣 java.library.path 설정 방법 (0) | 2022.12.09 |
---|---|
스프링 mybatis 폐쇄망 dtd 문제 (0) | 2022.11.29 |
스프링 빈 전역변수는 thread safe 한가? (0) | 2022.07.20 |
자바스크립트 유니코드 정규식 (0) | 2022.07.09 |
img 태그 adblock에 탐지되게 하는 방법 (0) | 2021.12.19 |