Подтвердить что ты не робот

Генерация токенов CSRF

Это вопрос о создании токенов CSRF.

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

Мой вопрос заключается в том, чтобы генерировать токены, когда нет уникальных пользовательских данных для использования. Нет сеансов, файлы cookie не являются опцией, IP-адрес и такие вещи ненадежны.

Есть ли причина, по которой я не могу включить строку в хэш как часть запроса? Пример псевдокода для создания маркера и его вставки:

var $stringToHash = random()
var $csrfToken = hash($stringToHash + $mySecretKey)
<a href="#" onclick="location.href='http://foo.com?csrfToken={$csrfToken}&key={$stringToHash}'; return false;">click me</a>

Пример проверки на стороне сервера токена CSRF

var $stringToHash = request.get('key')
var $isValidToken = hash($stringToHash + $mySecrtKey) == request.get('csrfToken')

Строка, используемая в хэше, будет отличаться для каждого запроса. До тех пор, пока он будет включен в каждый запрос, проверка маркера CSRF может продолжаться. Поскольку он является новым для каждого запроса и только встроен в страницу, внешний доступ к токену будет недоступен. Безопасность токена тогда падает до $mySecretKey, который известен только мне.

Это наивный подход? У меня отсутствует какая-то причина, почему это не может работать?

Спасибо

4b9b3361

Ответ 1

Есть ли причина, по которой я не могу включить строку в хэш как часть запроса?

Тоны CSRF имеют две части. Токен, встроенный в форму, и соответствующий токен в другом месте, будь то в файле cookie, хранящемся в сеансе или в другом месте. Это использование в другом месте останавливает страницу, содержащуюся в автономном режиме.

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

Даже если положить его в форму URL-адреса, значит, он сам содержится, злоумышленник просто копирует форму и URL-адрес отправки.

Ответ 3

CSRF-токен, предназначенный для предотвращения (непреднамеренных) модификаций данных, которые обычно применяются с запросами POST.

Таким образом, вы должны включить токен CSRF для каждого запроса, который изменяет данные (либо GET, либо POST-запрос).

Мой вопрос касается генерирование токенов при отсутствии уникальные пользовательские данные для использования. Нет сеансов доступны, файлы cookie не являются вариант, IP-адрес и природа не надежна.

Затем просто создайте уникальный идентификатор пользователя для каждого посетителя. Включите этот идентификатор в файл cookie или в URL-адреса (если файлы cookie отключены).

Edit:

Рассмотрим следующее событие:

Вы вошли в свою учетную запись на facebook и затем ввели на какой-то произвольный веб-сайт.

На этом веб-сайте есть форма, которую вы отправляете, которая сообщает вашему браузеру отправлять POST-запрос в вашу учетную запись facebook.

Этот запрос POST может изменить ваш пароль или добавить комментарий и т.д., потому что приложение facebook признало вас зарегистрированным и зарегистрированным пользователем. (если нет другого механизма блокировки, такого как CAPTCHA)

Ответ 4

Вам просто нужен один и тот же "токен" в URL/форме и в файле cookie. Это означает, что вы могли бы настроить страницу cookie файла на то, что он хочет (предпочтительно, какое-то случайное значение) с помощью JavaScript, а затем просто передать одно и то же значение во всех запросах, которые идут на ваш сервер (в качестве параметра URI? поле). Не нужно, чтобы ваш сервер генерировал cookie.

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

Если ваш сервер, создающий токен, будет считать, что этот токен можно безопасно передать в ваш браузер, не будучи подхваченным попытками CSRF (зачем рисковать?). Хотя вы могли бы добавить больше логики в маркер, созданный сервером, но для предотвращения CSRF нет необходимости.

(Если я ошибаюсь, пожалуйста, дайте мне знать)

Ответ 5

Я хочу сказать, что ваш подход работает, потому что атака CSRF является злоумышленником, использующим браузер жертвы, чтобы создать статус входа в систему, почему они могут это сделать? потому что на большинстве серверов проверка сеанса основана на SessionID в cookie, а cookie - часть данных, которая автоматически привязывается к HTTP-запросу, отправленному на сервер.

