Een gids voor CSRF-bescherming in Spring Security

1. Overzicht

In deze tutorial bespreken we Cross-Site Request Forgery CSRF-aanvallen en hoe u deze kunt voorkomen met Spring Security.

2. Twee eenvoudige CSRF-aanvallen

Er zijn meerdere vormen van CSRF-aanvallen - laten we enkele van de meest voorkomende bespreken.

2.1. KRIJG voorbeelden

Laten we eens kijken naar het volgende KRIJGEN verzoek gebruikt door ingelogde gebruikers om geld over te maken naar een specifieke bankrekening “1234”:

GET //bank.com/transfer?accountNo=1234&amount=100

Als de aanvaller in plaats daarvan geld wil overboeken van de rekening van een slachtoffer naar zijn eigen rekening - “5678” - hij moet ervoor zorgen dat het slachtoffer het verzoek activeert:

GET //bank.com/transfer?accountNo=5678&amount=1000

Er zijn meerdere manieren om dat voor elkaar te krijgen:

  • Koppeling: De aanvaller kan het slachtoffer overtuigen om bijvoorbeeld op deze link te klikken om de overdracht uit te voeren:
 Laat kittens foto's zien 
  • Beeld: De aanvaller kan een tag met de doel-URL als de afbeeldingsbron, zodat de klik niet eens nodig is. Het verzoek wordt automatisch uitgevoerd wanneer de pagina wordt geladen:

2.2. POST-voorbeeld

Als het hoofdverzoek een POST-verzoek moet zijn, bijvoorbeeld:

POST //bank.com/transfer accountNo = 1234 & bedrag = 100

Vervolgens moet de aanvaller het slachtoffer een soortgelijk programma laten uitvoeren:

POST //bank.com/transfer accountNo = 5678 & bedrag = 1000

Noch de of de zal in dit geval werken. De aanvaller heeft een - als volgt:

Het formulier kan echter automatisch worden verzonden met Javascript - als volgt:

  ...

2.3. Praktische simulatie

Nu we begrijpen hoe een CSRF-aanval eruitziet, gaan we deze voorbeelden simuleren in een Spring-app.

We beginnen met een eenvoudige implementatie van de controller: de BankController:

@Controller openbare klasse BankController {privé Logger-logger = LoggerFactory.getLogger (getClass ()); @RequestMapping (value = "/ transfer", method = RequestMethod.GET) @ResponseBody public String transfer (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") laatste int bedrag) {logger.info ("Transfer naar {} ", accountNo); ...} @RequestMapping (waarde = "/ transfer", method = RequestMethod.POST) @ResponseStatus (HttpStatus.OK) openbare ongeldige overdracht2 (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") laatste int bedrag) {logger.info ("Overboeken naar {}", accountNo); ...}}

En laten we ook een eenvoudige HTML-pagina hebben die de overboeking activeert:

 Maak geld over naar John Account Number Bedrag 

Dit is de pagina van de hoofdtoepassing die op het oorspronkelijke domein draait.

Merk op dat we zowel a KRIJGEN via een eenvoudige link en een POST door middel van een simpele .

Nu - laten we eens kijken hoe de aanvallerpagina zou eruit zien als:

  Laat kittens foto's zien 

Deze pagina wordt op een ander domein uitgevoerd: het domein van de aanvaller.

Laten we tot slot de twee applicaties - de originele en de aanvaller-applicatie - lokaal uitvoeren en eerst de originele pagina openen:

//localhost:8081/spring-rest-full/csrfHome.html

Laten we vervolgens naar de aanvallerpagina gaan:

//localhost:8081/spring-security-rest/api/csrfAttacker.html

Door de exacte verzoeken te volgen die afkomstig zijn van deze aanvallerpagina, kunnen we het problematische verzoek onmiddellijk herkennen, de originele applicatie raken en volledig geverifieerd.

3. Lente-beveiligingsconfiguratie

Om de CSRF-bescherming van Spring Security te gebruiken, moeten we er eerst voor zorgen dat we de juiste HTTP-methoden gebruiken voor alles dat de status (PATCH, POST, LEGGEN, en VERWIJDEREN - niet GET).

3.1. Java-configuratie

CSRF-bescherming is standaard ingeschakeld in de Java-configuratie. We kunnen het nog steeds uitschakelen als we:

@Override protected void configure (HttpSecurity http) genereert Uitzondering {http .csrf (). Disable (); }

3.2. XML-configuratie

In de oudere XML-configuratie (pre Spring Security 4) was CSRF-bescherming standaard uitgeschakeld en konden we het als volgt inschakelen:

 ...  

