Рубрики
Без рубрики

Руководство по защите CSRF в Spring Security

Узнайте, как CSRF-атаки работают в практическом приложении Spring, а затем как включить защиту от таких атак с помощью Spring Security.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы обсудим атаки CSRF на подделку межсайтовых запросов и способы их предотвращения с помощью Spring Security.

Дальнейшее чтение:

Защита CSRF с пружинным MVC и Thymeleaf

Автоматическая настройка безопасности весенней загрузки

Введение в безопасность метода Spring

2. Две простые атаки CSRF

Существует несколько форм CSRF – атак-давайте обсудим некоторые из наиболее распространенных из них.

2.1. ПРИВЕДИТЕ примеры

Рассмотрим следующий GET запрос, используемый зарегистрированным пользователем для перевода денег на определенный банковский счет “1234” :

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

Если злоумышленник хочет вместо этого перевести деньги со счета жертвы на свой собственный счет – “5678” – он должен заставить жертву инициировать запрос:

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

Есть несколько способов сделать это:

  • Ссылка: Злоумышленник может убедить жертву нажать на эту ссылку, например, для выполнения передачи:
  • Изображение: Злоумышленник может использовать тег с целевым URL – адресом в качестве источника изображения, поэтому щелчок даже не требуется. Запрос будет автоматически выполнен при загрузке страницы:

2.2. Пример публикации

Если основной запрос должен быть запросом POST – например:

POST http://bank.com/transfer
accountNo=1234&amount=100

Затем злоумышленник должен заставить жертву выполнить аналогичное:

POST http://bank.com/transfer
accountNo=5678&amount=1000

Ни , ни не будут работать в этом случае. Злоумышленнику потребуется <форма> – следующим образом:

Однако форма может быть отправлена автоматически с помощью Javascript – следующим образом:


...

2.3. Практическое Моделирование

Теперь, когда мы понимаем, как выглядит атака CSRF, давайте смоделируем эти примеры в приложении Spring.

Мы начнем с простой реализации контроллера – Банковского контроллера :

@Controller
public class 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 to {}", accountNo);
        ...
    }

    @RequestMapping(value = "/transfer", method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.OK)
    public void transfer2(@RequestParam("accountNo") int accountNo, 
      @RequestParam("amount") final int amount) {
        logger.info("Transfer to {}", accountNo);
        ...
    }
}

И давайте также создадим базовую HTML-страницу, которая запускает операцию банковского перевода:



    

CSRF test on Origin

Transfer Money to John

Это страница основного приложения, запущенного в исходном домене.

Обратите внимание, что мы смоделировали как GET через простую ссылку, так и POST через простую <форму> .

Теперь – давайте посмотрим, как будет выглядеть страница злоумышленника :

Эта страница будет работать в другом домене – домене злоумышленника.

Наконец, давайте запустим два приложения – исходное и атакующее – локально и сначала получим доступ к исходной странице:

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

Затем давайте перейдем на страницу злоумышленника:

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

Отслеживая точные запросы, исходящие с этой страницы злоумышленника, мы сможем сразу же определить проблемный запрос, попав в исходное приложение и полностью аутентифицировавшись.

3. Конфигурация безопасности Пружины

Чтобы использовать защиту CSRF Spring Security, нам сначала нужно убедиться, что мы используем правильные методы HTTP для всего, что изменяет состояние ( PATCH , POST , PUT и DELETE – not GET).

3.1. Конфигурация Java

Защита CSRF включена по умолчанию в конфигурации Java. Мы все еще можем отключить его, если понадобится:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable();
}

3.2. Конфигурация XML

В более старой конфигурации XML (pre Spring Security 4) защита CSRF была отключена по умолчанию, и мы могли бы включить ее следующим образом:


    ...
    

Начиная с Spring Security 4.x – защита CSRF также включена по умолчанию в конфигурации XML; мы, конечно, все еще можем отключить ее, если нам нужно:


    ...
    

3.3. Дополнительные параметры формы