Следовательно, существуют два ключевых фактора для защиты CSRF

  • Создайте токен запроса и попросите клиента передать его на сервер не-cookie образом, либо параметр URL, либо форма POST в порядке.
  • Храните маркер в безопасности так же, как и с SessionID, например, используя SSL.

Я рекомендую читать CryptForms Cheat Sheet

Ответ 6

Существует несколько реализаций CSRF токена. Главное, генерируется ли этот токен csrf на стороне клиента или на стороне сервера. Потому что реализация кардинально меняется для этих двух сценариев, а также для энтропии токена.

Для серверной стороны SecureRandom является предпочтительным способом, но в вашем случае вы хотите сгенерировать токен CSRF до того, как будет идентифицирован какой-либо пользователь, window.crypto предоставляет эту функциональность, где вы можете сгенерировать достаточно непонятную строку для использования в Токен CSRF

Ответ 7

Я думаю, что лучшая идея сделать хэш на основе HMAC, т.е. сделать хэш, зашифрованный каким-то паролем в этой последовательности: имя пользователя + user_id + метка времени. Каждый запрос хеш должен быть другим, отметка времени должна быть, если вы не хотите, чтобы простое воспроизведение хеша в атаке.

Ответ 8

С помощью токена CSRF мы можем убедиться, что входящий запрос аутентифицирован (знайте, что пользователь не хакер)

Пожалуйста, обратите внимание, что мне нужен подход ниже, но Google не может помочь мне, даже на stackoverflow, я не получил упомянутый код ниже, но после сбора ответа stackoverflow я сделал мой день. Так что это полезно для дальнейшего поиска/специально для начинающих

Я описал ниже Spring MVC с Spring Interceptor

Примечание. Я использовал кеш Google для хранения соли в кеше для повторной проверки.

ниже зависимости нужно добавить pom.xml

    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.0-jre</version>
    </dependency>

