org.wso2.carbon.ui.valve.XSSValve.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.ui.valve.XSSValve.java

Source

/*
 * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.wso2.carbon.ui.valve;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.utils.CarbonUtils;

import javax.servlet.ServletException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XSSValve extends ValveBase {

    private static String XSS_VALVE_PROPERTY = "Security.XSSPreventionConfig";
    private static String ENABLED_PROPERTY = XSS_VALVE_PROPERTY + ".Enabled";
    private static String RULE_PATTERN_PROPERTY = XSS_VALVE_PROPERTY + ".Patterns.Pattern";
    private static String RULE_PROPERTY = XSS_VALVE_PROPERTY + ".Rule";
    private static String XSS_EXTENSION_FILE_NAME = "xss-patterns.properties";
    private static boolean xssEnabled = false;
    private static String RULE_ALLOW = "allow";
    private static String RULE_DENY = "deny";
    private static String[] xssURIPatternList;
    private static String xssRule;
    private static String patterPath = "";
    private static ArrayList<Pattern> patternList;

    protected static final Log log = LogFactory.getLog(XSSValve.class);

    private static Pattern[] patterns = new Pattern[] { Pattern.compile("<input", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<body", Pattern.CASE_INSENSITIVE), Pattern.compile("<link", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<link", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
            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),
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("<img", Pattern.CASE_INSENSITIVE),
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("alert(.*)", Pattern.CASE_INSENSITIVE),
            Pattern.compile("onMouse", Pattern.CASE_INSENSITIVE),
            Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL) };

    @Override
    protected void initInternal() throws LifecycleException {
        super.initInternal();
        loadConfiguration();
    }

    /**
     * Load configuration
     */
    private void loadConfiguration() {

        ServerConfiguration serverConfiguration = ServerConfiguration.getInstance();
        if (serverConfiguration.getFirstProperty(ENABLED_PROPERTY) != null
                && Boolean.parseBoolean(serverConfiguration.getFirstProperty(ENABLED_PROPERTY))) {
            xssEnabled = true;
        }
        xssURIPatternList = serverConfiguration.getProperties(RULE_PATTERN_PROPERTY);
        xssRule = serverConfiguration.getFirstProperty(RULE_PROPERTY);
        patterPath = CarbonUtils.getCarbonSecurityConfigDirPath() + "/" + XSS_EXTENSION_FILE_NAME;
        buildScriptPatterns();
    }

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {

        if (xssEnabled) {
            String context = request.getRequestURI().substring(request.getRequestURI().indexOf("/") + 1);
            if (RULE_ALLOW.equals(xssRule) && !isContextStartWithGivenPatterns(context)) {
                validateParameters(request);
            } else if (RULE_DENY.equals(xssRule) && isContextStartWithGivenPatterns(context)) {
                validateParameters(request);
            } else if (!(RULE_ALLOW.equals(xssRule) || RULE_DENY.equals(xssRule))) {
                validateParameters(request);
            }

        }
        getNext().invoke(request, response);
    }

    private void validateParameters(Request request) throws ServletException {

        Enumeration<String> parameterNames = request.getParameterNames();

        while (parameterNames.hasMoreElements()) {

            String paramName = parameterNames.nextElement();
            String paramValue = request.getParameter(paramName);
            if (paramValue != null) {
                paramValue = paramValue.replaceAll("\0", "");
                for (Pattern scriptPattern : patternList) {
                    Matcher matcher = scriptPattern.matcher(paramValue);
                    if (matcher.find()) {
                        throw new ServletException(
                                "Possible XSS Attack. Suspicious code : " + matcher.toMatchResult().group());
                    }
                }
            }
        }
    }

    /**
     * Check whether context starts with defined pattern
     *
     * @param context
     * @return
     */
    private boolean isContextStartWithGivenPatterns(String context) {

        boolean patternMatched = false;

        for (String pattern : xssURIPatternList) {
            if (context.startsWith(pattern)) {
                patternMatched = true;
                break;
            }
        }
        return patternMatched;
    }

    private void buildScriptPatterns() {
        patternList = new ArrayList<Pattern>(Arrays.asList(patterns));
        if (patterPath != null && !patterPath.isEmpty()) {
            InputStream inStream = null;
            File xssPatternConfigFile = new File(patterPath);
            Properties properties = new Properties();
            if (xssPatternConfigFile.exists()) {
                try {
                    inStream = new FileInputStream(xssPatternConfigFile);
                    properties.load(inStream);
                } catch (FileNotFoundException e) {
                    log.error("Can not load xssPatternConfig properties file ", e);
                } catch (IOException e) {
                    log.error("Can not load xssPatternConfigFile properties file ", e);
                } finally {
                    if (inStream != null) {
                        try {
                            inStream.close();
                        } catch (IOException e) {
                            log.error("Error while closing stream ", e);
                        }
                    }
                }
            }
            if (!properties.isEmpty()) {
                for (String key : properties.stringPropertyNames()) {
                    String value = properties.getProperty(key);
                    patternList.add(Pattern.compile(value, Pattern.CASE_INSENSITIVE));
                }
            }

        }
    }

}