1. Введение
Версии Spring Framework с 5.0 по 5.0.4, с 4.3 по 4.3.14 и другие более старые версии имели уязвимость безопасности при обходе каталога или пути в системах Windows.
Неправильная настройка статических ресурсов позволяет злоумышленникам получить доступ к файловой системе сервера. Например, обслуживание статических ресурсов с использованием протокола file: обеспечивает несанкционированный доступ к файловой системе в Windows .
Spring Framework признал уязвимость и устранил ее в более поздних выпусках.
Следовательно, это исправление защищает приложения от атак с обходом пути. Однако с этим исправлением некоторые из более ранних URL-адресов теперь вызывают исключение org.springframework.security.web.firewall.RequestRejectedException
.
Наконец, в этом руководстве давайте узнаем об org.springframework.security.web.firewall.RequestRejectedException
и StrictHttpFirewall
в контексте атак обхода пути .
2. Уязвимости обхода пути
Уязвимость обхода пути или обхода каталога делает возможным несанкционированный доступ за пределы корневого каталога веб-документа. Например, изменение URL-адреса может привести к несанкционированному доступу к файлам за пределами корня документа.
Хотя самые последние и популярные веб-серверы компенсируют большинство этих атак, злоумышленники все еще могут использовать URL-кодирование специальных символов, таких как «./», «../», чтобы обойти защиту веб-сервера и получить незаконный доступ.
Кроме того, OWASP обсуждает уязвимости Path Traversal и способы их устранения.
3. Уязвимость Spring Framework
Теперь давайте попробуем воспроизвести эту уязвимость, прежде чем мы узнаем, как ее исправить.
Во-первых, давайте клонируем примеры Spring Framework MVC. Позже давайте изменим pom.xml
и заменим существующую версию Spring Framework уязвимой версией. **
**
Клонируем репозиторий:
git clone git@github.com:spring-projects/spring-mvc-showcase.git
Внутри клонированного каталога отредактируйте pom.xml
, включив 5.0.0.RELEASE
в качестве версии Spring Framework:
<org.springframework-version>5.0.0.RELEASE</org.springframework-version>
Затем отредактируйте класс веб-конфигурации WebMvcConfig
и измените метод addResourceHandlers
, чтобы сопоставить ресурсы с локальным файловым каталогом с помощью файла:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("file:./src/", "/resources/");
}
Позже соберите артефакт и запустите наше веб-приложение:
mvn jetty:run
Теперь, когда сервер запустится, вызовите URL:
curl 'http://localhost:8080/spring-mvc-showcase/resources/%255c%255c%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/windows/system.ini'
%252e%252e%255c
— это форма ..\ с двойным кодированием, а %255c%255c — форма
\\
с двойным кодированием . ``
Небезопасно, что ответом будет содержимое системного файла Windows system.ini.
4. Интерфейс Spring Security HttpFirewall
Спецификация сервлета точно не определяет различие между servletPath
и pathInfo.
Следовательно, существует несоответствие между контейнерами сервлетов при переводе этих значений.
Например, в Tomcat 9
для URL-адреса http://localhost:8080/api/v1/users/1
URI /1
должен быть переменной пути.
С другой стороны, следующее возвращает /api/v1/users/1
:
request.getServletPath()
Однако приведенная ниже команда возвращает null
:
request.getPathInfo()
Неспособность отличить переменные пути от URI может привести к потенциальным атакам, таким как атаки Path Traversal / Directory Traversal. Например, пользователь может использовать системные файлы на сервере, включив файлы \\,
/../, .
.\ в URL. К сожалению, только некоторые контейнеры сервлетов нормализуют эти URL-адреса.
Весенняя безопасность спешит на помощь. Spring Security последовательно ведет себя во всех контейнерах и нормализует такие вредоносные URL-адреса, используя интерфейс HttpFirewall
. Этот интерфейс имеет две реализации:
4.1. Брандмауэр по умолчаниюHttp
Во-первых, не будем путаться в названии класса реализации. Другими словами, это не реализация HttpFirewall
по умолчанию .
Брандмауэр пытается очистить или нормализовать URL-адреса и стандартизирует servletPath
и pathInfo
для контейнеров. Кроме того, мы можем переопределить поведение HttpFirewall
по умолчанию, явно объявив @Bean
:
@Bean
public HttpFirewall getHttpFirewall() {
return new DefaultHttpFirewall();
}
Однако StrictHttpFirewall
обеспечивает надежную и безопасную реализацию и является рекомендуемой реализацией.
4.2. Строгий HTTP-брандмауэр
StrictHttpFirewall
— это стандартная и более строгая реализация HttpFirewall.
Напротив, в отличие от DefaultHttpFirewall
, StrictHttpFirewall
отклоняет любые ненормализованные URL-адреса, обеспечивая более строгую защиту. Кроме того, эта реализация защищает приложение от нескольких других атак, таких как межсайтовая трассировка (XST) и подделка глаголов HTTP . ``
Более того, эта реализация настраиваема и имеет разумные значения по умолчанию. Другими словами, мы можем отключить (не рекомендуется) несколько функций, таких как разрешение точек с запятой как части URI:
@Bean
public HttpFirewall getHttpFirewall() {
StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();
strictHttpFirewall.setAllowSemicolon(true);
return strictHttpFirewall;
}
Короче говоря, StrictHttpFirewall
отклоняет подозрительные запросы с помощью org.springframework.security.web.firewall.RequestRejectedException
.
Наконец, давайте разработаем приложение управления пользователями с операциями CRUD для пользователей с использованием Spring REST и Spring Security и посмотрим на StrictHttpFirewall
в действии. **
**
5. Зависимости
Давайте объявим зависимости Spring Security и Spring Web :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
</dependency>
6. Конфигурация безопасности Spring
Далее давайте защитим наше приложение с помощью базовой аутентификации, создав класс конфигурации, который расширяет WebSecurityConfigurerAdapter
:
@Configuration
public class SpringSecurityHttpFirewallConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/error").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
По умолчанию Spring Security предоставляет пароль по умолчанию, который меняется при каждом перезапуске. Следовательно, давайте создадим имя пользователя и пароль по умолчанию в application.properties
:
spring.security.user.name=user
spring.security.user.password=password
Отныне мы будем получать доступ к нашим защищенным API REST, используя эти учетные данные.
7. Создание защищенного REST API
Теперь давайте создадим наш REST API управления пользователями:
@PostMapping
public ResponseEntity<Response> createUser(@RequestBody User user) {
userService.saveUser(user);
Response response = new Response()
.withTimestamp(System.currentTimeMillis())
.withCode(HttpStatus.CREATED.value())
.withMessage("User created successfully");
URI location = URI.create("/users/" + user.getId());
return ResponseEntity.created(location).body(response);
}
@DeleteMapping("/{userId}")
public ResponseEntity<Response> deleteUser(@PathVariable("userId") String userId) {
userService.deleteUser(userId);
return ResponseEntity.ok(new Response(200,
"The user has been deleted successfully", System.currentTimeMillis()));
}
Теперь давайте создадим и запустим приложение:
mvn spring-boot:run
8. Тестирование API
Теперь давайте начнем с создания пользователя
с помощью cURL :
curl -i --user user:password -d @request.json -H "Content-Type: application/json"
-H "Accept: application/json" http://localhost:8080/api/v1/users
Вот запрос.json
:
{
"id":"1",
"username":"navuluri",
"email":"bhaskara.navuluri@mail.com"
}
Следовательно, ответ:
HTTP/1.1 201
Location: /users/1
Content-Type: application/json
{
"code":201,
"message":"User created successfully",
"timestamp":1632808055618
}
Теперь давайте настроим наш StrictHttpFirewall
для отклонения запросов от всех HTTP-методов:
@Bean
public HttpFirewall configureFirewall() {
StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();
strictHttpFirewall
.setAllowedHttpMethods(Collections.emptyList());
return strictHttpFirewall;
}
Далее снова вызовем API. Поскольку мы настроили StrictHttpFirewall
для ограничения всех HTTP-методов, на этот раз мы получаем ошибку.
В журналах у нас есть это исключение:
org.springframework.security.web.firewall.RequestRejectedException:
The request was rejected because the HTTP method "POST" was not included
within the list of allowed HTTP methods []
Начиная с Spring Security v5.4
, мы можем использовать RequestRejectedHandler
для настройки состояния HTTP
при возникновении RequestRejectedException
:
@Bean
public RequestRejectedHandler requestRejectedHandler() {
return new HttpStatusRequestRejectedHandler();
}
Обратите внимание, что код состояния HTTP по умолчанию при использовании HttpStatusRequestRejectedHandler
равен 400.
Однако мы можем настроить его, передав код состояния в конструкторе класса HttpStatusRequestRejectedHandler
.
Теперь давайте перенастроим StrictHttpFirewall
, чтобы разрешить \\
в методах URL и HTTP GET
, POST
, DELETE
и OPTIONS
:
strictHttpFirewall.setAllowBackSlash(true);
strictHttpFirewall.setAllowedHttpMethods(Arrays.asList("GET","POST","DELETE", "OPTIONS")
Затем вызовите API:
curl -i --user user:password -d @request.json -H "Content-Type: application/json"
-H "Accept: application/json" http://localhost:8080/api<strong>\\</strong>v1/users
И вот у нас есть ответ:
{
"code":201,
"message":"User created successfully",
"timestamp":1632812660569
}
Наконец, давайте вернемся к исходной строгой функциональности StrictHttpFirewall
, удалив объявление @Bean
.
Далее попробуем вызвать наш API с подозрительными URL-адресами:
curl -i --user user:password -d @request.json -H "Content-Type: application/json"
-H "Accept: application/json" http://localhost:8080/api/v1<strong>//</strong>users
curl -i --user user:password -d @request.json -H "Content-Type: application/json"
-H "Accept: application/json" http://localhost:8080/api/v1<strong>\\</strong>users
Сразу же все вышеперечисленные запросы завершаются с ошибкой:
org.springframework.security.web.firewall.RequestRejectedException:
The request was rejected because the URL contained a potentially malicious String "//"
9. Заключение
В этой статье объясняется защита Spring Security от вредоносных URL-адресов, которые могут вызывать атаки Path Traversal/Directory Traversal.
DefaultHttpFirewall
пытается нормализовать вредоносные URL-адреса. Однако StrictHttpFirewall
отклоняет запросы с RequestRejectedException
. Наряду с атаками Path Traversal, StrictHttpFirewall
защищает нас от нескольких других атак. Следовательно, настоятельно рекомендуется использовать StrictHttpFirewall
вместе с его конфигурациями по умолчанию. **
**
Как всегда, полный исходный код доступен на Github .