Wednesday, March 1, 2017

Could not verify the provided CSRF token. Using only xml

Leave a Comment

I'm in the process of learning Spring framework, and I'm using xml configuration file for the security:

<security:http once-per-request="false" use-expressions="true">     <!-- <security:intercept-url pattern="/admin" access="hasAuthority('ROLE_ADMIN')" /> -->     <security:intercept-url pattern="/admin" access="permitAll" />     <security:intercept-url pattern="/createoffer" access="isAuthenticated()" />     <security:intercept-url pattern="/docreateoffer" access="isAuthenticated()" />     <security:intercept-url pattern="/offercreated" access="isAuthenticated()" />     <security:intercept-url pattern="/" access="permitAll" />     <security:intercept-url pattern="/denied" access="permitAll" />          <security:intercept-url pattern="/loggedout" access="permitAll" />     <security:intercept-url pattern="/newaccount" access="permitAll" />     <security:intercept-url pattern="/createaccount" access="permitAll" />     <security:intercept-url pattern="/accountcreated" access="permitAll" />     <security:intercept-url pattern="/static/**" access="permitAll" />     <security:intercept-url pattern="/login" access="permitAll" />     <security:intercept-url pattern="/offers" access="permitAll" />     <security:intercept-url pattern="/**" access="denyAll" />     <security:form-login login-page="/login" authentication-failure-url="/login?error=true" />     <security:logout logout-success-url="/loggedout" logout-url="/logout"/>     <security:remember-me key="offersAppKey"         user-service-ref="jdbcUserService"         remember-me-parameter="remember-me" token-validity-seconds="1209600" /> </security:http> 

and in my login form:

<form name='f' action='${pageContext.request.contextPath}/login'     method='POST'>     <table>         <tr>             <td>User:</td>             <td><input type='text' name='username' value=''></td>         </tr>         <tr>             <td>Password:</td>             <td><input type='password' name='password' /></td>         </tr>         <tr>             <td>Remember Me</td>             <td><input type="checkbox" checked="checked" name="remember-me" /></td>         </tr>         <tr>             <td colspan='2'><input name="submit" type="submit"                 value="Login" /></td>         </tr>         <input type="hidden" name="${_csrf.parameterName}"             value="${_csrf.token}" />     </table> </form> 

But after I log in and some time passes (I set the session timeout to be 1 minute in the web.xml) I get this error in the browser:

Could not verify the provided CSRF token because your session was not found.

So far, I couldn't find any post that only uses the xml file for the configuration. It's all about configuring using java and some other answers suggest that to include csrf hidden input in the login form, which I already do. Any idea how to fix this issue ?

P.S.: the web.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>spring-tutorial-51</display-name> <description>Spring tutorial web app</description> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>offers</display-name> <servlet-name>offers</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>offers</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <resource-ref> <description>DB Connection</description> <res-ref-name>jdbc/springtutorial</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:com/myproject/spring/web/config/dao-context.xml classpath:com/myproject/spring/web/config/service-context.xml classpath:com/myproject/spring/web/config/security-context.xml </param-value> </context-param> <filter> <display-name>springSecurityFilterChain</display-name> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <session-config> <session-timeout>1</session-timeout> </session-config> </web-app>

I added the following config to my security config file.

I added this to my security-context.xml which is a config file for security.

<security:remember-me key="offersAppKey" user-service-ref="jdbcUserService" remember-me-parameter="remember-me" token-validity-seconds="1209600" /> 

And I expect to have the token valid after 1 minute for 1209600 seconds.

3 Answers

Answers 1

I don't understand where is the suprising part here? You said that

I set the session timeout to be 1 minute in the web.xml

Where else the server can store its copy of your CSRF-token except for your session? So when in a minute your session expires, the server can't find its copy to match CSRF-token in your request and raises such an exception.

I think you can see relevant code around https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java#L113 and https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.java#L75

So to fix the issue just increase your session timeout. (Timeout of only 1 minute seems incredibly strict to me. I haven't seen anything less than 10 minutes in real life even in "secure" places and typically much more.)

Answers 2

Is this your entire file for spring xml configuration? Before starting your xml, try adding this block at start of your xml block that you have provided.

<security:http csrf disabled="true">         <security:intercept-url pattern="/*" access="ROLE_USER" />         <security:form-login/>  </security:http> <security:http auto-config:"true"/> 

This enables the http to adapt property of preventing Request forgery if any claimed from host's IP address.

Since this Security vulnerability is raised, turning it off, will make it unsafe from host's browser, which in this case is you. Doing this is normal, and will unblock you. Althouh i have never seen the issue

session not found 

If request forgery is turned off , it should be good enough to recognize session SID for system. Also remove this once you add this in xml, you no longer need it as hidden parameter

        <input type="hidden" name="${_csrf.parameterName}"             value="${_csrf.token}" /> 

Hope it helps.

Answers 3

After one minute when the session expires, it is expected to fail CSRF. What user need to do in that case is refresh the browser before hitting login(or any non-get) request. And if the user doesnt do that you can reload the login page with some frieldly message.

For better user experience, you may try some AJAX based UI technologies, if possible..

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment