Проблема генерации токенов CSRF
Чтобы предотвратить CSRF ниже были шаги, которые я следовал:
1 Когда сеанс запроса (для loginfilter) в первый раз приходит в ноль, создайте токен добавления сеанса (случайное число) к этому новому сеансу с помощью setAttribute() и перенаправьте на экран login.jsp с помощью dispatcher.forward.
2 на экране login.jsp используйте getAttribute() для сохранения токена в скрытом поле.
3 при отправке файла login.jsp первый запрос поступит в loginfilter, здесь сравните токен из запроса с токеном в сеансе, если совпадения затем продолжите выполнение класса действия. в противном случае создайте новый токен для того же сеанса и перенаправьте на login.jsp с помощью sendRedirect()
Когда я запустил инструмент безопасности в этом приложении, я получил сообщение "Отсутствует параметр токена одного раза".
Пожалуйста, помогите мне.
1.filter.java
if (session == null) {
chain.doFilter(request, response);
return;
}
else {
// validate the CSRF
String sToken = httprequest.getSession().getAttribute("CSRF_TOKEN")
.toString();
String pToken = httprequest.getParameter("CSRF_TOKEN");
System.out.println("Tokens - " + sToken + pToken);
if (sToken.equals(pToken)) {
chain.doFilter(request, response);
}
else {
CommonUtils.updateSessionToken(session);
/*
* RequestDispatcher rd =
* request.getRequestDispatcher("/login.jsp");
* rd.forward(request, response);
*/
httpresponse.sendRedirect("/login.jsp");
}
}
2.login.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
Object token = request.getSession().getAttribute("CSRF_TOKEN");
String tokenStr = "";
if (token != null) {
tokenStr = (String) token;
}
System.out.println("+tokenStr " + tokenStr);
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>CSRFGuard Test Application</title>
</head>
<body>
Welcome to the OWASP CSRFGuard Test Application! Where would you like
to go?
<br />
<form action="/CSRF/helloServlet" method="post">
<input type="text" name="username" /> <br /> <input type="text"
value="<%=tokenStr%>" name="CSRF_TOKEN" /> <input type="submit"
value="login">
</form>
</body>
</html>
3.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>CSRF</display-name>
<servlet>
<description>
</description>
<display-name>Hell0oServelt</display-name>
<servlet-name>HelloServelt</servlet-name>
<servlet-class>com.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServelt</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>com.CsrfGuardHttpSessionListener</listener-class>
</listener>
<filter>
<filter-name>LoggedInFilter</filter-name>
<filter-class>com.LoggedInFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggedInFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4.CsrfGuardHttpSessionListener
public class CsrfGuardHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
System.out.println("New session id - "+session.getId());
String tokenId = generateRandomId();
session.setAttribute("CSRF_TOKEN", tokenId);
System.out.println("newtoken -"+tokenId);
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
/** nothing to do **/
}
}
5.HelloServlet
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloServlet() {
super();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Welcome ...!");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
}
1 ответ
При перенаправлении браузер просто получает указание сделать новый запрос к login.jsp. На этой странице входа не будет установлен токен в скрытом поле, если вы не используете какую-либо инфраструктуру, которая может помочь вам в этом. Так что вместо этого используйте запрос диспетчера вперед. Также вы упомянули, что вы запустили инструмент безопасности. Если это инструмент, имитирующий запросы, убедитесь, что токен включен во второй запрос воспроизведения, иначе он не будет распознан.
РЕДАКТИРОВАТЬ (после того, как код был опубликован): я не получил обоснование использования слушателя. Чего вы хотели добиться, так это того, чтобы при создании сеанса вы автоматически ассоциировали с ним токен. Это вы можете сделать и в своем классе фильтров. Обратите внимание, что httpRequest.getSession проверяет сеанс и, если его нет, он также создает. Существуют и другие варианты этого метода, которые принимают логический параметр. Также вам нужно понимать, что когда вы выполняете chain.doFilter (request, response), это означает, что обработка запроса продолжится дальше в цепочке.
Вы можете получить помощь от других популярных реализаций фильтра CSRF, как здесь, здесь. Также вы можете посмотреть образец, размещенный здесь