Повторное использование значения ViewState в другом сеансе (CSRF)

Я использую *myfaces-api-2.2.3 с javax.faces.STATE_SAVING_METHOD для клиента,

Я получил следующий сценарий,

1) Пользователь X входит в систему и добавляет пользователя XXX (используя действие jsf f:ajax), при осмотре инструментов разработчика Chrome вы можете увидеть форму, отправляемую вместе со значением ViewState.

2) Скопируйте это значение ViewState (из chrome dev tools -> вкладка network) -> поместите его в html-файл с формой (которая имитирует мой оригинальный add user X)

3) Выйти из сеанса пользователя X (сеанс становится недействительным во время этого процесса)

4) Войдите в систему с пользователем Y -> откройте этот HTML-файл в браузере и нажмите кнопку отправки формы -> вы заметите, что пользователь XXX был добавлен (несмотря на то, что значение ViewState, которое использовалось в форме, принадлежит другим пользователь (пользователь X).

Я думал, что значение ViewState не может быть использовано таким образом, и я подумал, что он должен предотвращать такого рода действия, как получается, что можно использовать одно значение ViewState в совершенно новом сеансе, который содержит свое собственное значение ViewState, и как можно Я уверен, что пользователь не сможет повторно использовать ViewState?


Смотрите мой другой вопрос и ответ BalusC: предотвращение CSRF в JSF2 с сохранением состояния на стороне клиента

2 ответа

Решение

Это заданное / ожидаемое поведение при использовании сохранения состояния на стороне клиента. Состояние представления JSF сохраняется не в сеансе, а полностью в скрытом поле ввода в форме HTML на стороне клиента. Только при использовании сохранения состояния на стороне сервера состояние просмотра JSF будет недействительным, если оно не существует в сеансе HTTP пользователя.

Я не вижу никакого способа в MyFaces аннулировать сохраненное состояние на стороне клиента в случае, если оно повторно связано с "неправильным" сеансом во время обратной передачи. org.apache.myfaces.CLIENT_VIEW_STATE_TIMEOUT контекстный параметр приближается. Вы можете установить его в то же время, что и время ожидания сеанса.

Сценарий атаки CSRF IMO немного преувеличен. Во-первых, злоумышленник должен иметь возможность получить информацию о состоянии клиентской стороны. Самый простой способ для этого - дыра в XSS. Только у JSF есть защита от атак XSS везде (если вы осторожны с escape="false"). Самый сложный способ - это получить доступ ко всему выводу HTML. Это может быть сделано атакой "человек посередине". Только это также дает злоумышленнику весь файл cookie сеанса, поэтому весь сценарий атаки CSRF в этом случае "излишне усложняется" для злоумышленника, поскольку злоумышленник может просто перехватить сеанс. Лучшая защита от этого - просто использовать HTTPS вместо HTTP.

Даже в этом случае, если злоумышленник каким-то образом получил доступ к состоянию просмотра на стороне клиента, злоумышленник не может сделать с ним много опасных действий, не расшифровывая, не сериализуя и не манипулируя им. MyFaces шифрует его по умолчанию в течение длительного времени (уже с раннего 2.0.x). Атакующий не может расшифровать его без правильного ключа и, следовательно, также не манипулировать им. Это шифрование состояния представления на стороне клиента, кстати, стало обязательной частью спецификации JSF 2.2 (поэтому Mojarra также делает это). Этот ключ сбрасывается по умолчанию при перезапуске сервера приложений (и, следовательно, все состояния на стороне клиента будут недействительными). При необходимости вы можете указать фиксированный ключ с помощью следующей записи среды в web.xml:

<env-entry>
    <env-entry-name>jsf.ClientSideSecretKey</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>[AES key in Base64 format]</env-entry-value>
</env-entry>

Вы можете использовать эту страницу для генерации случайного ключа AES в формате Base64.

Даже тогда, если злоумышленнику каким-то образом удастся расшифровать состояние просмотра и заставить другого пользователя фактически отправить его (например, через дыру в XSS или фишинговый сайт), тогда лучше всего включить на страницу JSF токен CSRF на основе сеанса HTTP., Но это также небезопасно, если в веб-приложении все еще есть дыра XSS-атаки или он обслуживается по протоколу HTTP вместо HTTPS.

Смотрите также:

Spring Security по умолчанию не проверяет запрос GET. В защите CSRF важно предотвратить опасные манипуляции с запросом POST. Таким образом, вы можете игнорировать запросы ajax partialSubmit="true" со следующим набором кода.

      @Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        RequestMatcher csrfRequestMatcher = new RequestMatcher() {
            private final Pattern allowedMethods = Pattern.compile("^GET$");
            @Override
            public boolean matches(HttpServletRequest request) {

                if(request == null)
                    return false;

                if (allowedMethods.matcher(request.getMethod()).matches()) {
                    return false;
                }

                else return request.getParameter("javax.faces.partial.ajax") == null || !request.getParameter("javax.faces.partial.ajax").equals("true");
            }

        };

        http
            .anyRequest().authenticated()
            .....
            .csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
    }
}
Другие вопросы по тегам