Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Cross-Site Scripting (XSS) Attack Remediation

DZone 's Guide to

Cross-Site Scripting (XSS) Attack Remediation

Learn more about prevention and remediation after a cross-site scripting attack.

· Security Zone ·
Free Resource

For more details on XSS attacks, please refer to OWASP and this OWASP Prevention cheat sheet.

Preventing XSS

Various factors should be considered while acting on XSS Attacks, for example:

  • Input type in the HTTP request
  • Locations of the HTML document where data would be included

Note

  • A defense that works in one context (such as an HTML attribute) might not work in another context (such as a JavaScript variable assignment)
  • A defense that works with one kind of input (such as input validation and output encoding for a username) will not work with other kinds of input (such as sanitization for untrusted HTML).

We need to use a different output encoding function based on where you are inserting untrusted data into the webpage!

OWASP Java Encoder Project

<dependency>
    <groupId>org.owasp.encoder</groupId>
    <artifactId>encoder</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>org.owasp.encoder</groupId>
    <artifactId>encoder-jsp</artifactId>
    <version>1.2.1</version>
</dependency>


Here are a few examples of different contexts in an HTML page in a JSP:

Encoding

HTML

The following encoding functions are used to safely place untrusted data into different locations of an HTML document.

API Coments
Encode.forHtml(UNTRUSTED) If you are unsure, which one to use, you can use this in HTML context
Encode.forHtmlAttribute(UNTRUSTED)
Encode.forHtmlContent(UNTRUSTED)
Encode.forHtmlUnquotedAttribute(UNTRUSTED)

JavaScript

The following encoding functions are used to safely place untrusted data into different locations of JavaScript code.

API Comments
Encode.forJavaScript(UNTRUSTED) If you are unsure, which one to use, you can use this in JavaScript context
Encode.forJavaScriptAttribute(UNTRUSTED)
Encode.forJavaScriptBlock(UNTRUSTED)
Encode.forJavaScriptSource(UNTRUSTED)

CSS

The following encoding functions are used to safely place untrusted data into different locations of dynamic CSS code.

API Comments

Encode.forCssString(UNTRUSTED)

<div>Encode.forCssString(UNTRUSTED) %>”>

Encode.forCssUrl(UNTRUSTED)

<div>Encode.forCssUrl(UNTRUSTED) %>”>

XML

API Comments
Encode.forXML(UNTRUSTED)
Encode.forXMLContent(UNTRUSTED)
Encode.forXMLAttribute(UNTRUSTED)
Encode.forXMLComment(UNTRUSTED)
Encode.forCDATA(UNTRUSTED)

Tag Lib

Prefer tag libs while working with jsps:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@taglib prefix="e" uri="https://www.owasp.org/index.php/OWASP_Java_Encoder_Project" %>
<html>
<head>
<title><e:forHtml value="${param.title}" /></title>
</head>
<body>
<h1>${e:forHtml(param.data)}</h1>
</body> P


For more details, refer to the OWASP Java Encoder Project Docs.

Wrapper Class

import javax.servlet.http.HttpServletRequest;
  
import org.owasp.encoder.Encode;
  
public class SafeDataBuilder {
  
    private String paramVal = null;
  
    private SafeDataBuilder(HttpServletRequest request, String paramName) {
        String val = getValue(request, paramName);
        this.paramVal = cleanValue(val);
    }
  
    private String cleanValue(String val) {
        String result = val;
        if (val != null && val.length() > SecurityConstants.MAX_HTTP_PARAM_LENGTH) {
            result = val.substring(0, SecurityConstants.MAX_HTTP_PARAM_LENGTH);
        }
        return result;
    }
  
    private String getValue(HttpServletRequest request, String paramName) {
        String val = request.getParameter(paramName);
  
        if (val == null) {
            Object attr = request.getAttribute(paramName);
            if (attr instanceof String) {
                val = (String) attr;
            }
            if (attr instanceof Boolean) {
                val = attr.toString();
            }
        }
        return val;
    }
  
    public static SafeDataBuilder requestParam(HttpServletRequest request, String paramName) {
        return new SafeDataBuilder(request, paramName);
    }
  
    public SafeDataBuilder defaultValue(String val) {
        if (this.paramVal == null) {
            this.paramVal = val;
        }
        return this;
    }
      
    public SafeDataBuilder trim() {
        if (this.paramVal != null) {
            this.paramVal = paramVal.trim();
        }
        return this;
    }
  
    public SafeDataBuilder forHtml() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forHtml(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forHtmlContent() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forHtml(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forHtmlAttribute() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forHtml(this.paramVal);
        }
        return this;
    }
      
   public SafeDataBuilder forFormActionAttribute() {
        if (this.paramVal != null) {           
            this.paramVal = Encode.forHtmlAttribute(cleanJsAttribute(this.paramVal));
        }
        return this;
    }
  
    private String cleanJsAttribute(String action) {
        String result = action;
        String cleansed = action.trim().toLowerCase();
        if (cleansed.startsWith("javascript")) {
            result = cleansed.substring(10);
        }
        return result;
    }
    public SafeDataBuilder forJavaScript() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forJavaScript(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forJavaScriptAttribute() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forJavaScriptAttribute(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forJavaScriptBlock() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forJavaScriptBlock(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forJavaScriptSource() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forJavaScriptSource(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forCssString() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forCssString(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forCssUrl() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forCssUrl(this.paramVal);
        }
        return this;
    }
  
    public SafeDataBuilder forUriComponent() {
        if (this.paramVal != null) {
            this.paramVal = Encode.forUriComponent(this.paramVal);
        }
        return this;
    }
  
    public String get() {
        return this.paramVal;
    }
  
  
    public String getQuoted() {
        return "'" + this.paramVal + "'";
    }
}


Usage

String paramVal = SafeDataBuilder.requestParam(request, "paramName").forHtml().get();
String paramVal = SafeDataBuilder.requestParam(request, "paramName").defaultValue("def").forHtml().get();
String paramVal = SafeDataBuilder.requestParam(request, "paramName").defaultValue("def").trim().forHtml().get();


Solution

Refer to the project for more details:

  • Define a config for each page.
  • The config describes what are all parameters (and XSS type) used by the page
  • Configure an XSS filter (XSSFilter) for every request, which wraps an httpservelet request (XSSRequestWrapper).
  • Eventually, every page has XSSRequestWrapper as HTTPServletRequest, whenever getParameter happens, config is read to the determine the XSS type, and data is stripped accordingly.
  • Further, we may have to define one more method in XSSRequestWrapper getParameter (name, type) in case the need arises.
  • Further, we may have to define one more method in XSSRequestWrapper getOriginalParameter (name) in case if the need arises.
  • Further by default, for each parameter p, there should other for example p_xss_s_xss_type (for each  xss_type)

References

Topics:
security ,xss ,attacks ,string ,wrapper class ,prevention ,rehabilitation

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}