CSRF.java

package sk.iway.iwcm.system.stripes;

import net.sourceforge.stripes.util.CryptoUtil;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.RequestBean;
import sk.iway.iwcm.Tools;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import java.util.ArrayList;
import java.util.List;

/**
 *  CSRF.java - trieda pre zakladnu ochranu pred CSRF utokmi
 *
 *@Title        webjet7
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2014
 *@author       $Author: jeeff jeeff $
 *@version      $Revision: 1.3 $
 *@created      Date: 5.3.2014 16:54:56
 *@modified     $Date: 2004/08/16 06:26:11 $
 */
public class CSRF
{
	private static final String SESSION_KEY = "webjet-csrf-tokens-list";

	private static final String PARAMETER_NAME = "__token";


	/**
	 * Zapise input pole do formularu, vola sa priamo vo FormTag
	 * @param session
	 * @param out
	 */
	public static void writeCsrfTokenInputFiled(HttpSession session, JspWriter out)
	{
		try
		{
			out.write(getCsrfTokenInputFiled(session));
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
	}

	/**
	 * Vrati CSRF input field ktory sa vlozi do formularu
	 * @param session
	 * @return
	 */
	public static String getCsrfTokenInputFiled(HttpSession session)
	{
		return getCsrfTokenInputFiled(session, true);
	}

	/**
	 * Vrati CSRF input field ktory sa vlozi do formularu
	 * @param session
	 * @param saveToSession - ak je true, aj sa ulozi na verifikaciu, false sa pouziva pre obfuscovanu verziu pre boty vo WriteTagu
	 * @return
	 */
	public static String getCsrfTokenInputFiled(HttpSession session, boolean saveToSession)
	{
		StringBuilder out = new StringBuilder();

		String token = getCsrfToken(session, saveToSession);

		out.append("<input type=\"hidden\" name=\"");
		out.append(PARAMETER_NAME);
		out.append("\" value=\"");
		out.append(token);
		out.append("\" />");

		return out.toString();
	}

	/**
	 * Vrati CSRF token pre vlozenie do formularu
	 * @param session
	 * @param saveToSession
	 * @return
	 */
	public static String getCsrfToken(HttpSession session, boolean saveToSession)
	{
		String token = CryptoUtil.encrypt(session.getId()+"-"+String.valueOf(Tools.getNow()));
		if (saveToSession) setTokenToSession(session, token);

		return token;
	}


	/**
	 * Verifikuje a nasledne zmaze aby sa znova nedal pouzit token v session
	 * @param request
	 * @return
	 */
	public static boolean verifyTokenAndDeleteIt(HttpServletRequest request)
	{
		if ("true".equals(request.getSession().getAttribute("WriteTag.disableSpamProtectionJavascript"))) {
			return true;
		}

		String[] parameterValues = request.getParameterValues(PARAMETER_NAME);
		if (parameterValues != null) {
			for (String parameterValue : parameterValues) {
				if (verifyToken(request.getSession(), parameterValue, true)) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Verifikuje a nasledne zmaze aby sa znova nedal pouzit token v session
	 * @param session
	 * @param tokenValue - CSRF token
	 * @return
	 */
	public static boolean verifyTokenAndDeleteIt(HttpSession session, String tokenValue)
	{
		return verifyToken(session, tokenValue, true);
	}

	/**
	 * Verifikuje token v session, pouzit "iba" pri Ajax-ovych volaniach,
	 * Token sa nemaze po pouziti.
	 *
	 * @param request
	 * @return
	 */
	public static boolean verifyTokenAjax(HttpServletRequest request)
	{
		String tokenValue = request.getParameter(PARAMETER_NAME);
		return verifyTokenAjax(request.getSession(), tokenValue);
	}

	/**
	 * Verifikuje token v session, pouzit "iba" pri Ajax-ovych volaniach,
	 * Token sa nemaze po pouziti.
	 * @param session
	 * @param tokenValue - hodnota tokenu
	 * @return
	 */
	public static boolean verifyTokenAjax(HttpSession session, String tokenValue)
	{
		return verifyToken(session, tokenValue, false);
	}

	private static boolean verifyToken(HttpSession session, String tokenValue, boolean deleteToken)
	{
		if (Tools.isEmpty(tokenValue))
		{
			RequestBean.addError("CSRF token is empty");
			return false;
		}

		@SuppressWarnings("unchecked")
		List<String> tokenList = (List<String>)session.getAttribute(SESSION_KEY);
		if (tokenList == null)
		{
			RequestBean.addError("CSRF list in session is empty");
			return false;
		}

		boolean tokenFound = false;
		try
		{
			//tu nam hrozi zmena pola, pri standardnej praci pouzivatela by ale problem byt nemal
			int position = 0;
			for (position = 0; position<tokenList.size(); position++)
			{
				if (tokenValue.equals(tokenList.get(position)))
				{
					tokenFound = true;
					if(deleteToken)
						tokenList.remove(position);
					break;
				}
			}

			if (tokenFound==false)
			{
				RequestBean.addError("CSRF token not found, token="+tokenValue+" tokenList.size="+tokenList.size());
			}
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
			RequestBean.addError("CSRF token error "+e.getMessage());
		}

		return tokenFound;
	}

	private static void setTokenToSession(HttpSession session, String token)
	{
		@SuppressWarnings("unchecked")
		List<String> tokenList = (List<String>)session.getAttribute(SESSION_KEY);
		if (tokenList == null)
		{
			tokenList = new ArrayList<String>();
			session.setAttribute(SESSION_KEY, tokenList);
		}

		tokenList.add(token);

		try
		{
			if (tokenList.size()> Constants.getInt("csrfMaxTokensInSession"))
			{
				tokenList.remove(0);
			}
		}
		catch (Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
	}

	/**
	 * Vrati meno parametra aj s CSRF tokenom pre vlozenie do url ako parameter
	 * @param session
	 * @param saveToSession
	 * @return
	 */
	public static String getCSRFTokenQuery(HttpSession session, boolean saveToSession){
		return PARAMETER_NAME + "=" + getCsrfToken(session,saveToSession);
	}

	/**
	 * Returns CSRF token parameter name
	 * @return
	 */
	public static String getParameterName() {
		return PARAMETER_NAME;
	}
}