Saturday, September 1, 2018

Handling XSS and XSSF in Liferay

To handle Cross Site Scripting and Cross Site Script Forgery issues in Liferay, Using URL filters of Liferay we can hadle CSS and CSSF issues in Liferay. I have written a hook to handle the both issues below are the more details.

 liferayhook.xml :-

<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">

<hook>
    <!-- Secure URL Filter -->
    <servlet-filter>
        <servlet-filter-name>UrlFilterr</servlet-filter-name>
        <servlet-filter-impl>com.filter.URLFilterHook</servlet-filter-impl>
    </servlet-filter>
   
    <!-- Secure URL Filter Mapping -->
    <servlet-filter-mapping>
        <servlet-filter-name>UrlFilterr</servlet-filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </servlet-filter-mapping>
</hook>


URL Filter Class :-
com.filter.URLFilterHook
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.util.PortalUtil;


import java.io.IOException;

import javax.portlet.PortletException;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;

public class URLFilterHook implements Filter{

    private static final Log _log = LogFactoryUtil.getLog(URLFilterHook.class);

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        _log.info("Init method in Filter");
    }
  
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
            FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
        String currentPortalURL = PortalUtil.getCurrentCompleteURL(httpRequest);
        String accessDeniedReason;
            // Cross Site Scripting Start
            RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest)httpRequest);
              
            // Cross site forgery  - START
          
            String source = httpRequest.getHeader("Origin");       
            source = httpRequest.getHeader("Referer");
             if(!this.isBlank(source) && !isValidURL(source)) {
                accessDeniedReason = "New Invalid REFERER " + source + " request headers are both not same so we block the request !";
                httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedReason);
                return;
            }
            filterChain.doFilter(requestWrapper,httpResponse); 
    }

    private static boolean getValidURL(String currentURL)
               throws PortletException {
        boolean isvalidURL = false;
        try{
            String fileNameFromURL = FilenameUtils.getName(currentURL);
            if(currentURL.endsWith(fileNameFromURL) && FilenameUtils.isExtension(currentURL, FilterConstants.File_Extensions)){
                isvalidURL = true;
            }
        }catch(Exception e){
            _log.error("ERROR :- URLFilterHook while getting URLFilterHook ");
        }
        return isvalidURL;
    }
  
    private boolean isBlank(String source) {
        return source == null || source.trim().isEmpty();
    }
  
    private boolean isValidURL(String source) {
        String excludeURLString = FilterConstants.Exclude_URL_String;
        String[] excludeURLs = excludeURLString.split(",");
        boolean isValidURL = false;
        for(int i = 0; i < excludeURLs.length; i++) {
            if(source.startsWith(excludeURLs[i])) {              
                isValidURL = true;
                break;
            }
        }
        return isValidURL;
    }
    @Override
    public void destroy() {
    }
}

Request Wrapper Class :-
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;

public class RequestWrapper extends HttpServletRequestWrapper {
   
    private static Log _log = LogFactoryUtil.getLog(RequestWrapper.class);

    public RequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }
   
    private static Pattern[] patterns = new Pattern[] {
            // Script fragments
            Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
            // src='...'
            Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // lonely script tags
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // eval(...)
            Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // expression(...)
            Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // javascript:...
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            // vbscript:...
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            // onload(...)=...
            Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
           
            Pattern.compile("<(.*?)</(.*?)>",Pattern.CASE_INSENSITIVE),           
            //Avoid End Tag
            Pattern.compile("</(.*?)>",Pattern.CASE_INSENSITIVE),
            //Avoid Closing Tag
            Pattern.compile("<(.*?)>",Pattern.CASE_INSENSITIVE),
            //Avoid anything between script tags
            Pattern.compile("<script(.*?)</script>",Pattern.CASE_INSENSITIVE),
            //Avoid iframe tags
            Pattern.compile("<iframe(.*?)",Pattern.CASE_INSENSITIVE),
            //Avoid anything in a src=’…’ type of expression
            Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)           
           
       };
   
    @Override
    public String[] getParameterValues(String parameter) {
       
        String[] values = super.getParameterValues(parameter);

        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = stripXSS(values[i]);
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        value = stripXSS(value);   
        return value;      
    }
   
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return stripXSS(value);

    }
   
    public static String stripXSS(String value) {
        if (value != null) {           
            // Avoid null characters
            value = value.replaceAll("\0", "");
            // Remove all sections that match a pattern
            for (Pattern scriptPattern : patterns){               
                value = scriptPattern.matcher(value).replaceAll("");
            }
        }
        return value;
    }
}

Liferay DXP JNDI Data Source Cofiguration

 This Blog will help us to learn about the JNDI Data Source Configuration in Liferay DXP. We have tested this with Liferay 7.3 with Tomcat. ...