Skip to content

Commit

Permalink
avoid CSRF attacks by
Browse files Browse the repository at this point in the history
having API-calls send Spring-created CSRF tokens and checking them.
Also https with self-signed certificates is included commented out for experimenting
  • Loading branch information
wisskirchenj committed Apr 26, 2024
1 parent 3028540 commit bf463ac
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/Flashcards_DEVMODE.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/runConfigurations/Flashcards_client.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docker/arch/compose_arm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
MONGO_HOST: flashcards-mongodb
SPRING_SECURITY_OAUTH2_AUTHORIZATIONSERVER_CLIENT_CLIENT_1_REGISTRATION_REDIRECT_URIS:
'http://${HOST_IP:-127.0.0.1}:8080/login/oauth2/code/spring'
# 'https://${HOST_IP:-127.0.0.1}:8443/login/oauth2/code/spring'
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY: INFO # switch to TRACE for debugging
depends_on:
- mongodb
Expand Down
1 change: 1 addition & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#VITE_BASE_URL=https://${HOST_IP:-127.0.0.1}:8443/api
VITE_BASE_URL=http://${HOST_IP:-127.0.0.1}:8080/api
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.hyperskill.community.flashcards.config;

import jakarta.annotation.Nonnull;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.hyperskill.community.flashcards.registration.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -15,14 +20,24 @@
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.web.csrf.CookieCsrfTokenRepository.withHttpOnlyFalse;

