Перейти к содержимому

Csrf disable spring что это

  • автор:

Зачем и нужно ли выключать csrf в SpringSecurity при использовании авторизации через custom токен

Смотрел обучающее видео по авторизации и аутентификации по токену, и там в методе, который подключает фильтры есть запись http.csrf().disable() . Зачем мы отключаем csrf-защиту, человек не пояснил? Вот полный класс для лучшего понятия, о чем я:

@Configuration @RequiredArgsConstructor public class SecurityConfig < private final JwtTokenProvider jwtTokenProvider; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception < http .httpBasic().disable() .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(authz -> < try < authz .requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/admin/sensors/**").hasAuthority("ROLE_ADMIN") .requestMatchers("/api/sensors/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER") .anyRequest().authenticated() .and() .apply(new JwtConfigurer(jwtTokenProvider)); >catch (Exception e) < throw new RuntimeException(e); >> ); return http.build(); > > 

Отслеживать

задан 15 янв 2023 в 23:52

0

Сортировка: Сброс на вариант по умолчанию

Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.

  • spring-security
  • csrf

Spring Security 4 + CSRF (добавление в Spring проект защиты от межсайтовой подделки запроса)

Здравствуйте!
Современное веб приложение считается уязвимым, если в нем отсутствует защита от Межсайтовой подделки запроса (CSRF).
В Spring Security 4.x она включена по умолчанию, поэтому при миграции с Spring Security 3.x на 4.x ее надо либо отключить

либо, правильнее и зачетнее, добавить в проект.

Собственно, сделал это в 10-минутном видео:

Ниже приведу основные моменты внедрения CSRF в проект:

  1. Использовать правильные HTTP запросы: по умолчанию CSRF защита отсутствует для запросов GET, HEAD, TRACE, OPTIONS. Это в частности означает, что для logout, если мы не хотим, чтобы злоумышленный сайт мог разлогинить пользователя, авторизованного в нашем приложении, требуется запрос POST.
  2. Во всех формах, где есть submit, добавить скрытое поле name=_csrf со значением csrfToken. Проще всего это сделать через Spring’s form tag library, который, кроме биндинга и валидации, при включенном csrf подставит в форму требуемое скрытое поле.
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); >); 
@RestController @RequestMapping(value = AdminRestController.REST_URL, consumes = MediaType.APPLICATION_JSON_VALUE) 

Ну и напоследок несколько ссылок на тему:

  • Spring Security CSRF Protection
  • Are JSON web services vulnerable to CSRF attacks
  • Правило ограничения домена (SOP)
  • Cross-origin resource sharing (CORS)

CSRF-токен

В этой статье рассмотрим, что такое Сross-Site Request Forgery и как включить в Spring Boot приложение CSRF-токен — защиту от этого мошенничества. Назвать этот токен можно было бы «анти-CSRF-токен».

Same Origin Policy и CSRF (cross-site request forgery)

Обычно запросы, сделанные в браузере с одного домена на другой, не проходят, поскольку браузер придерживается Same Origin Policy. Это политика безопасности, защищающая одни сайты от других.

Например, пусть пользователь случайно заходит на домен evil.com (мошеннический сайт) и щелкает там яркую кнопку, которая выполняет либо PUT-запрос, либо DELETE-запрос на bank.com. И так случайно получилось, что этот пользователь зарегистрирован на bank.com и в браузере хранятся куки к нему. Благодаря политике безопасности браузера, ничего плохого не случится. Потому что браузер сначала вышлет так называемый «preflight», то есть предварительный OPTIONS-запрос на bank.com с заголовком

Origin: evil.com

и затем отправит за ним настоящий PUT/DELETE-запрос, но только в том случае, если в ответе пришло разрешение на отправку запросов от evil.com. В противном случае в метод PUT/DELETE банковского сайта мы даже не попадем.

То есть предварительный запрос спрашивает у одного домена разрешение на отправку данных с другого. И чтобы bank.com (получатель запросов) отправил положительный ответ, программист bank.com должен знать домен evil.com (отправителя запросов). Тогда программист делает так, чтобы согласно спецификации CORS bank.com отправлял в ответе в заголовке Access-Control-Allow-Origin адрес evil.com. Это означает буквально «сайту evil.com можно ко мне обращаться». Как настроить CORS в Spring Boot читайте тут. CORS — это своего рода послабление Same Origin Policy. В данной статье CORS нет.

Но есть запросы, которые отправляются сразу, без предварительного запроса, так называемые simple requests. Это GET, HEAD, POST с определенным Content-Type. В частности, POST-запросы с формы. Такой запрос сразу идет в контроллер. А учитывая, что куки браузер отправляет автоматически, запрос попадет в защищенный контроллер и выполнит действие на банковском сайте. Хотя ответ получить и распарсить нельзя, действие будет выполнено.

Ниже рассмотрим, как это происходит. В примере одно приложение на одном домене делает POST-запрос на другой домен, где работает второе приложение. Рассмотрим, как защититься от таких запросов с помощью CSRF-токена.

Пример жульничества

Создадим два приложения на Spring Boot:

  • мишень для атаки, «банковское» приложение на порту 8080
  • и мошеннический сайт на порту 8081

Поскольку в примере мы будем делать запрос на «другой домен», пропишем для localhost:8080 новое имя в файле hosts:

C:\Windows\System32\drivers\etc\hosts

А именно, добавим в файл hosts строку:

127.0.0.1 bank-server

Теперь к приложению-мишени будем обращаться по адресу http://bank-server:8080/..вместо http://localhost:8080..

Приложение-мишень

Итак, пусть в нашем приложении есть контроллер и форма, с которой что-то добавляется:

Добавление

@Controller public class DocumentController < @PostMapping("/add") public String add(Document document, Model model) < System.out.println(document.getId()+" "+document.getText()+" added"); model.addAttribute("document", document); model.addAttribute("message", "добавлено"); System.out.println("PostMapping /add"); return "add"; >@GetMapping("/add") public String get() < System.out.println("GetMapping /add"); return "add"; >@GetMapping("/") public String main() < return "redirect:/add"; >>

Форма на Thymeleaf выглядит так:

При этом адрес http://bank-server:8080/add защищен, доступ к нему возможен только благодаря куки JSESSIONID после входа с помощью формы логина.

В приложении задан единственный in-memory пользователь с именем user и паролем user:

@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter < @Bean public PasswordEncoder passwordEncoder() < return NoOpPasswordEncoder.getInstance(); >@Override public void configure(AuthenticationManagerBuilder auth) throws Exception < auth.inMemoryAuthentication() .withUser("user") .password("user") .authorities("ROLE_USER"); >@Override protected void configure(HttpSecurity http) throws Exception < http.authorizeRequests() .anyRequest().authenticated() .and().formLogin(); http.csrf().disable(); >>

Выше прописано, что все запросы доступны только аутентифицированным пользователям (в том числе наша форма — ее получение по адресу /add методом GET и отправка методом POST). Форма находится в шаблоне add.html

Проверку CSRF-токена мы отключили выше отдельной строкой:

http.csrf().disable();

Это сделано для того, чтобы продемонстрировать атаку с помощью второго приложения ниже. А затем включить CSRF-токен обратно. (По умолчанию он и так включен, просто нужно не забывать добавлять его и на форму, что будет в показано самом конце).

Мошенническое приложение

Второе приложение совсем простое, оно состоит из одного view с кнопкой атаки POST (полный код тут):

Сайт жуликов

 . 

Мошенническая кнопка:

Кнопка отправляет форму со скрытыми полями: и text=«document». Если в браузере мы залогинены в приложении http://bank-server:8080 и откроем мошеннический сайт localhost:8081 с вышеприведенной кнопкой и щелкнем ее, то сохраненный куки JSESSIONID отправляется тоже, и мы попадаем в защищенный метод add() контроллера DocumentController банковского сайта. Документ добавляется.

Защита с помощью CSRF-токена

Чтобы защититься от таких запросов, нужно включить CSRF-токен, то есть убрать строку отключения, которую мы добавили выше:

// http.csrf().disable();

Теперь когда пользователь логинится на сайт http://bank-server:8080, ему выделяется специальный CSRF-токен. Он хранится в сессии и должен отправляться как скрытое поле со всех форм (также он должен прилагаться в XMLHttpRequest-запросах PUT, DELETE, POST — это для JavaScript). Выше мы показали только одну атаку, но при определенных настройках Spring MVC в определенных браузерах возможны и более сложные атаки. И хотя браузеры становятся все более безопасными, как и сам Spring MVC, все равно Spring Security имеет вот такое требование и для PUT, и для DELETE-запросов, хотя для них существуют «preflight» запросы от браузера. Но эти настройки CSRF-токена можно и поменять — убрать некоторые методы или некоторые url — сделать так, чтобы для них не требовался токен.

Итак, токен в POST-запросах сейчас требуется. Если оставить все как есть, то наше собственное банковское приложение не заработает — при попытке отправить форму получим 403. Надо сделать так, чтобы выданный CSRF-токен отправлялся. Для этого добавим его как скрытое поле на форму в наш Thymeleaf-шаблон:

Все, теперь и наше приложение работает, и с чужого сайта форму отправить нельзя, потому что мошенники не знают CSRF-токен. В отличие от JSESSIONID, он не хранится в куки браузера и не отправляется автоматически при запросах на банковский сайт.

Итоги

Исходный код обоих приложений есть на GitHub.

Как защитить Spring приложение, отключив CSRF-токен?

Всем доброго времени суток.
Я пишу Spring-boot (security) приложение и хочу использовать все инструменты веб-разработки. А именно: iframe и упрощённые ajax запросы. Во время разработки отключаю CSRF, добавляя в настройки http.csrf().disable() По хорошему CORS запросы настраиваются, но в некоторых случаях настроек мало. Например, когда нужно сделать sharing через iframe (пример: YouTube, Twitter и др.). И я решаю эту задачу путём банального добавления Principal в параметры каждого контроллера.

@GetMapping("/page") public @ResponseBody void page(Principal principal) < if(principal==null) return; String email = principal.getName(); Person pers = service.getUserByLogin(email); //some code. >

Вся логика приложения строится на том, что каждый запрос либо авторизованный, либо нет. Разумеется, у каждого пользователя свои связные данные (Linked data) и он не может получить доступ к личной информации других пользователей. Вопрос: На сколько удовлетворяет такое решение Same Origin Policy и на сколько это безопасно?

  • Вопрос задан 04 дек. 2023
  • 171 просмотр

Комментировать
Решения вопроса 0
Ответы на вопрос 2
calculator212 @calculator212
Ответ написан 05 дек. 2023
Нравится 2 2 комментария
My1Name @My1Name Автор вопроса

По второй ссылке хорошо описана ситуация и, в данном случае, мне необходимо позаботиться о «кликджекинге».

Когда сайт загружается в IFrame, все его кнопки становятся доступными и на сайте потенциального злоумышленника; любой щелчок считается действительным и для моего приложения. Это может быть фишинг и всё что угодно.

Возникает другой вопрос: Если добавить проверку референс getRequestURL(); в методы контролёра, такой запрос через iframe будет считаться внешним или локальным? Как узнать URL запроса если страница во фрейме?

calculator212 @calculator212

позаботиться о «кликджекинге»

Вроде достаточно настроить X-Frame-Options: тут
хз насколько актуально но вроде это просто делается в спринге

My1Name @My1Name Автор вопроса

Учитывая тот факт, что у меня не банковское приложение и финансовых рисков особо никаких нет, я не вижу причин запрещать пользователям встраивать личные страницы (или др. страницы сайта) в iframe. Главная задача в таком случае: ͟з͟а͟п͟р͟е͟т͟и͟т͟ь͟ ͟а͟в͟т͟о͟р͟и͟з͟и͟р͟о͟в͟а͟н͟н͟ы͟е͟ ͟з͟а͟п͟р͟о͟с͟ы͟. Для этого я разместил в шапку (в header на всех страницах сайта) незамысловатый скрипт:

const currentUrl = document.referrer; $.get('ajax/index', );

document.referrer — возвращает URL родительской страницы (которая загрузила iframe). Это работает, даже если родительский документ и iframe находятся на разных доменах. Как только начинается загрузка страницы, currentUrl отправляется в контроллер:

private @ResponseBody void checkReferrer(Principal principal, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException < String currentUrl = req.getParameter("ref"); currentUrl = currentUrl.substring(currentUrl.indexOf("/")+2); String localName = req.getServerName(); if (principal!=null && !currentUrl.startsWith(localName)) < req.getSession().invalidate(); req.logout(); Cookie [] cookies = req.getCookies(); if(cookies!=null) < for(Cookie c : cookies) < c.setValue(""); c.setPath("/"); c.setMaxAge(0); resp.addCookie(c); >> > >

Контроллер проверяет переданный URL и сравнивает его с именем сервера. Далее, если переданный адрес не включает в себя имя сервера и это ͟а͟в͟т͟о͟р͟и͟з͟и͟р͟о͟в͟а͟н͟н͟ы͟й͟ ͟з͟а͟п͟р͟о͟с͟, то происходит выход с системы и удаление всех куки. В настройках Spring-Security:

http.csrf() .disable().and() . frameOptions().sameOrigin();

Политика Same-Origin предотвращает доступ внешним скриптам к странице встроенной в iframe. А при отключении скриптов, любые запросы будут происходить от имени пользователя. То есть, програмно можно получить доступ только к своему аккаунту и только через главный вход (через iframe вход не получится). А если я что-то не учёл — критика в комментариях приветствуется.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *