En guide til CSRF-beskyttelse i vårsikkerhet

1. Oversikt

I denne opplæringen vil vi diskutere CSRF-angrep på tvers av sider for forfalskning og hvordan du kan forhindre dem ved hjelp av Spring Security.

2. To enkle CSRF-angrep

Det er flere former for CSRF-angrep - la oss diskutere noen av de vanligste.

2.1. FÅ eksempler

La oss vurdere følgende forespørsel brukt av innloggede brukere for å overføre penger til en spesifikk bankkonto “1234”:

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

Hvis angriperen i stedet ønsker å overføre penger fra ofrenes konto til sin egen konto - “5678” - han må få offeret til å utløse forespørselen:

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

Det er flere måter å få det til:

  • Lenke: Angriperen kan overbevise offeret om å klikke på denne lenken for eksempel for å utføre overføringen:
 Vis kattungebilder 
  • Bilde: Angriperen kan bruke en tag med mål-URL som bildekilde - så klikket er ikke engang nødvendig. Forespørselen blir automatisk utført når siden lastes inn:

2.2. POST Eksempel

Hvis hovedforespørselen må være en POST-forespørsel - for eksempel:

POST //bank.com/transfer accountNo = 1234 & beløp = 100

Da må angriperen få offeret til å kjøre et lignende:

POST //bank.com/transfer accountNo = 5678 & beløp = 1000

Verken den eller vil fungere i dette tilfellet. Angriperen trenger en - som følger:

Skjemaet kan imidlertid sendes inn automatisk ved hjelp av Javascript - som følger:

  ...

2.3. Praktisk simulering

Nå som vi forstår hvordan et CSRF-angrep ser ut, la oss simulere disse eksemplene i en Spring-app.

Vi skal begynne med en enkel kontrollerimplementering - the BankController:

@Controller offentlig klasse BankController {private Logger logger = LoggerFactory.getLogger (getClass ()); @RequestMapping (value = "/ transfer", method = RequestMethod.GET) @ResponseBody public String transfer (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") final int amount) {logger.info ("Transfer til {} ", kontonr. nr); ...} @RequestMapping (value = "/ transfer", method = RequestMethod.POST) @ResponseStatus (HttpStatus.OK) public void transfer2 (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") final int beløp) {logger.info ("Transfer to {}", accountNo); ...}}

Og la oss også ha en grunnleggende HTML-side som utløser bankoverføringsoperasjonen:

 Overfør penger til John-kontonummerbeløpet 

Dette er siden til hovedprogrammet som kjører på opprinnelsesdomenet.

Merk at vi har simulert både a gjennom en enkel lenke samt en POST gjennom en enkel .

Nå - la oss se hvordan angriper-siden vil se ut som:

  Vis kattungebilder 

Denne siden kjører på et annet domene - angriperdomenet.

Til slutt, la oss kjøre de to applikasjonene - originalen og angriperapplikasjonen - lokalt, og la oss først få tilgang til originalsiden:

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

La oss da få tilgang til angriperens side:

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

Når vi sporer de nøyaktige forespørslene som kommer fra denne angriper-siden, kan vi øyeblikkelig oppdage den problematiske forespørselen ved å treffe den opprinnelige applikasjonen og fullstendig godkjent.

3. Vårs sikkerhetskonfigurasjon

For å kunne bruke Spring Security CSRF-beskyttelse, må vi først sørge for at vi bruker de riktige HTTP-metodene for alt som endrer tilstand (LAPP, POST, SETTE, og SLETT - ikke FÅ).

3.1. Java-konfigurasjon

CSRF-beskyttelse er aktivert som standard i Java-konfigurasjonen. Vi kan fortsatt deaktivere det hvis vi trenger å:

@ Override-beskyttet tomkonfigurasjon (HttpSecurity http) kaster unntak {http .csrf (). Deaktiver (); }

3.2. XML-konfigurasjon

I den eldre XML-konfigurasjonen (pre Spring Security 4) var CSRF-beskyttelse deaktivert som standard, og vi kunne aktivere den som følger:

 ...  

Starter fra Spring Security 4.x - CSRF-beskyttelsen er også aktivert i XML-konfigurasjonen; vi kan selvfølgelig fortsatt deaktivere det hvis vi trenger å:

 ...  

