Cross-site request forgery.

This can occur if you are logged into a site like your on-line bank, and in a different tab of the browser you are surfing the interweb.

While surfing, you navigate to a page that contains an image tag with a carefully crafted URL. The URL is an instruction to transfer money from your bank account to the hacker's account. Because you are logged into your on-line bank, the fake URL is taken as an authenticated request.

To defend against this, each form that your web application uses should contain some random token which is checked on the server before the application takes any further action. The token needs to change at least every session.
So, when a user logs in, put the random token in the session and each time you render a form, add the same token as a hidden parameter. A servlet filter can then check that the two match.
If you need your web app to be stateless, you can put the random token into a cookie instead of the session.

Consider using CSRFGuard.

Also, if you only update data on the server in response to POST requests, you reduce the attack surface of your site. For example, hackers cannot use image tags to attack, as these do GET requests.

A neat implementation of CSRF protection, using Spring is on Michael Simons' website.

If you don't like Spring, you can do the same in a servlet filter:

package uk.co.donaldhorrell.common;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;


public class CSRFFilter implements Filter
{
	private static Logger LOGGER = Logger.getLogger(CSRFFilter.class);

	/** Used for the request parameter name for the CSRF token. Also the session attr name. */
	public static final String CSRF_TOKEN_NAME = "csrfToken";
	private static final List METHODS_TO_CHECK = Collections.unmodifiableList(Arrays.asList("POST", "PUT", "DELETE"));
	private static final SecureRandom RANDOM = new SecureRandom();
	private static final Collection IGNORE_URLS;		// URLs that do not get checked.

	static
	{
		Collection temp = new ArrayList();
		temp.add("j_security_check");
		
		IGNORE_URLS = Collections.unmodifiableCollection(temp);
	}
	
	
	@Override
	public void destroy() 
	{
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException 
	{
	    HttpServletRequest theRequest = (HttpServletRequest)request;
	    HttpServletResponse theResponse = (HttpServletResponse)response;
	    
	    if (theRequest.getServletPath() != null && theRequest.getServletPath().indexOf(".") > 0)
	    {
	        String action  = theRequest.getServletPath().substring(1, theRequest.getServletPath().lastIndexOf("."));
	        //if(LOGGER.isDebugEnabled()) {LOGGER.debug("CSRFFilter.doFilter() : Action[" + action + "] method["+theRequest.getMethod()+"].");}
	    
	        // Only remove session attributes for specific URIs
	        if (!IGNORE_URLS.contains(action))
	        {
	        	if(!isTokenValid(theRequest))
	        	{
                    		// Report error to browser.
        			theResponse.addHeader("X-InvalidCSRFToken", Boolean.toString(true));
        			theResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
                    		return;
	        	}
	        }
	    }
	    
	    // Execute the chain
	    filterChain.doFilter(request, response);
		
	}

	protected boolean isTokenValid(HttpServletRequest request)
	{
		String method = request.getMethod();
		if(method == null)
		{
			method = "";
		}
		else
		{
			method = method.toUpperCase();
		}
		HttpSession session = request.getSession(false);
		if(session == null)
		{
    			// Log the URL and method (POST, GET etc.).
            		LOGGER.error("CSRFFilter.isTokenValid() : No session for URL["+request.getServletPath()+"] method["+method+"].");
			return(false);
		}
		
		String requestToken = request.getParameter(CSRF_TOKEN_NAME);
		String sessionToken = (String)session.getAttribute(CSRF_TOKEN_NAME);
		
		if(METHODS_TO_CHECK.contains(method))
		{
			if(requestToken != null && requestToken.equals(sessionToken))
			{
				// Request token matches session token.
				return(true);
			}
			else
			{
	            		LOGGER.error("CSRFFilter.isTokenValid() : No valid token for URL["+request.getServletPath()+"] method["+method+"] reqestToken["+requestToken+"] sessionToken["+sessionToken+"].");
                		return(false);
			}
		}
		else
		{
			// Don't check GET methods.
			return(true);
		}
	}
	
	@Override
	public void init(FilterConfig arg0) throws ServletException 
	{
	}
	
	/** Generates a secure random token. */
	public static String generateToken() 
	{
		final byte[] bytes = new byte[32];
		RANDOM.nextBytes(bytes);
		return Base64.encodeBase64URLSafeString(bytes);
	}
}