Coding - Java Web Application

Anti Cross-Site Scripting (XSS)

Here is a good and simple anti cross-site scripting (XSS) filter written for Java web applications. What it basically does is remove all suspicious strings from request parameters before returning them to the application.

You should configure it as the first filter in your chain (web.xml) and it’s generally a good idea to let it catch every request made to your site.

public class XSSFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
	}
}

public class XSSRequestWrapper extends HttpServletRequestWrapper {
	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) };

	public XSSRequestWrapper(HttpServletRequest servletRequest) {
		super(servletRequest);
	}

	@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);

		return stripXSS(value);
	}

	@Override
	public String getHeader(String name) {
		String value = super.getHeader(name);
		return stripXSS(value);
	}

	private String stripXSS(String value) {
		if (value != null) {
			// NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to
			// avoid encoded attacks.
			// value = ESAPI.encoder().canonicalize(value);

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

HTTP/HTTPS Client Operations

Upload Data File to Server

Here is a full function (client) to upload a zip file to a server using HTTP, please also note the UTF-8:

	protected void uploadBulkFile(BatchJob job) throws Exception {
		final String CRLF = "\r\n";
		URL url = new URL(job.getUploadUrl());
		HttpURLConnection connection = null;

		FileInputStream reader = null;
		OutputStream output = null;
		PrintWriter writer = null;

		try {
			// Set up the connection and headers
			connection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
			connection.setUseCaches(false);
			connection.setDoOutput(true);
			connection.setDoInput(true);

			// Set up the authentication properties
			AuthorizationData authorizationData = serviceClient.getAuthorizationData();
			PasswordAuthentication passwordAuthentication = (PasswordAuthentication) authorizationData.getAuthentication();
			connection.setRequestProperty("UserName", passwordAuthentication.getUserName());
			connection.setRequestProperty("Password", passwordAuthentication.getPassword());

			// Content-Type must be multipart/form-data with custom boundary
			String boundary = "--------------------" + Long.toString(System.currentTimeMillis(), 16);
			String contentType = "multipart/form-data; boundary=" + boundary;
			connection.setRequestProperty("Content-Type", contentType);

			final int bufferSize = 100 * 1024;
			byte[] buffer = new byte[bufferSize];

			File file = new File(job.getBulkFile());
			reader = new FileInputStream(file);
			output = connection.getOutputStream();
			writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);

			// Add the file within the specified boundary
			writer.append("--" + boundary).append(CRLF);
			writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"").append(CRLF);
			writer.append("Content-Type: application/zip").append(CRLF);
			writer.append("Content-Transfer-Encoding: binary").append(CRLF);
			writer.append(CRLF);
			writer.flush();

			int count = 0;
			while ((count = reader.read(buffer)) != -1) {
				output.write(buffer, 0, count);
			}

			output.flush();

			writer.append(CRLF).flush();
			writer.append("--" + boundary + "--").append(CRLF);
			writer.flush();

			logger.debug("Upload Connection Response: {}", connection.getResponseMessage());
		} finally {
			reader.close();
			writer.flush();
			writer.close();
			output.flush();
			output.close();
		}
	}

Customized Apache Http Client

public class ApiHttpClient {
	private final CloseableHttpClient httpClient;

	public ApiHttpClient(String configSection) {
		HttpClientBuilder builder = HttpClients.custom();
		Registry<ConnectionSocketFactory> socketFactoryRegistry = null;

		// use client cert if configured so
		if (Config.getBool(configSection, "UseClientCert", false)) {
			ClassLoader classLoader = ApiHttpClient.class.getClassLoader();
			File clientCertFile = new File(classLoader.getResource(Config.getStr(configSection, "ClientCertFile")).getFile());
			try {
				KeyStore keystore = KeyStore.getInstance("PKCS12");
				keystore.load(new FileInputStream(clientCertFile), Config.getStr(configSection, "ClientCertPassword").toCharArray());
				SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keystore, Config.getStr(configSection, "ClientCertPassword").toCharArray()).build();
				SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext.getSocketFactory(), new NoopHostnameVerifier());
				socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslsf).register("http", PlainConnectionSocketFactory.getSocketFactory()).build();

			} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException | KeyManagementException e) {
				throw new RuntimeException(e);
			}
		}

		// use pooling http client manager
		PoolingHttpClientConnectionManager connectionManager = null;
		if (socketFactoryRegistry == null)
			connectionManager = new PoolingHttpClientConnectionManager();
		else
			connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		connectionManager.setMaxTotal(Config.getInt(configSection, "MaxTotal", 200));
		connectionManager.setDefaultMaxPerRoute(Config.getInt(configSection, "DefaultMaxPerRoute", 20));
		builder.setConnectionManager(connectionManager);

		// set host proxy if needed
		if (Config.getBool(configSection, "UseProxy", false)) {
			HttpHost proxy = new HttpHost(Config.getStr(configSection, "ProxyHost"), Config.getInt(configSection, "ProxyPort"));
			builder.setProxy(proxy);
		}

		/*
		 * // add more request settings RequestConfig config =
		 * RequestConfig.custom()
		 * .setConnectTimeout(Config.getInt(configSection, "ConnectionTimeout",
		 * 20000)) .setSocketTimeout(Config.getInt(configSection,
		 * "SocketTimeout", 30000)).build();
		 *
		 * builder.setDefaultRequestConfig(config);
		 */

		this.httpClient = builder.build();
	}

	public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws ClientProtocolException, IOException {
		return httpClient.execute(request, responseHandler);
	}

	public CloseableHttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException, ClientProtocolException {
		return httpClient.execute(request, context);
	}

	public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException, ClientProtocolException {
		return httpClient.execute(request, responseHandler, context);
	}

	public CloseableHttpResponse execute(HttpUriRequest request) throws IOException, ClientProtocolException {
		return httpClient.execute(request);
	}
}