/**
* Spring security 6.x style provision of SecurityFilterChain bean with the security configuration,
Expand All @@ -37,7 +52,11 @@ public class WebSecurityConfiguration {
@ConditionalOnProperty(name = "DEV_MODE", havingValue = "false", matchIfMissing = true)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(CsrfConfigurer::disable)
.csrf(csrf -> csrf
.csrfTokenRepository(withHttpOnlyFalse())
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
.oauth2ResourceServer(auth -> auth.jwt(withDefaults()))
.oauth2Login(withDefaults())
.oauth2Client(withDefaults())
Expand All @@ -60,6 +79,53 @@ public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

static final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
/*
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
this.delegate.handle(request, response, csrfToken);
}

@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
return super.resolveCsrfTokenValue(request, csrfToken);
}
/*
* In all other cases (e.g. if the request contains a request parameter), use
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
}
}

static final class CsrfCookieFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.getToken();

filterChain.doFilter(request, response);
}
}

@Bean
@ConditionalOnProperty(name = "DEV_MODE", havingValue = "true")
public SecurityFilterChain filterChainDevMode(HttpSecurity http) throws Exception {
Expand Down
11 changes: 10 additions & 1 deletion server/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,14 @@ spring.security.oauth2.client.registration.spring.scope=user.read,user.write,ope

spring.security.oauth2.client.provider.spring.issuer-uri=http://localhost:8000
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8000

#spring.ssl.bundle.pem.server.keystore.certificate=classpath:certs/server.crt
#spring.ssl.bundle.pem.server.keystore.private-key=classpath:certs/server.key
#spring.ssl.bundle.pem.server.keystore.private-key-password=123456
#spring.ssl.bundle.pem.server.reload-on-update=true
#
#server.port=8443
#server.ssl.bundle=server

logging.level.org.hyperskill.community=trace
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.include=health,info,metrics
17 changes: 17 additions & 0 deletions server/src/main/resources/certs/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICtjCCAZ4CCQDHuFDM/gr98zANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJz
ZWxmLXNpZ25lZC1jZXJ0LTIwHhcNMjQwNDAxMTYxMDA3WhcNMjUwNDAxMTYxMDA3
WjAdMRswGQYDVQQDDBJzZWxmLXNpZ25lZC1jZXJ0LTIwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDLTAikVnlXKi0nBh2P9XDJmHf1l3zl8jmPx6LEUkwz
ENDzbCsn0evkMVPhE4IX2ChPBGlhD/WQiltXhvjWtH/HGe5S+rChRrIuww5/HIky
YhymKGMzS8KEmOPj5vczVwaUxc7mIih8BsPU2K5R5UrQFajbi5v5o5dnx8Dlq6SD
VHPJdkclImV6iXvlclPBX4D6WMhVoNYu+TBGO9FnRHdV8i9WYn7l6cFzw9rkgxYb
mLNUIZD/LnUgeoTC5z4i8K0MQR6QRa7VduEh3iwjoDSpWd1F4hXOj7czubLmgQbo
jGJQv/c25XmUVWNkkEzA34kan66iMLpG00MDk0MSpOttAgMBAAEwDQYJKoZIhvcN
AQELBQADggEBAL68OtdUX+9cAJZ6miS+1a3hjD0HsGv6PAVbxaVdoqOnH2Qp60nz
UZaxVOBwCtKHjXwlFFCsGs/VeP/f+E+HlpZ81GARgg6EjKEGQIBwQdKAbtOBstrl
45QQ9WhlIMgzkOaEmsfUvNRN/7kOsrfz0y00lPkzlhQrt4IaIJGDd74xdW0ahIdL
3wBKFMnclQgLjZ+s0TzkiBr9a7LMTJkyHuxrnA7Tluty55Q/eEe5ozqAXdvI8Ohd
EcTs7rtKAJ3N4JhUHINfsSNZEqxU2WMS4FROU2VpwKGXHAuUwKcuLhC/hr4mmK9o
Z41c2Wj3Lh+34RypxYu5VUKDVAUXC/1SLdM=
-----END CERTIFICATE-----
30 changes: 30 additions & 0 deletions server/src/main/resources/certs/server.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQI8nF84OPB3JICAggA
MB0GCWCGSAFlAwQBKgQQDs47J36sy3U5aeZLwegI6gSCBNBUUrOyLpK/q2XMCBKf
DhmMlflF7NBmrDDJYXah2Y2KCboLuwYdmbvU9yunSKLwaxjolsV2qbh5vDoMPJ8q
D5Bh1ajv7ltLoLLnu8ckTJvv+ybrLk0Zw7cJD6OHphi1ypEXgtUEjW+kNMhAq1eu
J4VaAt/I+bACNqyv5XkY7c4MJq/nNuKqez6D4S80GpWCcOZsCnEV1ts3Bc+9Nzk3
/GNje6cOYzbGHD4W5bjxYnMFnxHo91bGgAorwJt6xKrJeHi96KGxiNoFFbCpo5GE
mv+ZoTG+MzCygs1htDdgNCGQcVj5MDGwDOvM/1NdVS0nbakb2oJtubu2mYIyoSS6
FXXsnLoEsYkOMx+Jo1zUg1b7MZZq1JfYhbToJpd5QoU2VOYWTzda1SGWgv39UmSE
Jc6+jF4Xm9dh1bD4xtqwYK+JCA1cOG9mEACF04g3Jz0Cmods3MMzSIvCKUZ/dDhZ
8jNx6QdZM+zpolNBibZxjqdhOdYjuRfKib/LuMrKxfeUfHXawrzGTBXh351/jJ1V
om8NVMB5JH70c4M7s5HHCcs3+/dibcMD2iHf8svbRAKd+aUjnuqB4+Gc4XH/ZGb3
JvAOev1o5mTfiw/ogmfFvbkEnqvzXWKxf8FnvIvcSva2MyrTOfK/GEv77WQ2Ze3H
tiO2tpNZ5WXfrNhAUgL8XpdO4VB0ZpM4GM7QEmgHHVZ3G17yNTc4VVwxiFR3s0ur
Akgz3oWJ5TXbUa4cO+gaPt/ymwuF8GcM4rSyMj4g/+z9J9ozCZbFFj2nUxfedv9x
aLq1DXWzaINHv2TTjdbu9JreC9kBfIfLAtoMFiZVtO9PTSnCfk98YQBN7Nvji1p8
2Uzr8+ZugemP2v9h4NfplX7XJPY7IHjixk1oJ6s/JfWsfMTd1rRg/KbCzguv9LBh
5F8MwxwlBWEmCyXVSVJolhhGGeGb0RSbQ59c/Bn5Y8XIFdlCWp9ipBmE84RDma3L
LejCWZAYhrEjVFgOYonDm9G8FaV3VUdbiVmsPmXAOr6vkBd0GNMdPud07ctrbPw6
nP3M5snQz850GWjyivxKc2IIZpXM9G8RaiQxmBGWyNAD2FLiaD4npO2Bb+1jkgMc
N8saAX2n58QoAMtvCOAc8tn9Xqb+klNNrddSkYH9iX4EkSHV75nhyZSgtWB81gvK
zmiref/vQRX/odFR6SR1AMtD7GmW34dCf1aciO+mhRCLRaqNfMFMqLPE0lnfHqLt
SfQ6HBaIspl7UEvfxX3wqn4UD9Txz5HaSWRden/AGnM6RNCMW8paHwcDqTmT15l/
06rBeDaf6KSsva5vL2HMz59Q07C9rnOxBTsTe0gIlKWiLkYYKE0DXwmK3gm7rNj2
NWLSn++Yor0hlKI1uzOh5cBkTtJ98iqzu6zCo+iLsHmjX/pHMLm2oXwOlEsKVgXR
nPdSrqgh+r57qC0LXriD5wq5HCd5CI0QkmAJ2MpOWmS9yezm4zkTVDHOoIjgTL2p
NWBbbdnPj4TyY7xzlP9rQU0Y+iVhI6J+zk+7oQwXT6zFqY4FkTM6a2hhVu9vINYh
fmUtxWgEqkOBcKWl0GGAgKeQOqmVUSsAImrl//jOHDLoIHWi222etUYHogyTTzAN
gmWpud3emNXinEZioWjERUNFSw==
-----END ENCRYPTED PRIVATE KEY-----
1 change: 0 additions & 1 deletion sonar/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: "3"
services:
sonarqube:
image: sonarqube:community
Expand Down

0 comments on commit bf463ac

Please sign in to comment.