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;
    }
}

Thursday, April 12, 2018

LogIn using custom portlet in Liferay

Below code will help you to login to the Liferay Portal using custom portlet or we can say custom logic

JSP Page :-

<portlet:actionURL var="createNewIndex" name="createNewIndex">
<portlet:param name="action" value="customLogin" />
</portlet:actionURL>

<form action="<%=createNewIndex %>" method="post">
  Email: <input type="text" name="email">  </br>
  Password: <input type="text" name="pasword">  </br>

  <input type="submit" value="Submit">
</form>


Controller:-

package com.securtiy.sample;

import com.liferay.portal.CompanyMaxUsersException;
import com.liferay.portal.CookieNotSupportedException;
import com.liferay.portal.NoSuchUserException;
import com.liferay.portal.PasswordExpiredException;
import com.liferay.portal.UserEmailAddressException;
import com.liferay.portal.UserLockoutException;
import com.liferay.portal.UserPasswordException;
import com.liferay.portal.UserScreenNameException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.model.CompanyConstants;
import com.liferay.portal.security.auth.AuthException;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.portal.util.PortalUtil;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.portlet.bind.annotation.ActionMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;

@Controller
@RequestMapping("VIEW")
public class PortletViewController {

private static Log logger = LogFactoryUtil.getLog(PortletViewController.class);
private static final String NAMESPACE_SERVLET_REQUEST_FQCN = "com.liferay.portal.servlet.NamespaceServletRequest";
private String authType ="emailAddress";
private String handleLabel;


@RenderMapping
public String question(RenderRequest renderRequest, RenderResponse renderResponse, Model model){
System.out.println("Inside Render Method");
return "sample/view";
}

@ActionMapping(params = "action=createIndex")
public void authenticate(Model model,ActionRequest actionRequest,ActionResponse actionResponse) {

ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(actionRequest);
System.out.println("---------- Testing for Log in  ---------");
// If the request object is a wrapper that handles special namespacing considerations for portlet session
// attributes, then get the inner-most wrapped request. This will ensure that the following call to
// LoginUtil.login(...) will be able to work with a session that has an attribute map shared by the portal.
if (httpServletRequest.getClass().getName().equals(NAMESPACE_SERVLET_REQUEST_FQCN)) {

while (httpServletRequest instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper httpServletRequestWrapper = (HttpServletRequestWrapper) httpServletRequest;
httpServletRequest = (HttpServletRequest) httpServletRequestWrapper.getRequest();
}
}

HttpServletResponse httpServletResponse = PortalUtil.getHttpServletResponse(actionResponse);

String handle = "test@liferay.com"; // hardcode email for testing purpose
String password = "test"; // hardcoded password for testing purpose
boolean rememberMe = false; // made remember me check box as false
boolean authenticated = false; // boolean value to check authentication is true or false

try {
                // Method which will use to perform the login
LoginUtilCompat.invokeLogin(httpServletRequest, httpServletResponse, handle, password, rememberMe,authType);
authenticated = true;
}
catch (Exception e) {
logger.error(e);
}

if (authenticated) {
System.out.println("Authentication is sucess");
/*
                       We must redirect the Page to particular location then only this method will work otherwise it will not work sometimes
try {
ExternalContext externalContext = liferayFacesContext.getExternalContext();

if (PropsValuesCompat.PORTAL_JAAS_ENABLE) {
externalContext.redirect(themeDisplay.getPathMain() + "/portal/protected");
}
else {
String redirect = ParamUtil.getString(actionRequest, "redirect");

if (Validator.isNotNull(redirect)) {
redirect = PortalUtilCompat.escapeRedirect(redirect);

if (!redirect.startsWith(Http.HTTP)) {
redirect = getCompleteRedirectURL(httpServletRequest, redirect);
}

externalContext.redirect(redirect);
}
else {
boolean doActionAfterLogin = ParamUtil.getBoolean(actionRequest, "doActionAfterLogin");

if (doActionAfterLogin) {
return;
}
else {

redirect = getCompleteRedirectURL(httpServletRequest, themeDisplay.getPathMain());
externalContext.redirect(redirect);
}
}
}
}
catch (IOException e) {
logger.error(e);
liferayFacesContext.addGlobalUnexpectedErrorMessage();
}
*/}
else {
System.out.println("Authentication Failed");
}
}
}


Util Class :
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.liferay.portal.kernel.util.ClassResolverUtil;
import com.liferay.portal.kernel.util.MethodKey;
import com.liferay.portal.kernel.util.PortalClassInvoker;

public class LoginUtilCompat {
// Private Constants
private static final String LOGIN_UTIL_FQCN = "com.liferay.portlet.login.util.LoginUtil";
private static final String LOGIN_METHOD = "login";
private static final Class[] LOGIN_PARAM_TYPES = new Class[] {
HttpServletRequest.class, HttpServletResponse.class, String.class, String.class, boolean.class, String.class
};
   // Method which is used to Invoke the Login
public static Object invokeLogin(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
String handle, String password, boolean rememberMe, String authType) throws Exception {
Class loginUtilClass = ClassResolverUtil.resolveByPortalClassLoader(LOGIN_UTIL_FQCN);
MethodKey methodKey = new MethodKey(loginUtilClass, LOGIN_METHOD, LOGIN_PARAM_TYPES);

return PortalClassInvoker.invoke(false, methodKey, httpServletRequest, httpServletResponse, handle, password,
rememberMe, authType);
}
}

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. ...