ниже реализации HandlerInterceptorAdapter


    package com.august.security;

    import java.security.SecureRandom;
    import java.util.Enumeration;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;

    import org.apache.commons.lang3.RandomStringUtils;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;

    public class CsrfSecurity extends HandlerInterceptorAdapter {
        List<String> urlList= new LinkedList<>();
        private static final String CSRF_TAG = "CSRF-CHECK";

        @SuppressWarnings("unchecked")
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handleer)
                throws Exception {
            System.out.println("Inside Pre Handler");

            String reqUrl = request.getRequestURI().toString();
            System.out.println("Request URL : " + reqUrl);
            String ipAddress = request.getHeader("X-FORWARDED-FOR");
            if (ipAddress == null) {
                ipAddress = request.getRemoteAddr();
            }
            //local host url http://localhost:8080/august/
            if (request.getRequestURI().contains("/august/")) {
                System.out.println("pre handler return true");
                //it will return and next executed postHandelr method
                //because of on above url my webApplication page working
                return true;
            }
            if (ignoreUrl().contains(request.getRequestURI())) {
                System.out.println("inside ignore uri");
                return true;
            } else {
                System.out.println("CSRF Security intercepter preHandle method started.......");
                String salt = request.getParameter("csrfPreventionSalt");
                HttpSession sessionAttribute = request.getSession();
                Cache<String, Boolean> csrfPreventionSalt = (Cache<String, Boolean>) sessionAttribute
                        .getAttribute("csrfPreventionSalt");
                if (csrfPreventionSalt == null) {
                    System.out.println("Salt not matched session expired..");
                    parameterValuesPrint(request, "saltCacheNotFound");
                    response.sendRedirect("error");
                    return false;
                } else if (salt == null) {
                    parameterValuesPrint(request, "noSaltValue");
                    System.out.println("Potential CSRF detected !! inform ASAP");
                    response.sendRedirect("error");
                    return false;
                } else if (csrfPreventionSalt.getIfPresent(salt) == null) {
                    System.out.println("saltValueMisMatch");
                    System.out.println("Potential CSRF detected !! inform ASAP");
                    response.sendRedirect("error");
                } else {
                    request.setAttribute("csrfPreventionSalt", csrfPreventionSalt);
                }
                return true;
            }

        }

        @SuppressWarnings("unchecked")
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) {
            System.out.println("Inside post Handler");
            System.out.println("CSRF Security key generator method started");
            try {
                //localhost url http://localhost:8080/august/
                //api is my controller path so no need to genrate token for api
                if (request.getRequestURI().contains("/august/api/")) {
                    System.out.println("No need to genrate salt for api");
                } else {
                    HttpSession sessionAttribute = request.getSession();
                    Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>) sessionAttribute
                            .getAttribute("csrfPreventionSalt");
                    System.out.println("csrfPreventionSaltCache ::: " + csrfPreventionSaltCache);
                    if (csrfPreventionSaltCache == null) {
                        csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000)
                                .expireAfterWrite(20, TimeUnit.MINUTES).build();
                        request.getSession().setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);
                    }

                    String salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());
                    System.out.println("csrfPreventionSalt genrated ::: " + salt);
                    csrfPreventionSaltCache.put(salt, Boolean.TRUE);
                    if (modelAndView != null) {
                        System.out.println("Model and view not null and salt is added in modelAndView");
                        modelAndView.addObject("csrfPreventionSalt", salt);
                    }
                }
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
                ex.printStackTrace();
            }
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("afterCompletion : ");
            if (ex != null) {
                System.out.println("exception : " + ex.getMessage());
                ex.printStackTrace();
            }
        }

        private List<String> ignoreUrl() {
            if(urlList == null) {
                urlList.add("/august/error");
                //add here your ignored url.
            }
            return urlList;
        }

        private void parameterValuesPrint(HttpServletRequest request, String err) {
            StringBuilder reqParamAndValue = new StringBuilder();
            Enumeration<?> params = request.getParameterNames();
            while (params.hasMoreElements()) {
                Object objOri = params.nextElement();
                String param = (String) objOri;
                String value = request.getParameter(param);
                reqParamAndValue = reqParamAndValue.append(param + "=" + value + ",");
            }
            System.out.println(CSRF_TAG + " " + err + "RequestedURL : " + request.getRequestURL());
        }
    }

Ниже приведена регистрация перехватчика с контекстом весны


package com.august.configuration;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;

    import com.august.security.CsrfSecurity;

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages="com.august")
    public class SpringConfiguration extends WebMvcConfigurerAdapter  {

        @Bean
        public ViewResolver viewResolver() {
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
            //viewResolver.setViewClass(JstlView.class);
            viewResolver.setPrefix("/WEB-INF/views/");
            viewResolver.setSuffix(".jsp");
            return viewResolver;

        }

        @Bean
        public CsrfSecurity csrfSecurity() {
            return new CsrfSecurity();
        }
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new CsrfSecurity());
        }
    }

ниже мой контроллер


    package com.august.v1.appcontroller;

    import javax.servlet.http.HttpSession;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;


    @Controller
    public class HomeController {

        @Autowired 
        HttpSession httpSession;

        @RequestMapping("/")
        public String index(Model model) {
            httpSession.invalidate();
            System.out.println("Home page loaded");
            return "index";
        }
    }

ниже моя страница index.jsp jsp


    <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
        pageEncoding="ISO-8859-1" isELIgnored="false"%>
         //don't forget to add isELIgnored="false" on old(version) jsp page because of i 
         //have wasted 1 hour for this
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>ADS Home</title>
    </head>
    <body>
    <h1>${csrfPreventionSalt}</h1>
    <input type="hidden" name="csrfPreventionSalt" value=${csrfPreventionSalt}>
    </body>
    </html>

Для понимания о CSRF - объяснение CSRF

Ответ 9

CSRF использует сеанс пользователя, поэтому, если у вас его нет, CSRF отсутствует.