xssAndCsrf
์คํ๋ง ์ํ๋ฆฌํฐ์์์ XSS(Cross Site Scripting)์ CSRF(Cross Site Request Forgery) ํด๊ฒฐ๋ฐฉ์
Reference
Prevent Cross-Site Scripting (XSS) in a Spring Application : https://www.baeldung.com/spring-prevent-xss
A Guide to CSRF Protection in Spring Security: https://www.baeldung.com/spring-security-csrf
XSS
xss๋ ๋ฐ๋ก ์คํ๋๋ reflected xss๊ฐ ์๊ณ , db์ ์ ์ฅ๋ ํ ์คํ๋๋ stored xss๊ฐ ์๋ค. ๋ณดํต xss๋ฅผ ๋งํ๋ฉด stored xss๋ฅผ ๋งํ๋ค.
stored xss์ ์์๋ก, ๊ณต๊ฒฉํ ๋์ ํ์ด์ง์ ๋๊ธ์ ์คํฌ๋ฆฝํธ๋ฅผ ์จ๋๊ณ , ๋๊ธ์ด db์ ์ ์ฅ๋๋ฉด ๋ค๋ฅธ ์ฌ์ฉ์๊ฐ ๊ทธ ํ์ด์ง๋ฅผ ๋ก๋ฉํ์ ๋ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋๋ ๋ฐฉ์์ด๋ค.
์ด๋ ์คํฌ๋ฆฝํธ๋ฅผ ํํฐ๋งํจ์ผ๋ก์จ ํด๊ฒฐ๊ฐ๋ฅํ๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ๋ ์๋์ ๋ฐฉ๋ฒ์ ์ง์ํ๋ค. **CSP(Content Security Policy)**๋ xss์ data injection์ ๋ง์์ฃผ๋ ์คํ๋ง ์ํ๋ฆฌํฐ์์ ๋ง๋๋ ํ๋์ ๋ ์ด์ด์ด๋ค.
@Configuration
public class SecurityConf extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
CSRF(XSRF)
์ฟ ํค๋ฅผ ์ด์ฉํ ๊ณต๊ฒฉ์ผ๋ก, ์ค์ ์ฌ์ดํธ๋ก๋ถํฐ ๋ฐ์ ์ฟ ํค๋ฅผ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅํ ํ, ๋ค๋ฅธ ์ฌ์ดํธ๊ฐ ์ด ์ฟ ํค๋ฅผ ์ฌ์ฉํด ์ค์ ์ฌ์ดํธ๋ก ์์ฒญํ๋ ๋ฐฉ์์ด๋ค. (์ฟ ํค๋ ์์ฒญ์ ์๋์ผ๋ก ํฌํจ๋๋ ํน์ฑ์ ์ด์ฉ) ์ฟ ํค๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ฟ ํค๋ฅผ ์ฌ์ฉํ์ง ์๋ jwt๋ csrf๋ฅผ ๋ง์ ํ์๊ฐ ์๋ค.
์์๋ก, ์ค์ ์ฌ์ดํธ์ ๋น์ทํ๊ฒ ์์กฐํ ์ฌ์ดํธ๋ฅผ ๋ง๋ค์ด์ ํผํด์๊ฐ ์ด ์์กฐ ํ์ด์ง์ ์ ์ํ๋ฉด ํด๋ฆญ ํน์ ํ์ด์ง ๋ก๋ฉ๊ณผ ๋์์ ์ค์ ์ฌ์ดํธ๋ก ํผํด์๊ฐ ์์กฐ๋ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ์์ด๋ค.
์๋๋ ์คํ๋ง ์ํ๋ฆฌํฐ์์ ์ ๊ณตํ๋ ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง๊ณผ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง ๋ฐฉ์์ csrf๋ฅผ ๋ง๋ ๋ฐฉ์์ด๋ค.(spring security 4.x์ด์ ๋ฒ์ ์์๋ csrf๊ฐ default๋ก ํ์ฑํ๋์ด ์๋ค.)
์ฒซ๋ฒ์งธ๋ก ์คํ๋ง๊ณผ ํตํฉ๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง ๊ธฐ์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ ๋๋ง๋ html์ csrf token์ ์ ์ฉํ๋ ๋ฐฉ์์ด ์๋ค.
- form ์ ์ก ๋ฐฉ์
๋ชจ๋ form์ ์๋์ hidden input์ ์ถ๊ฐํ๋ค.
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
์ด๋ ๋ชจ๋ form์ ์์ hidden input์ ์ถ๊ฐํด์ผ ํ๋ค๋ ๋จ์ ์ด ์กด์ฌํ๋ค.
๋ํ multipartํ์์ผ๋ก ์ ์กํด์ผ ํ ๊ฒฝ์ฐ ์์ฒญ ํ์ด๋ก๋์ ์๋ _csrfํ๋๋ฅผ ์คํ๋ง์ด ์ธ์ํด์ผ ํ๋๋ฐ, ์ด ๋ Multipartfilter๋ฅผ ์ฌ์ฉํ๋ค. ๋ถํธ๊ฐ ์๋ ์คํ๋ง์์ Multipartfilter๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ tomcat์์ MultipartParsing์ allowํ๋์ง ํ์ธํด์ผ ํ๋ค.
์๋๋ ์ธ์ฅ tomcat์ MultipartParsing์ allowํ๋ context.xml์ค์ ์ด๋ค.
<Context allowCasualMultipartParsing="true"> <WatchedResource>WEB-INF/web.xml</WatchedResource> <-- ... --> </Context>
- json ์ ์ก ๋ฐฉ์
root html์ ๋ค์์ meta tag๋ฅผ ์ถ๊ฐํ๋ค. (jquery๋ฅผ ์ฌ์ฉํ๋ค.)
<meta name="_csrf" content="${_csrf.token}"/> <meta name="_csrf_header" content="${_csrf.headerName}"/>
root ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ชจ๋ XHR request(XMLHttpRequest)์ ์ ํํ๋ ์ค์ ์ ํด์ค๋ค.
$(document).ajaxSend(function(e, xhr, options) { xhr.setRequestHeader(header, token); });
root html์ ์์ 2๋ฒ์ meta tag์ 3๋ฒ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ง ์ค์ ํ๋ฉด json์ ์ก ๋ฐฉ์์ form์ ์ก ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ ๋ฐ๋ก csrf token์ ์ค์ ํ ํ์๊ฐ ์๋ค.
๋๋ฒ์งธ๋ Stateless Spring API๋ฅผ ์ฌ์ฉํ ๋ ํด๋ผ์ด์ธํธ ์ธก์์ csrf token์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
- ์ฟ ํค์์ csrf token ๊บผ๋ด์ ์ฌ์ฉํ๊ธฐ
๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ html์ ๋ ๋๋งํ์ง ์๊ธฐ ๋๋ฌธ์ ์์์ ์ค๋ช ํ๋ ๋ฐฉ์์ ์ฌ์ฉํ ์ ์๋ค. ๋ฐ๋ผ์ ์ฟ ํค์ CSRF Token์ ๋ด๋๋ค. CookieCsrfTokenRepository.withHttpOnlyFalse()์ ๋งค๊ฐ๋ณ์๋ก ๋ฃ์ผ๋ฉด
cookieHttpOnly
๋ผ๋CookieCsrfTokenRepository
์ ๋ฉค๋ฒ ๋ณ์๊ฐ false๋ก ์ค์ ๋๋ค. httponlycookie๋ ํด๋ผ์ด์ธํธ์์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํด์ ์ฟ ํค์ ์ ๊ทผํ๋ ๊ฒ์ ๋ง๊ธฐ ์ํด ์ฌ์ฉํ๋๋ฐ, ํด๋ผ์ด์ธํธ์์ ์ฟ ํค์ ๋ค์ด ์๋ csrf token์ ์ฌ์ฉํด์ผ ํ๋ฏ๋ก false๋ก ์ค์ ํ๋ ๊ฒ์ด๋ค.(์ด ํค๋๋ฅผ ์ง์ํ๋ ๋ธ๋ผ์ฐ์ ์ ํํด์ HttpOnlyCookie๊ฐ true์ด๋ฉด ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํด์ ์ฟ ํค๋ฅผ ์กฐ์ํ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํ๋ค.) HttpOnly cookie์ ๋ํ ์์ธํ ์ ๋ณด : https://www.whitehatsec.com/glossary/content/httponly-session-cookie@Configuration public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } }
๋ ๋ฒ์งธ๋ก ํด๋ผ์ด์ธํธ ์ธก์์ ์๋ฒ ์ธก ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ชจ๋ ์์ฒญ์ ์ ์ฉ๋๋ ์ค์ ์ ํด์ค๋ค. ์คํ๋ง์์๋ ํค๋์ X-XSRF-TOKEN์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ๋๋ฐ, multipart์์ฒญ์ผ๋ก _csrfํ๋์ ๋ฃ๋ ๊ฒ์ ๋ฐ์ ์๋ ์๋ค.
const csrfToken = document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1'); fetch(url, { method: 'POST', body: /* data to send */, headers: { 'X-XSRF-TOKEN': csrfToken }, })
Last updated