Beginnend vanaf Spring Security 4.x - de CSRF-bescherming is ook standaard ingeschakeld in de XML-configuratie; we kunnen het natuurlijk nog steeds uitschakelen als we:

 ...  

3.3. Extra formulierparameters

Ten slotte, met CSRF-bescherming ingeschakeld aan de serverzijde, moeten we het CSRF-token ook opnemen in onze verzoeken aan de clientzijde:

3.4. JSON gebruiken

We kunnen het CSRF-token niet als parameter indienen als we JSON gebruiken; in plaats daarvan kunnen we het token in de koptekst indienen.

We zullen eerst het token op onze pagina moeten opnemen - en daarvoor kunnen we metatags gebruiken:

Vervolgens construeren we de koptekst:

var token = $ ("meta [name = '_ csrf']"). attr ("content"); var header = $ ("meta [name = '_ csrf_header']"). attr ("content"); $ (document) .ajaxSend (functie (e, xhr, opties) {xhr.setRequestHeader (header, token);});

4. CSRF uitgeschakeld test

Met dat alles op zijn plaats, gaan we wat testen doen.

Laten we eerst proberen een eenvoudig POST-verzoek in te dienen wanneer CSRF is uitgeschakeld:

@ContextConfiguration (klassen = {SecurityWithoutCsrfConfig.class, ...}) openbare klasse CsrfDisabledIntegrationTest breidt CsrfAbstractIntegrationTest {@Test openbare leegte gegevenNotAuth_whenAddFoo_thenSONUnauthorized () throws Exception ({mvc.Type) content (createFoo ())) .andExpect (status (). isUnauthorized ()); } @Test openbare leegte gegevenAuth_whenAddFoo_thenCreated () gooit uitzondering {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()) .with (testUser ())) .andExpect (status () .is gecreëerd()); }}

Zoals je misschien hebt gemerkt, gebruiken we een basisklasse om de algemene testhulplogica te bevatten - de CsrfAbstractIntegrationTest:

@RunWith (SpringJUnit4ClassRunner.class) @WebAppConfiguration openbare klasse CsrfAbstractIntegrationTest {@Autowired privé WebApplicationContext-context; @Autowired privéfilter springSecurityFilterChain; beschermde MockMvc mvc; @Before public void setup () {mvc = MockMvcBuilders.webAppContextSetup (context) .addFilters (springSecurityFilterChain) .build (); } beschermde RequestPostProcessor testUser () {retour gebruiker ("gebruiker"). wachtwoord ("userPass"). rollen ("USER"); } protected String createFoo () gooit JsonProcessingException {retourneer nieuwe ObjectMapper (). writeValueAsString (nieuwe Foo (randomAlphabetic (6))); }}

Merk op dat wanneer de gebruiker de juiste beveiligingsreferenties had, het verzoek met succes werd uitgevoerd - er was geen extra informatie vereist.

Dat betekent dat de aanvaller eenvoudig een van de eerder besproken aanvalsvectoren kan gebruiken om het systeem gemakkelijk in gevaar te brengen.

5. CSRF-geactiveerde test

Laten we nu CSRF-bescherming inschakelen en het verschil zien:

@ContextConfiguration (klassen = {SecurityWithCsrfConfig.class, ...}) openbare klasse CsrfEnabledIntegrationTest breidt CsrfAbstractIntegrationTest {@Test openbare leegte gegevenNoCsrf_whenAddFoo_thSONenForbidden () gooit Uitzondering ({mv.). content (createFoo ()) .with (testUser ())) .andExpect (status (). isForbidden ()); } @Test openbare leegte gegevenCsrf_whenAddFoo_thenCreated () gooit Uitzondering {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()) .with (testUser ()). Met (csrf ()) ) .andExpect (status (). isCreated ()); }}

Nu, hoe deze test een andere beveiligingsconfiguratie gebruikt - een waarbij de CSRF-bescherming is ingeschakeld.

Nu zal het POST-verzoek gewoon mislukken als het CSRF-token niet is opgenomen, wat natuurlijk betekent dat de eerdere aanvallen niet langer een optie zijn.

Let ten slotte op de csrf () methode in de test; dit creëert een RequestPostProcessor die automatisch een geldig CSRF-token in het verzoek invult voor testdoeleinden.

6. Conclusie

In dit artikel hebben we een aantal CSRF-aanvallen besproken en hoe u deze kunt voorkomen met Spring Security.

De volledige implementatie van deze tutorial is te vinden in het GitHub-project - dit is een op Maven gebaseerd project, dus het zou gemakkelijk te importeren en uit te voeren moeten zijn zoals het is.


$config[zx-auto] not found$config[zx-overlay] not found