Наконец, с включенной защитой CSRF на стороне сервера нам также нужно будет включить маркер CSRF в наши запросы на стороне клиента:

3.4. Использование JSON

Мы не можем отправить токен CSRF в качестве параметра, если мы используем JSON; вместо этого мы можем отправить токен в заголовке.

Сначала нам нужно будет включить токен на нашу страницу – и для этого мы можем использовать мета-теги:


Затем мы построим заголовок:

var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");

$(document).ajaxSend(function(e, xhr, options) {
    xhr.setRequestHeader(header, token);
});

4. Тест CSRF отключен

Со всем этим мы перейдем к некоторым тестам.

Давайте сначала попробуем отправить простой запрос POST, когда CSRF отключен:

@ContextConfiguration(classes = { SecurityWithoutCsrfConfig.class, ...})
public class CsrfDisabledIntegrationTest extends CsrfAbstractIntegrationTest {

    @Test
    public void givenNotAuth_whenAddFoo_thenUnauthorized() throws Exception {
        mvc.perform(
          post("/foos").contentType(MediaType.APPLICATION_JSON)
                       .content(createFoo())
          ).andExpect(status().isUnauthorized());
    }

    @Test 
    public void givenAuth_whenAddFoo_thenCreated() throws Exception {
        mvc.perform(
          post("/foos").contentType(MediaType.APPLICATION_JSON)
                       .content(createFoo())
                       .with(testUser())
        ).andExpect(status().isCreated()); 
    } 
}

Как вы, возможно, заметили, мы используем базовый класс для хранения общей вспомогательной логики тестирования – CsrfAbstractIntegrationTest :

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class CsrfAbstractIntegrationTest {
    @Autowired
    private WebApplicationContext context;

    @Autowired
    private Filter springSecurityFilterChain;

    protected MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders.webAppContextSetup(context)
                             .addFilters(springSecurityFilterChain)
                             .build();
    }

    protected RequestPostProcessor testUser() {
        return user("user").password("userPass").roles("USER");
    }

    protected String createFoo() throws JsonProcessingException {
        return new ObjectMapper().writeValueAsString(new Foo(randomAlphabetic(6)));
    }
}

Обратите внимание, что, когда у пользователя были правильные учетные данные безопасности, запрос был успешно выполнен – никакой дополнительной информации не требовалось.

Это означает, что злоумышленник может просто использовать любой из ранее рассмотренных векторов атаки, чтобы легко скомпрометировать систему.

5. Тест с поддержкой CSRF

Теперь давайте включим защиту CSRF и посмотрим, в чем разница:

@ContextConfiguration(classes = { SecurityWithCsrfConfig.class, ...})
public class CsrfEnabledIntegrationTest extends CsrfAbstractIntegrationTest {

    @Test
    public void givenNoCsrf_whenAddFoo_thenForbidden() throws Exception {
        mvc.perform(
          post("/foos").contentType(MediaType.APPLICATION_JSON)
                       .content(createFoo())
                       .with(testUser())
          ).andExpect(status().isForbidden());
    }

    @Test
    public void givenCsrf_whenAddFoo_thenCreated() throws Exception {
        mvc.perform(
          post("/foos").contentType(MediaType.APPLICATION_JSON)
                       .content(createFoo())
                       .with(testUser()).with(csrf())
         ).andExpect(status().isCreated());
    }
}

Теперь, как этот тест использует другую конфигурацию безопасности – ту, в которой включена защита CSRF.

Теперь запрос POST просто завершится неудачей, если токен CSRF не будет включен, что, конечно, означает, что более ранние атаки больше не являются вариантом.

Наконец, обратите внимание на метод csrf() в тесте; это создает RequestPostProcessor , который автоматически заполнит допустимый токен CSRF в запросе для целей тестирования.

6. Заключение

В этой статье мы обсудили несколько атак CSRF и способы их предотвращения с помощью Spring Security.

полную реализацию этого учебника можно найти в проекте GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.