Commit 3a6976f0 authored by Dave Cridland's avatar Dave Cridland

OF-777 CVE-2015-6973 CSRF protection (partial)

Extending the previous commit, this adds CSRF to a number of high-value target
pages, including user password changing, dleetion, lockout, etc, and also for
the login page (to avoid a class of attack we probably don't care about).

The CSRF mechanism requires manual addition to each form, but has been
design reviewed by Simon Waters (Surevine Ltd).
parent ce87ab79
...@@ -126,6 +126,7 @@ public class CookieUtils { ...@@ -126,6 +126,7 @@ public class CookieUtils {
Cookie cookie = new Cookie(name, value); Cookie cookie = new Cookie(name, value);
cookie.setMaxAge(maxAge); cookie.setMaxAge(maxAge);
cookie.setPath(path); cookie.setPath(path);
cookie.setHttpOnly(true);
response.addCookie(cookie); response.addCookie(cookie);
} }
} }
...@@ -86,7 +86,21 @@ ...@@ -86,7 +86,21 @@
Map<String, String> errors = new HashMap<String, String>(); Map<String, String> errors = new HashMap<String, String>();
if (ParamUtils.getBooleanParameter(request, "login")) { Boolean login = ParamUtils.getBooleanParameter(request, "login");
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (login) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
login = false;
errors.put("csrf", "CSRF Failure!");
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
if (login) {
String loginUsername = username; String loginUsername = username;
if (loginUsername != null) { if (loginUsername != null) {
loginUsername = JID.escapeNode(loginUsername); loginUsername = JID.escapeNode(loginUsername);
...@@ -182,6 +196,7 @@ ...@@ -182,6 +196,7 @@
<% } catch (Exception e) { Log.error(e); } } %> <% } catch (Exception e) { Log.error(e); } } %>
<input type="hidden" name="login" value="true"> <input type="hidden" name="login" value="true">
<input type="hidden" name="csrf" value="${csrf}">
<div align="center"> <div align="center">
<!-- BEGIN login box --> <!-- BEGIN login box -->
......
...@@ -45,8 +45,20 @@ ...@@ -45,8 +45,20 @@
String password = ParamUtils.getParameter(request,"password"); String password = ParamUtils.getParameter(request,"password");
String passwordConfirm = ParamUtils.getParameter(request,"passwordConfirm"); String passwordConfirm = ParamUtils.getParameter(request,"passwordConfirm");
boolean isAdmin = ParamUtils.getBooleanParameter(request,"isadmin"); boolean isAdmin = ParamUtils.getBooleanParameter(request,"isadmin");
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
Map<String, String> errors = new HashMap<String, String>(); Map<String, String> errors = new HashMap<String, String>();
if (create) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
create = false;
errors.put("csrf", "CSRF Failure!");
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
// Handle a cancel // Handle a cancel
if (cancel) { if (cancel) {
response.sendRedirect("user-summary.jsp"); response.sendRedirect("user-summary.jsp");
...@@ -203,6 +215,7 @@ ...@@ -203,6 +215,7 @@
<% } %> <% } %>
<form name="f" action="user-create.jsp" method="get"> <form name="f" action="user-create.jsp" method="get">
<input type="hidden" name="csrf" value="${csrf}">
<div class="jive-contentBoxHeader"> <div class="jive-contentBoxHeader">
<fmt:message key="user.create.new_user" /> <fmt:message key="user.create.new_user" />
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<%@ page import="org.jivesoftware.openfire.user.UserManager" %> <%@ page import="org.jivesoftware.openfire.user.UserManager" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %> <%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %> <%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="org.jivesoftware.util.CookieUtils" %>
<%@ page import="org.xmpp.packet.JID" %> <%@ page import="org.xmpp.packet.JID" %>
<%@ page import="org.xmpp.packet.StreamError" %> <%@ page import="org.xmpp.packet.StreamError" %>
<%@ page import="java.net.URLEncoder" %> <%@ page import="java.net.URLEncoder" %>
...@@ -39,6 +40,17 @@ ...@@ -39,6 +40,17 @@
boolean cancel = request.getParameter("cancel") != null; boolean cancel = request.getParameter("cancel") != null;
boolean delete = request.getParameter("delete") != null; boolean delete = request.getParameter("delete") != null;
String username = ParamUtils.getParameter(request,"username"); String username = ParamUtils.getParameter(request,"username");
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (delete) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
delete = false;
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
// Handle a cancel // Handle a cancel
if (cancel) { if (cancel) {
...@@ -107,6 +119,7 @@ ...@@ -107,6 +119,7 @@
</c:if> </c:if>
<form action="user-delete.jsp"> <form action="user-delete.jsp">
<input type="hidden" name="csrf" value="${csrf}">
<input type="hidden" name="username" value="<%= StringUtils.escapeForXML(username) %>"> <input type="hidden" name="username" value="<%= StringUtils.escapeForXML(username) %>">
<input type="submit" name="delete" value="<fmt:message key="user.delete.delete" />"> <input type="submit" name="delete" value="<fmt:message key="user.delete.delete" />">
<input type="submit" name="cancel" value="<fmt:message key="global.cancel" />"> <input type="submit" name="cancel" value="<fmt:message key="global.cancel" />">
......
...@@ -45,19 +45,21 @@ ...@@ -45,19 +45,21 @@
Map<String, String> errors = new HashMap<String, String>(); Map<String, String> errors = new HashMap<String, String>();
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf"); Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf"); String csrfParam = ParamUtils.getParameter(request, "csrf");
// Handle a cancel
if (request.getParameter("cancel") != null) {
response.sendRedirect("user-properties.jsp?username=" + URLEncoder.encode(username, "UTF-8"));
return;
}
if (save) { if (save) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) { if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
save = false; save = false;
errors.put("csrf", "CSRF Failure"); errors.put("csrf", "CSRF Failure");
} }
} }
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
// Handle a cancel
if (request.getParameter("cancel") != null) {
response.sendRedirect("user-properties.jsp?username=" + URLEncoder.encode(username, "UTF-8"));
return;
}
// Load the user object // Load the user object
User user = webManager.getUserManager().getUser(username); User user = webManager.getUserManager().getUser(username);
...@@ -101,9 +103,6 @@ ...@@ -101,9 +103,6 @@
return; return;
} }
} }
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
%> %>
<html> <html>
...@@ -157,7 +156,7 @@ ...@@ -157,7 +156,7 @@
<form action="user-edit-form.jsp"> <form action="user-edit-form.jsp">
<input type="hidden" name="csrf" value="<%= StringUtils.escapeForXML(csrfParam) %>"> <input type="hidden" name="csrf" value="${csrf}">
<input type="hidden" name="username" value="<%= StringUtils.escapeForXML(username) %>"> <input type="hidden" name="username" value="<%= StringUtils.escapeForXML(username) %>">
<input type="hidden" name="save" value="true"> <input type="hidden" name="save" value="true">
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<%@ page import="org.jivesoftware.openfire.session.ClientSession" %> <%@ page import="org.jivesoftware.openfire.session.ClientSession" %>
<%@ page import="org.jivesoftware.util.ParamUtils" %> <%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %> <%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="org.jivesoftware.util.CookieUtils" %>
<%@ page import="org.xmpp.packet.JID" %> <%@ page import="org.xmpp.packet.JID" %>
<%@ page import="org.xmpp.packet.StreamError" %> <%@ page import="org.xmpp.packet.StreamError" %>
<%@ page import="java.net.URLEncoder" %> <%@ page import="java.net.URLEncoder" %>
...@@ -50,6 +51,17 @@ ...@@ -50,6 +51,17 @@
if (duration == -2) { if (duration == -2) {
duration = ParamUtils.getIntParameter(request,"duration_custom", -1); duration = ParamUtils.getIntParameter(request,"duration_custom", -1);
} }
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (lock || unlock) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
lock = false;
unlock = false;
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
// Handle a cancel // Handle a cancel
if (cancel) { if (cancel) {
...@@ -146,6 +158,7 @@ ...@@ -146,6 +158,7 @@
<form action="user-lockout.jsp"> <form action="user-lockout.jsp">
<input type="hidden" name="username" value="${usernameHtmlEscaped}"> <input type="hidden" name="username" value="${usernameHtmlEscaped}">
<input type="hidden" name="csrf" value="${csrf}">
<input type="submit" name="unlock" value="<fmt:message key="user.lockout.unlock" />"> <input type="submit" name="unlock" value="<fmt:message key="user.lockout.unlock" />">
<input type="submit" name="cancel" value="<fmt:message key="global.cancel" />"> <input type="submit" name="cancel" value="<fmt:message key="global.cancel" />">
</form> </form>
...@@ -169,6 +182,7 @@ ...@@ -169,6 +182,7 @@
</c:if> </c:if>
<form action="user-lockout.jsp"> <form action="user-lockout.jsp">
<input type="hidden" name="csrf" value="${csrf}">
<% if (LockOutManager.getLockOutProvider().isDelayedStartSupported()) { %> <% if (LockOutManager.getLockOutProvider().isDelayedStartSupported()) { %>
<b><fmt:message key="user.lockout.time.startdelay" /></b><br /> <b><fmt:message key="user.lockout.time.startdelay" /></b><br />
<input type="radio" name="startdelay" value="-1" checked="checked" /> <fmt:message key="user.lockout.time.immediate" /><br /> <input type="radio" name="startdelay" value="-1" checked="checked" /> <fmt:message key="user.lockout.time.immediate" /><br />
......
...@@ -35,6 +35,17 @@ ...@@ -35,6 +35,17 @@
String username = ParamUtils.getParameter(request,"username"); String username = ParamUtils.getParameter(request,"username");
String password = ParamUtils.getParameter(request,"password"); String password = ParamUtils.getParameter(request,"password");
String passwordConfirm = ParamUtils.getParameter(request,"passwordConfirm"); String passwordConfirm = ParamUtils.getParameter(request,"passwordConfirm");
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
if (update) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
update = false;
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
// Handle a cancel // Handle a cancel
if (cancel) { if (cancel) {
...@@ -133,6 +144,7 @@ ...@@ -133,6 +144,7 @@
<form action="user-password.jsp" name="passform" method="post"> <form action="user-password.jsp" name="passform" method="post">
<input type="hidden" name="username" value="<%=StringUtils.escapeForXML(username) %>"> <input type="hidden" name="username" value="<%=StringUtils.escapeForXML(username) %>">
<input type="hidden" name="csrf" value="${csrf}">
<fieldset> <fieldset>
<legend><fmt:message key="user.password.change" /></legend> <legend><fmt:message key="user.password.change" /></legend>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment