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.