mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Fix CSRF bugs
This commit is contained in:
@@ -18,6 +18,7 @@ import org.springframework.security.web.access.AccessDeniedHandler;
|
|||||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
|
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||||
import org.springframework.security.web.csrf.CsrfToken;
|
import org.springframework.security.web.csrf.CsrfToken;
|
||||||
import org.springframework.security.web.csrf.CsrfException;
|
import org.springframework.security.web.csrf.CsrfException;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
@@ -41,6 +42,9 @@ public class SecurityConfig {
|
|||||||
HttpSecurity http,
|
HttpSecurity http,
|
||||||
OAuth2AuthorizedClientRepository authorizedClientRepository
|
OAuth2AuthorizedClientRepository authorizedClientRepository
|
||||||
) throws Exception {
|
) throws Exception {
|
||||||
|
CsrfTokenRequestAttributeHandler csrfRequestHandler = new CsrfTokenRequestAttributeHandler();
|
||||||
|
csrfRequestHandler.setCsrfRequestAttributeName("_csrf");
|
||||||
|
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests((auth) ->
|
.authorizeHttpRequests((auth) ->
|
||||||
auth
|
auth
|
||||||
@@ -89,6 +93,7 @@ public class SecurityConfig {
|
|||||||
.csrf((csrf) ->
|
.csrf((csrf) ->
|
||||||
csrf
|
csrf
|
||||||
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||||
|
.csrfTokenRequestHandler(csrfRequestHandler)
|
||||||
.ignoringRequestMatchers("/ws/**")
|
.ignoringRequestMatchers("/ws/**")
|
||||||
)
|
)
|
||||||
.addFilterAfter(csrfTokenCookieFilter(), CsrfFilter.class);
|
.addFilterAfter(csrfTokenCookieFilter(), CsrfFilter.class);
|
||||||
@@ -140,7 +145,10 @@ public class SecurityConfig {
|
|||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
FilterChain filterChain
|
FilterChain filterChain
|
||||||
) throws java.io.IOException, jakarta.servlet.ServletException {
|
) throws java.io.IOException, jakarta.servlet.ServletException {
|
||||||
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
|
||||||
|
if (csrfToken == null) {
|
||||||
|
csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||||
|
}
|
||||||
if (csrfToken != null) {
|
if (csrfToken != null) {
|
||||||
String token = csrfToken.getToken();
|
String token = csrfToken.getToken();
|
||||||
Cookie existingCookie = WebUtils.getCookie(request, "XSRF-TOKEN");
|
Cookie existingCookie = WebUtils.getCookie(request, "XSRF-TOKEN");
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ function connect() {
|
|||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.warn("WebSocket connection issue", error);
|
console.warn("WebSocket connection issue", error);
|
||||||
|
fetchAssets();
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => showToast("Live updates connection interrupted. Retrying may be necessary.", "warning"),
|
() => showToast("Live updates connection interrupted. Retrying may be necessary.", "warning"),
|
||||||
1000,
|
1000,
|
||||||
@@ -2086,7 +2087,7 @@ function uploadAsset(file = null) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedFile.size > UPLOAD_LIMIT_BYTES) {
|
if (selectedFile.size > UPLOAD_LIMIT_BYTES) {
|
||||||
showToast(`File is too large. Maximum upload size is ${UPLOAD_MAX_BYTES / 1024 / 1024} MB.`, "error");
|
showToast(`File is too large. Maximum upload size is ${UPLOAD_LIMIT_BYTES / 1024 / 1024} MB.`, "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -352,7 +352,7 @@
|
|||||||
const broadcaster = /*[[${broadcaster}]]*/ '';
|
const broadcaster = /*[[${broadcaster}]]*/ '';
|
||||||
const username = /*[[${username}]]*/ '';
|
const username = /*[[${username}]]*/ '';
|
||||||
const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0;
|
const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0;
|
||||||
const SETTINGS = /*[[${settingsJson}]]*/;
|
const SETTINGS = JSON.parse(/*[[${settingsJson}]]*/);
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/cookie-consent.js"></script>
|
<script src="/js/cookie-consent.js"></script>
|
||||||
<script src="/js/toast.js"></script>
|
<script src="/js/toast.js"></script>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.kruhlmann.imgfloat;
|
package dev.kruhlmann.imgfloat;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.oauth2Login;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
@@ -48,6 +49,7 @@ class ChannelApiIntegrationTest {
|
|||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content("{\"username\":\"helper\"}")
|
.content("{\"username\":\"helper\"}")
|
||||||
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
||||||
|
.with(csrf())
|
||||||
)
|
)
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ class ChannelApiIntegrationTest {
|
|||||||
multipart("/api/channels/{broadcaster}/assets", broadcaster)
|
multipart("/api/channels/{broadcaster}/assets", broadcaster)
|
||||||
.file(file)
|
.file(file)
|
||||||
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
||||||
|
.with(csrf())
|
||||||
)
|
)
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andReturn()
|
.andReturn()
|
||||||
@@ -96,6 +99,7 @@ class ChannelApiIntegrationTest {
|
|||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(visibilityRequest))
|
.content(objectMapper.writeValueAsString(visibilityRequest))
|
||||||
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
||||||
|
.with(csrf())
|
||||||
)
|
)
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.hidden").value(false));
|
.andExpect(jsonPath("$.hidden").value(false));
|
||||||
@@ -113,7 +117,7 @@ class ChannelApiIntegrationTest {
|
|||||||
.perform(
|
.perform(
|
||||||
delete("/api/channels/{broadcaster}/assets/{id}", broadcaster, assetId).with(
|
delete("/api/channels/{broadcaster}/assets/{id}", broadcaster, assetId).with(
|
||||||
oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster))
|
oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster))
|
||||||
)
|
).with(csrf())
|
||||||
)
|
)
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
@@ -126,6 +130,7 @@ class ChannelApiIntegrationTest {
|
|||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content("{\"username\":\"helper\"}")
|
.content("{\"username\":\"helper\"}")
|
||||||
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", "intruder")))
|
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", "intruder")))
|
||||||
|
.with(csrf())
|
||||||
)
|
)
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user