1
0
mirror of synced 2026-05-22 21:33:16 +00:00

SEC-2325 Added JSP tags for CSRF meta tags and form fields

This commit is contained in:
beamerblvd
2014-02-06 21:56:47 -06:00
committed by Rob Winch
parent 26cee61b98
commit a3e0475998
9 changed files with 457 additions and 6 deletions
@@ -1,3 +1,19 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.taglibs;
import org.apache.commons.logging.Log;
@@ -6,7 +22,7 @@ import org.apache.commons.logging.LogFactory;
import javax.servlet.jsp.tagext.Tag;
/**
* internal cconfiguration class for taglibs.
* internal configuration class for taglibs.
*
* Not for public use.
*
@@ -0,0 +1,49 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.taglibs.csrf;
import org.springframework.security.web.csrf.CsrfToken;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;
/**
* An abstract tag for handling CSRF operations.
*
* @since 3.2.1
* @author Nick Williams
*/
public abstract class AbstractCsrfTag extends TagSupport {
@Override
public int doEndTag() throws JspException {
CsrfToken token = (CsrfToken)this.pageContext.getRequest().getAttribute(CsrfToken.class.getName());
if (token != null) {
try {
this.pageContext.getOut().write(this.handleToken(token));
} catch (IOException e) {
throw new JspException(e);
}
}
return EVAL_PAGE;
}
protected abstract String handleToken(CsrfToken token);
}
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.taglibs.csrf;
import org.springframework.security.web.csrf.CsrfToken;
/**
* A JSP tag that prints out a hidden form field for the CSRF token. See the JSP Tab Library documentation for more
* information.
*
* @since 3.2.1
* @author Nick Williams
*/
public class FormFieldTag extends AbstractCsrfTag {
@Override
public String handleToken(CsrfToken token) {
return "<input type=\"hidden\" name=\"" + token.getParameterName() + "\" value=\"" + token.getToken() +
"\" />\n";
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.taglibs.csrf;
import org.springframework.security.web.csrf.CsrfToken;
/**
* A JSP tag that prints out a meta tags holding the CSRF form field name and token value for use in JavaScrip code.
* See the JSP Tab Library documentation for more information.
*
* @since 3.2.1
* @author Nick Williams
*/
public class MetaTagsTag extends AbstractCsrfTag {
@Override
public String handleToken(CsrfToken token) {
return "<meta name=\"_csrf_parameter\" content=\"" + token.getParameterName() + "\" />\n" +
" <meta name=\"_csrf_header\" content=\"" + token.getHeaderName() + "\" />\n" +
" <meta name=\"_csrf\" content=\"" + token.getToken() + "\" />\n";
}
}
@@ -1,5 +1,19 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright 2002-2014 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<taglib 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-jsptaglibrary_2_0.xsd"
@@ -177,4 +191,35 @@
</attribute>
</tag>
<tag>
<description><![CDATA[
If CSRF protection is enabled, this tag inserts a hidden form field with the correct name and value for the
CSRF protection token. If CSRF protection is not enabled, this tag outputs nothing. Normally Spring Security
automatically inserts this form field for any <form:form> tags, but if for some reason you cannot use
<form:form> this tag is a handy replacement. You should place this tag within an HTML <form></form> block,
where you would normally place other <input>s. Do NOT place this tag within a Spring <form:form></form:form>
block—Spring Security handles Spring forms automatically.
]]></description>
<name>csrfField</name>
<tag-class>org.springframework.security.taglibs.csrf.FormFieldTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<description><![CDATA[
If CSRF protection is enabled, this tag inserts meta tags containing the CSRF protection token form
field and header names and CSRF protection token value. These tags are useful for employing CSRF protection
within JavaScript in your applications. You should place this tag within an HTML <head></head> block, where
you would normally place other meta tags. Once you use this tag, you can access the form field name using
the JQuery $("meta[name='_csrf_parameter']").attr("content") and the header name using
$("meta[name='_csrf_header']").attr("content"). Likewise, you can access the token value with
$("meta[name='_csrf']").attr("content"). You should use a form field when creating and submitting forms from
JavaScript, and you should use a header when sending AJAX requests. If CSRF protection is not enabled, this
tag outputs nothing.
]]></description>
<name>csrfMetaTags</name>
<tag-class>org.springframework.security.taglibs.csrf.MetaTagsTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
@@ -0,0 +1,91 @@
package org.springframework.security.taglibs.csrf;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockPageContext;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.UnsupportedEncodingException;
import static org.junit.Assert.*;
/**
* @author Nick Williams
*/
public class AbstractCsrfTagTests {
public MockTag tag;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp() {
MockServletContext servletContext = new MockServletContext();
this.request = new MockHttpServletRequest(servletContext);
this.response = new MockHttpServletResponse();
MockPageContext pageContext = new MockPageContext(servletContext, this.request, this.response);
this.tag = new MockTag();
this.tag.setPageContext(pageContext);
}
@Test
public void testDoEndTag01() throws JspException, UnsupportedEncodingException {
this.tag.handleReturn = "fooBarBazQux";
int returned = this.tag.doEndTag();
assertEquals("The returned value is not correct.", TagSupport.EVAL_PAGE, returned);
assertEquals("The output value is not correct.", "", this.response.getContentAsString());
}
@Test
public void testDoEndTag02() throws JspException, UnsupportedEncodingException {
CsrfToken token = new DefaultCsrfToken("X-Csrf-Token", "_csrf", "abc123def456ghi789");
this.request.setAttribute(CsrfToken.class.getName(), token);
this.tag.handleReturn = "fooBarBazQux";
int returned = this.tag.doEndTag();
assertEquals("The returned value is not correct.", TagSupport.EVAL_PAGE, returned);
assertEquals("The output value is not correct.", "fooBarBazQux", this.response.getContentAsString());
assertSame("The token is not correct.", token, this.tag.token);
}
@Test
public void testDoEndTag03() throws JspException, UnsupportedEncodingException {
CsrfToken token = new DefaultCsrfToken("X-Csrf-Token", "_csrf", "abc123def456ghi789");
this.request.setAttribute(CsrfToken.class.getName(), token);
this.tag.handleReturn = "<input type=\"hidden\" />";
int returned = this.tag.doEndTag();
assertEquals("The returned value is not correct.", TagSupport.EVAL_PAGE, returned);
assertEquals("The output value is not correct.", "<input type=\"hidden\" />",
this.response.getContentAsString());
assertSame("The token is not correct.", token, this.tag.token);
}
private static class MockTag extends AbstractCsrfTag {
private CsrfToken token;
private String handleReturn;
@Override
protected String handleToken(CsrfToken token) {
this.token = token;
return this.handleReturn;
}
}
}
@@ -0,0 +1,45 @@
package org.springframework.security.taglibs.csrf;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import static org.junit.Assert.*;
/**
* @author Nick Williams
*/
public class FormFieldTagTests {
public FormFieldTag tag;
@Before
public void setUp() {
this.tag = new FormFieldTag();
}
@Test
public void testHandleToken01() {
CsrfToken token = new DefaultCsrfToken("X-Csrf-Token", "_csrf", "abc123def456ghi789");
String value = this.tag.handleToken(token);
assertNotNull("The returned value should not be null.", value);
assertEquals("The output is not correct.",
"<input type=\"hidden\" name=\"_csrf\" value=\"abc123def456ghi789\" />\n",
value);
}
@Test
public void testHandleToken() {
CsrfToken token = new DefaultCsrfToken("X-Csrf-Token", "csrfParameter", "fooBarBazQux");
String value = this.tag.handleToken(token);
assertNotNull("The returned value should not be null.", value);
assertEquals("The output is not correct.",
"<input type=\"hidden\" name=\"csrfParameter\" value=\"fooBarBazQux\" />\n",
value);
}
}
@@ -0,0 +1,49 @@
package org.springframework.security.taglibs.csrf;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import static org.junit.Assert.*;
/**
* @author Nick Williams
*/
public class MetaTagsTagTests {
public MetaTagsTag tag;
@Before
public void setUp() {
this.tag = new MetaTagsTag();
}
@Test
public void testHandleToken01() {
CsrfToken token = new DefaultCsrfToken("X-Csrf-Token", "_csrf", "abc123def456ghi789");
String value = this.tag.handleToken(token);
assertNotNull("The returned value should not be null.", value);
assertEquals("The output is not correct.",
"<meta name=\"_csrf_parameter\" content=\"_csrf\" />\n" +
" <meta name=\"_csrf_header\" content=\"X-Csrf-Token\" />\n" +
" <meta name=\"_csrf\" content=\"abc123def456ghi789\" />\n",
value);
}
@Test
public void testHandleToken02() {
CsrfToken token = new DefaultCsrfToken("csrfHeader", "csrfParameter", "fooBarBazQux");
String value = this.tag.handleToken(token);
assertNotNull("The returned value should not be null.", value);
assertEquals("The output is not correct.",
"<meta name=\"_csrf_parameter\" content=\"csrfParameter\" />\n" +
" <meta name=\"_csrf_header\" content=\"csrfHeader\" />\n" +
" <meta name=\"_csrf\" content=\"fooBarBazQux\" />\n",
value);
}
}