Sprievodca ochranou CSRF v jarnej bezpečnosti
1. Prehľad
V tomto tutoriáli sa budeme zaoberať útokmi CSRF na Cross-Site Request Forgery a o tom, ako im zabrániť pomocou Spring Security.
2. Dva jednoduché útoky CSRF
Existuje niekoľko foriem útokov CSRF - poďme si predstaviť niektoré z najbežnejších.
2.1. ZÍSKAJTE príklady
Zvážme nasledujúce ZÍSKAJTE žiadosť používaná prihlásenými používateľmi na prevod peňazí na konkrétny bankový účet “1234”:
ZÍSKAJTE //bank.com/transfer?accountNo=1234&amount=100
Ak chce útočník namiesto toho previesť peniaze z účtu obete na jeho vlastný účet - “5678” - musí prinútiť obeť, aby podala žiadosť:
ZÍSKAJTE //bank.com/transfer?accountNo=5678&amount=1000
Existuje niekoľko spôsobov, ako to dosiahnuť:
- Odkaz: Útočník môže presvedčiť obeť, aby klikla na tento odkaz, napríklad aby uskutočnila prenos:
Zobraziť obrázky mačiatok
- Obrázok: Útočník môže použiť značka s cieľovou adresou URL ako zdrojom obrázka - kliknutie teda nie je ani potrebné. Žiadosť sa automaticky vykoná po načítaní stránky:
2.2. Príklad POST
Ak musí byť hlavnou požiadavkou požiadavka POST - napríklad:
POST //bank.com/transfer accountNo = 1234 & suma = 100
Potom musí útočník nechať obeť bežať podobne:
POST //bank.com/transfer accountNo = 5678 & suma = 1000
Ani alebo bude v tomto prípade fungovať. Útočník bude potrebovať - nasledovne:
Formulár je však možné odoslať automaticky pomocou Javascriptu - a to nasledovne:
...
2.3. Praktická simulácia
Teraz, keď chápeme, ako vyzerá útok CSRF, simulujme tieto príklady v rámci jarnej aplikácie.
Začneme jednoduchou implementáciou radiča BankController:
@Controller verejná trieda BankController {private Logger logger = LoggerFactory.getLogger (getClass ()); @RequestMapping (value = "/ transfer", method = RequestMethod.GET) @ResponseBody public String transfer (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") konečná int suma) {logger.info ("Transfer do {} ", accountNo); ...} @RequestMapping (value = "/ transfer", method = RequestMethod.POST) @ResponseStatus (HttpStatus.OK) public void transfer2 (@RequestParam ("accountNo") int accountNo, @RequestParam ("amount") final int suma) {logger.info ("Prevod na {}", číslo účtu); ...}}
A máme tiež základnú stránku HTML, ktorá spúšťa bankový prevod:
Prevod peňazí na čiastku čísla účtu John
Toto je stránka hlavnej aplikácie bežiacej na pôvodnej doméne.
Upozorňujeme, že sme simulovali obidve a ZÍSKAJTE prostredníctvom jednoduchého odkazu ako aj a POST prostredníctvom jednoduchého .
Teraz - uvidíme ako stránka útočníka bude vyzerať takto:
Zobraziť obrázky mačiatok
Táto stránka bude fungovať na inej doméne - doméne útočníka.
Na záver spustime lokálne dve aplikácie - pôvodnú a útočnícku - a najskôr si otvorme pôvodnú stránku:
//localhost:8081/spring-rest-full/csrfHome.html
Potom vstúpime na stránku útočníka:
//localhost:8081/spring-security-rest/api/csrfAttacker.html
Pri sledovaní presných požiadaviek, ktoré pochádzajú z tejto stránky útočníka, dokážeme problémovú požiadavku okamžite spozorovať, naraziť na pôvodnú aplikáciu a úplne sa autentifikovať.
3. Jarná konfigurácia zabezpečenia
Aby sme mohli použiť Spring Security CSRF ochranu, najskôr sa musíme uistiť, že používame správne metódy HTTP pre čokoľvek, čo modifikuje stav (PATCH, POST, PUTa ODSTRÁNIŤ - nie ZÍSKAŤ).
3.1. Konfigurácia Java
Ochrana CSRF je predvolene povolené v konfigurácii Java. Stále ho môžeme deaktivovať, ak potrebujeme:
@Override protected void configure (HttpSecurity http) vyvolá výnimku {http .csrf (). Disable (); }
3.2. Konfigurácia XML
V staršej konfigurácii XML (pred Spring Security 4) bola ochrana CSRF predvolene vypnutá a mohli sme ju povoliť nasledovne:
...
Začať z Jarná bezpečnosť 4.x - ochrana CSRF je štandardne povolená aj v konfigurácii XML; samozrejme ho môžeme kedykoľvek deaktivovať, ak potrebujeme:
...
3.3. Parametre extra formulára
Nakoniec, s povolenou ochranou CSRF na strane servera, budeme musieť zahrnúť token CSRF aj do našich požiadaviek na strane klienta:
3.4. Používanie JSON
Ak používame JSON, nemôžeme odoslať token CSRF ako parameter; namiesto toho môžeme token odoslať do hlavičky.
Najprv budeme musieť zahrnúť token na našu stránku - a na to môžeme použiť metaznačky:
Potom zostrojíme hlavičku:
var token = $ ("meta [name = '_ csrf']"). attr ("obsah"); var header = $ ("meta [name = '_ csrf_header']"). attr ("obsah"); $ (document) .ajaxSend (function (e, xhr, options) {xhr.setRequestHeader (header, token);});
4. Test deaktivácie CSRF
Keď budeme mať všetko pripravené, urobíme nejaké testovanie.
Skúsme najskôr odoslať jednoduchú požiadavku POST, keď je vypnutá funkcia CSRF:
@ContextConfiguration (classes = {SecurityWithoutCsrfConfig.class, ...}) verejná trieda CsrfDisabledIntegrationTest predlžuje CsrfAbstractIntegrationTest {@Test verejná neplatnosť givenNotAuth_whenAddFoo_thenUnauthorized () obsahovala () "" content (createFoo ())). andExpect (status (). isUnauthorized ()); } @Test public void givenAuth_whenAddFoo_thenCreated () vyvolá výnimku {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()) .with (testUser ())) .andExpect (status () .isCreated ()); }}
Ako ste si mohli všimnúť, na udržanie bežnej pomocnej logiky testovania používame základnú triedu - CsrfAbstractIntegrationTest:
@RunWith (SpringJUnit4ClassRunner.class) @WebAppConfiguration verejná trieda CsrfAbstractIntegrationTest {@Autowired súkromný kontext WebApplicationContext; @Autowired private Filter springSecurityFilterChain; chránený MockMvc mvc; @Before public void setup () {mvc = MockMvcBuilders.webAppContextSetup (kontext). AddFilters (springSecurityFilterChain) .build (); } chránené RequestPostProcessor testUser () {návratový používateľ ("používateľ"). heslo ("userPass"). role ("USER"); } chránený reťazec createFoo () hodí JsonProcessingException {vrátiť nový ObjectMapper (). writeValueAsString (nový Foo (randomAlphabetic (6)))); }}
Upozorňujeme, že keď mal používateľ správne bezpečnostné poverenia, požiadavka bola úspešne vykonaná - neboli potrebné žiadne ďalšie informácie.
To znamená, že útočník môže jednoducho použiť ktorýkoľvek z predtým diskutovaných vektorov útoku na ľahké narušenie systému.
5. CSRF povolený test
Poďme povoliť ochranu CSRF a pozrime sa na rozdiel:
@ContextConfiguration (classes = {SecurityWithCsrfConfig.class, ...}) verejná trieda CsrfEnabledIntegrationTest rozširuje CsrfAbstractIntegrationTest {@Test public void givenNoCsrf_whenAddFoo_thenForbidden () hodí (). "" content (createFoo ()). with (testUser ())). andExpect (status (). isForbidden ()); } @Test public void givenCsrf_whenAddFoo_thenCreated () vyvolá výnimku {mvc.perform (post ("/ foos"). ContentType (MediaType.APPLICATION_JSON) .content (createFoo ()) .with (testUser ()). S (csrf ()) ). andExpect (status (). isCreated ()); }}
Teraz tento test používa inú konfiguráciu zabezpečenia - takú, ktorá má povolenú ochranu CSRF.
Teraz nebude požiadavka POST zlyhať, ak nebude zahrnutý token CSRF, čo samozrejme znamená, že skoršie útoky už nie sú možné.
Nakoniec si všimnite csrf () metóda v teste; toto vytvára a RequestPostProcessor ktorá automaticky vyplní platný token CSRF v žiadosti na účely testovania.
6. Záver
V tomto článku sme diskutovali o niekoľkých útokoch CSRF a o tom, ako im zabrániť v používaní Spring Security.
The úplná implementácia tohto tutoriálu nájdete v projekte GitHub - jedná sa o projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.