View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *     http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package org.gbif.ws.util;
15  
16  import java.util.regex.Matcher;
17  import java.util.regex.Pattern;
18  
19  import org.apache.commons.lang3.StringUtils;
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  /**
24   * Class with utility methods for XSS filtering.
25   */
26  public class XSSUtil {
27    private static final Logger LOG = LoggerFactory.getLogger(XSSUtil.class);
28  
29    private XSSUtil() {
30      // empty private constructor
31    }
32  
33    private static final Pattern NULL_CHAR = Pattern.compile("\0");
34    private static final Pattern[] PATTERNS =
35        new Pattern[] {
36          // Avoid anything in a <script> type of expression
37          Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
38          // Avoid anything in a src='...' type of expression
39          Pattern.compile(
40              "src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
41              Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
42          Pattern.compile(
43              "src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
44              Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
45          // Remove any lonesome </script> tag
46          Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
47          // Avoid anything in a <iframe> type of expression
48          Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE),
49          // Remove any lonesome <script ...> tag
50          Pattern.compile(
51              "<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
52          // Remove any lonesome <img ...> tag
53          Pattern.compile(
54              "<img(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
55          // Avoid eval(...) expressions
56          Pattern.compile(
57              "eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
58          // Avoid expression(...) expressions
59          Pattern.compile(
60              "expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
61          // Avoid javascript:... expressions
62          Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
63          // Avoid vbscript:... expressions
64          Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
65          // Avoid onload= expressions
66          Pattern.compile(
67              "on(load|error|mouseover|submit|reset|focus|click)(.*?)=",
68              Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)
69        };
70  
71    /**
72     * Method tests whether a string contains malicious XSS script or not.
73     *
74     * @param value decoded string to test
75     *
76     * @return true if string matches at least one XSS pattern, or false otherwise
77     */
78    public static boolean containsXSS(String value) {
79      if (value != null) {
80  
81        // Avoid null characters
82        String cleanValue = NULL_CHAR.matcher(value).replaceAll("");
83  
84        // Remove all sections that match a pattern
85        for (Pattern scriptPattern : PATTERNS) {
86          Matcher matcher = scriptPattern.matcher(cleanValue);
87          if (matcher.find()) {
88            LOG.warn("Potentially malicious XSS script found: {}", cleanValue);
89            return true;
90          }
91        }
92      }
93      return false;
94    }
95  
96    /**
97     * Strip all instances of all XXS patterns that match.
98     *
99     * @param value
100    * @return
101    */
102   public static String stripXSS(String value) {
103 
104     if (value == null) {
105       return null;
106     }
107 
108     // Avoid null characters
109     String cleanValue = NULL_CHAR.matcher(value).replaceAll("");
110     if (StringUtils.isBlank(cleanValue)) {
111       return cleanValue;
112     }
113 
114     // Remove all sections that match a pattern
115     for (Pattern scriptPattern : PATTERNS) {
116       Matcher matcher = scriptPattern.matcher(cleanValue);
117       cleanValue = matcher.replaceAll("");
118     }
119     return cleanValue;
120   }
121 }