Are you sure you’ve sanitized your inputs?

This boggles the mind. Using an alphabet of just 6 non-alphanumeric characters, anyone can write any javascript code. The problem of how to allow some friendly javascript code while blocking anything unfriendly might be a subject worthy of computer science research.

In the meantime, eBay (and others) really should do something to reduce this vulnerability. I have a quick-and-dirty solution in Java based on detecting significantly long runs of the 6 characters in question. The weakness of the attack in question is of course that you need a lot of characters to do anything evil in the obfuscated javascript, so there should be long runs containing only  the 6 characters. It’s possible to include spaces and line breaks and even comments to break up the runs – I took this into account in my solution.  I chose 10 as the run length threshold for detecting the obfuscation, because I don’t know of something legitimate you can do in javascript using 10 of these characters in a row that you couldn’t do another way using some alphabetic characters, and if I saw code with 10 of those characters in a row, I would suspect it right away.

Here’s some of the code in my solution. First, the implementation of containsSneakyJavascript:

public static boolean containsSneakyJavascriptCode(final String userInput) {
	SneakyJSDetectionContext ctx = new SneakyJSDetectionContext(userInput);
	while (ctx.notDone()) {
		ctx.processCurrentChar();
		ctx.nextChar();
	}
	return ctx.detectedSneakyJS();
}

That’s code at a pretty high level of abstraction, so here’s more detail with the implementation of the processCurrentChar() call that you see in the code above. It ignores whitespace and characters inside comments and otherwise checks whether the current character adds to or ends the current run of suspect characters and whether it starts a comment:

void processCurrentChar() {
	if (insideAComment()) {
		checkForEndOfComment();
	} else if (isNotWhiteSpace()) {
		if (isInSneakyAlphabet(currentChar())) {
			incrementCurrentRunLength();
		} else {
			if (isStartOfComment()) {
				setCommentStart();
			} else {
				resetCurrentRunToEmpty();
			}
		}
	}
}

The full implementation code is here, and for good measure here are the unit tests for it.

You’re welcome, eBay.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s