3.3. Ekstra skjemaparametere

Til slutt, med CSRF-beskyttelse aktivert på serversiden, må vi også inkludere CSRF-token i våre forespørsler på klientsiden:

3.4. Bruke JSON

Vi kan ikke sende inn CSRF-token som en parameter hvis vi bruker JSON; i stedet kan vi sende inn tokenet i overskriften.

Vi må først ta med tokenet på siden vår - og for det kan vi bruke metatags:

Så konstruerer vi overskriften:

var token = $ ("meta [name = '_ csrf']"). attr ("innhold"); var header = $ ("meta [name = '_ csrf_header']"). attr ("content"); $ (dokument) .ajaxSend (funksjon (e, xhr, opsjoner) {xhr.setRequestHeader (header, token);});

4. CSRF deaktivert test

Med alt dette på plass, vil vi gå for å gjøre noen tester.

La oss først prøve å sende inn en enkel POST-forespørsel når CSRF er deaktivert:

@ContextConfiguration (klasser = {SecurityWithoutCsrfConfig.class, ...}) offentlig klasse CsrfDisabledIntegrationTest utvider CsrfAbstractIntegrationTest {@Test offentlig ugyldighet gittNotAuth_whenAddFoo_thenUnauthorized () kaster Exception {mvc.performat. (Type). content (createFoo ())). og Expect (status (). isUnauthorized ()); } @Test offentlig ugyldig givenAuth_whenAddFoo_thenCreated () kaster unntak {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()) .with (testUser ())). Og Expect (status () .er skapt()); }}

Som du kanskje har lagt merke til, bruker vi en baseklasse for å holde den vanlige testhjelperlogikken - CsrfAbstractIntegrationTest:

@RunWith (SpringJUnit4ClassRunner.class) @WebAppConfiguration public class CsrfAbstractIntegrationTest {@Autowired private WebApplicationContext context; @Autowired private Filter springSecurityFilterChain; beskyttet MockMvc mvc; @Før offentlig ugyldig oppsett () {mvc = MockMvcBuilders.webAppContextSetup (context) .addFilters (springSecurityFilterChain) .build (); } beskyttet RequestPostProcessor testUser () {retur bruker ("bruker"). passord ("userPass"). roller ("USER"); } beskyttet String createFoo () kaster JsonProcessingException {return new ObjectMapper (). writeValueAsString (new Foo (randomAlphabetic (6))); }}

Merk at når brukeren hadde de riktige sikkerhetsopplysningene, ble forespørselen fullført - ingen ekstra informasjon var nødvendig.

Det betyr at angriperen ganske enkelt kan bruke noen av de tidligere diskuterte angrepsvektorene for å kompromittere systemet.

5. CSRF-aktivert test

La oss nå aktivere CSRF-beskyttelse og se forskjellen:

@ContextConfiguration (klasser = {SecurityWithCsrfConfig.class, ...}) offentlig klasse CsrfEnabledIntegrationTest utvider CsrfAbstractIntegrationTest {@Test offentlig ugyldighet gittNoCsrf_whenAddFoo_thenForbidden () kaster Unntak {mvc.performat. (Type). content (createFoo ()) .with (testUser ())). andExpect (status (). isForbidden ()); } @Test offentlig ugyldighet gittCsrf_whenAddFoo_thenCreated () kaster unntak {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()). With (testUser ()). With (csrf ()) ). ogExpect (status (). isCreated ()); }}

Nå hvordan denne testen bruker en annen sikkerhetskonfigurasjon - en som har CSRF-beskyttelse aktivert.

Nå vil POST-forespørselen ganske enkelt mislykkes hvis CSRF-token ikke er inkludert, noe som selvfølgelig betyr at de tidligere angrepene ikke lenger er et alternativ.

Til slutt, legg merke til csrf () metode i testen; dette skaper en RequestPostProcessor som automatisk fyller ut et gyldig CSRF-token i forespørselen for testformål.

6. Konklusjon

I denne artikkelen diskuterte vi et par CSRF-angrep og hvordan vi kan forhindre dem ved hjelp av Spring Security.

De full gjennomføring av denne opplæringen finnes i GitHub-prosjektet - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


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