React

React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called “components”.

We use components to tell React what we want to see on the screen. When our data changes, React will efficiently update and re-render our components.

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// Example usage: <ShoppingList name="Mark" />

jQuery Dialog for Rule Settings

function updateRuleSettings(updateButton) {
	$.getJSON("getRule.do?ruleId=" + $(updateButton).closest("tr").attr("id")).done(function(rule) {
		$("#dialogUpdateSettings input[name='id']").val(rule.id);
		$("#dialogUpdateSettings input[name='encodeUrlParams']").prop("checked", rule.ruleSettings['ENCODE_URL_PARAMS'] == 'true');
		$("#dialogUpdateSettings input[name='appendSlingshotID']").prop("checked", rule.ruleSettings['APPEND_SLINGSHOT_ID'] == 'true');
		$("#dialogUpdateSettings select[name='fowardRequestMethod']").val(rule.ruleSettings['FORWARD_REQUEST_METHOD']);
		$("#dialogUpdateSettings input[name='passForwardRestParams']").prop("checked", rule.ruleSettings['PASS_FORWARD_REST_PARAMS']);
		var validateOptions = [];
		if (rule.validateLevel == null || rule.validateLevel == 0) {
			validateOptions.push("0");
		} else {
				if ((rule.validateLevel >> 0 & 1) == 1)
				validateOptions.push("1");  
				if ((rule.validateLevel >> 1 & 1) == 1)
						validateOptions.push("2");
				if ((rule.validateLevel >> 2 & 1) == 1)
						validateOptions.push("4");
				if ((rule.validateLevel >> 3 & 1) == 1)
						validateOptions.push("8");
		}
		$("#dialogUpdateSettings select[name='validateOptions']").val(validateOptions);			
		$("#dialogUpdateSettings").dialog({
			autoOpen : false,
			resizable : false,
			height : 'auto',
			width : 'auto',
			modal : true,
			buttons : {
				Submit: function() {
					var params = $("#dialogUpdateSettings form").serialize();
					$.post("updateSettings.do", params, function(result) {
						if (result == 'OK') {
							$("#slingshotRuleTable").dataTable({bRetrieve : true}).fnDraw(true);
							$("#dialogUpdateSettings").dialog().dialog("close");
						} else {
							$("#dialogUpdateSettings #message").html(result).show();
						}
					});						
				},
				Cancel: function() {
					$(this).dialog("close");
				}
			},
			open: function(event, ui) {

			},
			close: function() {
				$("#dialogUpdateSettings form")[0].reset();
				$("#dialogUpdateSettings #message").html("").hide();
			}
		}).dialog("open");
	})
}

Tomcat Server Troubleshooting

If Tomcat 7+ starts adding unexpected double quote around the cookie value due to ‘=’ sign presents in value, you can add this line to context.xml:

<CookieProcessor className="org.apache.tomcat.util.http.LegacyCookieProcessor" allowHttpSepsInV0="true" />

Or uncomment the following line in catalina.properties:

org.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_SEPARATORS_IN_V0=true

Url with invalid character issue

To deal with this error in Tomcat 8+: java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

You can use following settings and specify some allowed characters in URL:

# Allow for changes to HTTP request validation
#WARNING: Using this option will expose the server to CVE-2016-6816
tomcat.util.http.parser.HttpParser.requestTargetAllow=|

Splunk Search Query Samples

index=sales sourcetype=vendor_sales
| dedup Vendor  
| sort limit=10 - VendorCountry, +VendorStateProvince, VendorCity, Vendor  
| table VendorCountry, VendorStateProvince, VendorCity, Vendor
index=web sourcetype=access_combined action=* productId=*
| dedup clientip, productId
| table productId, clientip, action
| sort productId, -clientip
| rename productId as "Product #", clientip as "Client IP Address", action as "Action Taken"
index=security sourcetype=linux_secure (fail* OR invalid)
| top limit=5 src_ip countfield=ATTACKS showperc=f useother=t
index=network sourcetype=cisco_wsa_squid
| stats dc(s_hostname) as "Websites visited:" values(s_hostname) as "Site Names"  count as "Visits" list(s_hostname) as "List of visits" by cs_username
index=web sourcetype=access_combined
| top limit=2 status by host
| fields - count
| sort host, -percent
index=web sourcetype=access_combined action=*
| stats count, avg(price), sum(price) by action
| rename count as "Total Events", avg(price) as "Average Price", sum(price) as "Total Amount", action as Action
sourcetype=vendor_sales earliest=-21d@d latest=@d
| timechart sum(price) as Revenue
| timewrap 1w series=exact