Tuesday, February 1, 2011

Taking care of Cross Site Scripting and SQL Injection in web applications

How many times it happens to us that we design the application without considering the security for the web application I have encountered this a couple of times and decided to share my experiences in Blog. I will only talk about the application aspects here so generally the threats to take care are SQL Injection and  XSS(Cross Site Scripting).

SQL Injection: Exploits applications that use external input in the database commands.
Humm so how is this vulnerable, there are various ways to do so send commands that could destroy the data, send inputs which could corrupt the data or the worst send inputs which could expose all the data. Ohh well my application is strong and this cannot happen, think again if we do not sanitize the data all probability that this will happen. With the advent of ORM systems this is taken care of in a big way.
Putting my thinking cap on: How can safeguard from my side still. Scan the parameters such as ',",1=1 and block them at the input level or at the request level.

XSS (Cross site scripting): The most common form used in web applications, this results in unwanted scripts being executed on the website and it can result in displaying unpleasant text, pictures etc. This works by exploiting applications or form fields that echo raw unfiltered input  to the web pages, steal cookie information, deface and disable sites.
Enough about what it can do now how it can be stopped?
  1. Scan all the input fields by escaping standard html tags, one of the important aspects is to clearly demarcate what is not acceptable as the input and strip off the inputs that are not allowed.
  2. One of the most effective way I found was to have portal wide filter which scan all the requests and strip of or escape the inputs. I have encountered these issues in Liferay Portal and the changes we did are listed below, but nothing stops us from using the same concepts in a general web application. So here we go on what I did :
   1. Created a request Wrapper class the code for the same is pasted below:

    private String cleanXSS(String value) {
        value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
        value = value.replaceAll("\\(", "&#040;").replaceAll("\\)", "&#041;");
        value = value.replaceAll("'", "&#039;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = XSS_Field.HandlingScripts(value);
        return value;
    }
}

    So this method is replacing the html tags with their XML equivalents so that it cannot be executed and as u that it calls a method called Handling scripts which will do a lot more as we will see now.
      2. Created methods to cover for all the script variations.
    public static String handleCrossSiteScript(String sData) {
            //System.out.println("................................"+sData);
            //sData = sData.replaceAll("script", "");
            //sData = sData.replaceAll("SCRIPT", "");
            //System.out.println("................................"+sData);
            sData = escapeScriptTag(sData);
            sData = filterInputString(sData);
            sData = escapeJavaScriptWord(sData);
            sData = escapeScriptWord(sData);
            StringBuffer buf = new StringBuffer("");
            char ch[] = null;
            if (sData != null) {
                int len = sData.length();
                ch = sData.toCharArray();
                int startIndex = 0;
                for (int i = 0; i < len; i++) {
                    if (ch[i] == '\"') {
                        // buf.append(sData.substring(startIndex,
                        // i)).append("\\\"");
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '&') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '\'') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '<') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '>') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } /*else if (ch[i] == '%') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }*/ else if (ch[i] == '#') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '!') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '@') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '^') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '*') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '(') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == ')') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '$') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == ';') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '`') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '~') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }/* else if (ch[i] == '-') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '_') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == '+') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }*/ else if (ch[i] == '=') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if (ch[i] == ',') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } /*else if (ch[i] == '.') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }   else if (ch[i] == '/') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }*/ else if (ch[i] == ':') {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;                   
                    } else if ((int) ch[i] == 9) {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    } else if ((int) ch[i] == 10) {
                        buf.append(sData.substring(startIndex, i)).append("");
                        startIndex = i + 1;
                    }
                }

                buf.append(sData.substring(startIndex));
            }
            return buf.toString();
        }

         // Added to handle cross-site scripting
        public static String escapeScriptTag(String s) {
           
            StringBuffer buf = new StringBuffer("");
            int len = s.length();
            int startIndex = 0;

            // To find out if there is a <script>
            int indexOfScript = s.indexOf("<script>");
            if(indexOfScript==-1){
                indexOfScript=s.indexOf("<SCRIPT>");
            }
        
            // To find out if there is a </script>
            int indexOfEndScript = s.indexOf("</script>");
            if(indexOfEndScript==-1){
                indexOfEndScript=s.indexOf("</SCRIPT>");
            }
           
           
            if ((indexOfScript < 0) && (indexOfEndScript < 0))
                return s;
            if (((indexOfScript > 0) && (indexOfEndScript < 0)) || (indexOfScript == 0) ) {
                buf.append(s.substring(startIndex, indexOfScript));
                buf.append(s.substring(indexOfScript + 8));
                s = buf.toString();
                return s;
            }
            if (((indexOfScript < 0) && (indexOfEndScript > 0)) || (indexOfEndScript ==0)) {
                buf.append(s.substring(startIndex, indexOfEndScript));
                buf.append(s.substring(indexOfEndScript + 9));
                s = buf.toString();
                return s;
            }
            if ((indexOfScript > 0) && (indexOfEndScript > 0)) {
                buf.append(s.substring(startIndex, indexOfScript));
                buf.append(s.substring(indexOfScript + 8, indexOfEndScript));
                buf.append(s.substring(indexOfEndScript + 9));
                s = buf.toString();
                return s;
            }
            escapeScriptTag(s);
            return s;
        }

       // Added to handle cross-site scripting
        public static String filterInputString(String data) {
           
            StringBuffer buf = new StringBuffer("");
            if (isBlankString(data))
                return data;       

            java.util.StringTokenizer tokens = new java.util.StringTokenizer(data,
                    " ");
            int tokenCount = tokens.countTokens();
            String[] result = new String[tokenCount];

            for (int i = 0; i < tokenCount; i++) {
                result[i] = tokens.nextToken();
                if ("<SCRIPT".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("SCRIPT>".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("SCRIPT".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("JAVASCRIPT".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("JAVASCRIPT>".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("\"JAVASCRIPT\"".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("\"JAVASCRIPT\">".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("=JAVASCRIPT".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("=JAVASCRIPT>".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("=\"JAVASCRIPT\"".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("=\"JAVASCRIPT\">".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("'JAVASCRIPT'".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("'JAVASCRIPT'>".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("='JAVASCRIPT'".equalsIgnoreCase(result[i]))
                    result[i] = "";
                if ("='JAVASCRIPT'>".equalsIgnoreCase(result[i]))
                    result[i] = "";

                buf.append(result[i]);
                if (i != tokenCount - 1)
                    buf.append(" ");
            }
            data = buf.toString();
           
            return data;
        }

        // Added to handle cross-site scripting
        public static String escapeJavaScriptWord(String s) {
           
            StringBuffer buf = new StringBuffer("");
            int len = s.length();
            int startIndex = 0;

            // To find out if there is a javascript word
            int indexOfScript1 = s.indexOf("javascript");
            int indexOfScript = -1;
            if (indexOfScript1 > 0)
                indexOfScript = indexOfScript1;
            int indexOfScript2 = s.indexOf("JAVASCRIPT");
            if (indexOfScript2 > 0)
                indexOfScript = indexOfScript2;
            int indexOfScript3 = s.indexOf("JavaScript");
            if (indexOfScript3 > 0)
                indexOfScript = indexOfScript3;
            int indexOfScript4 = s.indexOf("javaScript");
            if (indexOfScript4 > 0)
                indexOfScript = indexOfScript4;

            if ((indexOfScript1 < 0) && (indexOfScript2 < 0)
                    && (indexOfScript3 < 0) && (indexOfScript4 < 0))
                return s;
            else if ((indexOfScript1 > 0) || (indexOfScript2 > 0)
                    || (indexOfScript3 > 0) || (indexOfScript4 > 0)) {
                buf.append(s.substring(startIndex, indexOfScript));
                buf.append(s.substring(indexOfScript + 10));
                s = buf.toString();
                return s;
            }
            escapeJavaScriptWord(s);
            return s;
        }

        // Added to handle cross-site scripting
        public static String escapeScriptWord(String s) {
           
            StringBuffer buf = new StringBuffer("");
            int len = s.length();
            int startIndex = 0;

            // To find out if there is a javascript word
            int indexOfScript1 = s.indexOf("script");
            int indexOfScript = -1;
            if (indexOfScript1 > 0)
                indexOfScript = indexOfScript1;
            int indexOfScript2 = s.indexOf("SCRIPT");
            if (indexOfScript2 > 0)
                indexOfScript = indexOfScript2;
            int indexOfScript3 = s.indexOf("Script");
            if (indexOfScript3 > 0)
                indexOfScript = indexOfScript3;

            if ((indexOfScript1 < 0) && (indexOfScript2 < 0)
                    && (indexOfScript3 < 0))
                return s;
            else if ((indexOfScript1 > 0) || (indexOfScript2 > 0)
                    || (indexOfScript3 > 0)) {
                buf.append(s.substring(startIndex, indexOfScript));
                buf.append(s.substring(indexOfScript + 6));
                s = buf.toString();
                return s;
            }
            escapeJavaScriptWord(s);
            return s;
        }

public static String handleSqlInjection(String iChars,String inputString)
           {
               String returnval=inputString;
             
                for (int j = 0; j < returnval.length(); j++) {  
                      if (iChars.indexOf(returnval.charAt(j)) != -1)
                      {  
                          returnval = returnval.replace(String.valueOf(returnval.charAt(j)),"");
                    }}     
              // System.out.println("After SQL INJECTION *****"+returnval);
               return returnval;
           }
        
/*         Added to handle cross-site scripting*/
            public static String ReplaceSpecialSymbol(String sData) {
              
                StringBuffer buf = new StringBuffer("");
                char ch[] = null;
                if (sData != null) {
                    int len = sData.length();
                    ch = sData.toCharArray();
                    int startIndex = 0;
                    for (int i = 0; i < len; i++) {
                        if (ch[i] == '\"') {
                            // buf.append(sData.substring(startIndex,
                            // i)).append("\\\"");
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '&') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '\'') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '<') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '>') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } /*else if (ch[i] == '%') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }*/ else if (ch[i] == '#') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '!') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '@') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '^') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '*') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '(') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == ')') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '$') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == ';') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '`') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '~') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }/* else if (ch[i] == '-') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '_') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == '+') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }*/ else if (ch[i] == '=') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if (ch[i] == ',') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } /*else if (ch[i] == '.') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }   else if (ch[i] == '/') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }*/ else if (ch[i] == ':') {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;                  
                        } else if ((int) ch[i] == 9) {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        } else if ((int) ch[i] == 10) {
                            buf.append(sData.substring(startIndex, i)).append("");
                            startIndex = i + 1;
                        }
                    }

                    buf.append(sData.substring(startIndex));
                }
                return buf.toString();
            }

3. call this from a filter so my entry in web.xml read something like
       
            <filter>
            <filter-name>XSS</filter-name>
                <filter-class>com.liferay.portal.servlet.filters.xss.CrossScriptingFilter</filter-class>
            </filter>
            <filter-mapping>
                <filter-name>XSS</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>

           
            This is pretty self explanatory am I right?
           Is this the best approach I don't know but I hope it helps people in the secrity issues faced.