001package org.apache.wiki.http.filter;
002
003import org.apache.logging.log4j.LogManager;
004import org.apache.logging.log4j.Logger;
005import org.apache.wiki.api.core.Engine;
006import org.apache.wiki.api.core.Session;
007import org.apache.wiki.api.spi.Wiki;
008
009import javax.servlet.Filter;
010import javax.servlet.FilterChain;
011import javax.servlet.FilterConfig;
012import javax.servlet.ServletException;
013import javax.servlet.ServletRequest;
014import javax.servlet.ServletResponse;
015import javax.servlet.http.HttpServletRequest;
016import javax.servlet.http.HttpServletResponse;
017import java.io.IOException;
018
019
020/**
021 * CSRF protection Filter which uses the synchronizer token pattern – an anti-CSRF token is created and stored in the
022 * user session and in a hidden field on subsequent form submits. At every submit the server checks the token from the
023 * session matches the one submitted from the form.
024 */
025public class CsrfProtectionFilter implements Filter {
026
027    private static final Logger LOG = LogManager.getLogger( CsrfProtectionFilter.class );
028
029    public static final String ANTICSRF_PARAM = "X-XSRF-TOKEN";
030
031    /** {@inheritDoc} */
032    @Override
033    public void init( final FilterConfig filterConfig ) {
034    }
035
036    /** {@inheritDoc} */
037    @Override
038    public void doFilter( final ServletRequest request, final ServletResponse response, final FilterChain chain ) throws IOException, ServletException {
039        if( isPost( ( HttpServletRequest ) request ) ) {
040            final Engine engine = Wiki.engine().find( request.getServletContext(), null );
041            final Session session = Wiki.session().find( engine, ( HttpServletRequest ) request );
042            if( !requestContainsValidCsrfToken( request, session ) ) {
043                LOG.error( "Incorrect {} param with value '{}' received for {}",
044                           ANTICSRF_PARAM, request.getParameter( ANTICSRF_PARAM ), ( ( HttpServletRequest ) request ).getPathInfo() );
045                ( ( HttpServletResponse ) response ).sendRedirect( "/error/Forbidden.html" );
046                return;
047            }
048        }
049        chain.doFilter( request, response );
050    }
051
052    public static boolean isCsrfProtectedPost( final HttpServletRequest request ) {
053        if( isPost( request ) ) {
054            final Engine engine = Wiki.engine().find( request.getServletContext(), null );
055            final Session session = Wiki.session().find( engine, request );
056            return requestContainsValidCsrfToken( request, session );
057        }
058        return false;
059    }
060
061    private static boolean requestContainsValidCsrfToken( final ServletRequest request, final Session session ) {
062        return session.antiCsrfToken().equals( request.getParameter( ANTICSRF_PARAM ) );
063    }
064
065    static boolean isPost( final HttpServletRequest request ) {
066        return "POST".equalsIgnoreCase( request.getMethod() );
067    }
068
069    /** {@inheritDoc} */
070    @Override
071    public void destroy() {
072    }
073
074}