AntiSamy was originally authored by Arshan Dabirsiaghi (arshan.dabirsiaghi [at] gmail.com) of Contrast Security with help from Jason Li (jason.li [at] owasp.org) and is currently maintained by Dave Wichers (dave.wichers [at] owasp.org) and Sebastian Passaro (sebastian.passaro [at] owasp.org).
Philosophically, AntiSamy is a departure from contemporary security mechanisms. Generally, the security mechanism and user have a communication that is virtually one way, for good reason. Letting the potential attacker know details about the validation is considered unwise as it allows the attacker to “learn” and “recon” the mechanism for weaknesses. These types of information leaks can also hurt in ways you don’t expect. A login mechanism that tells the user, “Username invalid” leaks the fact that a user by that name does not exist. A user could use a dictionary or phone book or both to remotely come up with a list of valid usernames. Using this information, an attacker could launch a brute force attack or massive account lock denial-of-service. We get that.
Unfortunately, that’s just not very usable in this situation. Typical Internet users are largely pretty bad when it comes to writing HTML/CSS, so where do they get their HTML from? Usually they copy it from somewhere out on the web. Simply rejecting their input without any clue as to why is jolting and annoying. Annoyed users go somewhere else to do their social networking.
What is AntiSamy?
OWASP AntiSamy provides:
This page shows a big-picture comparison between the versions. Since it’s an unfunded open source project, the ports can’t be expected to mirror functionality exactly. If there’s something a port is missing – let us know, and we’ll try to accommodate, or write a patch!
From OWASP & WASC AppSec U.S. 2007 Conference (San Jose, CA): AntiSamy: Picking a Fight with XSS (ppt) - by Arshan Dabirsiaghi - AntiSamy project lead
From OWASP AppSec Europe 2008 (Ghent, Belgium): The OWASP AntiSamy project (ppt) - by Jason Li - AntiSamy project contributor
From OWASP AppSec India 2008 (Delhi, India): Validating Rich User Content (ppt) - by Jason Li - AntiSamy project contributor
From Shmoocon 2009 (Washington, DC): AntiSamy - Picking a Fight with XSS (pptx) - by Arshan Dabirsiaghi - AntiSamy project lead
News and Events
[28 Sep 2017] Please update AntiSamy to 1.5.7 or later per CVE-2017-14735 and CVE-2016-10006
There are 4 steps in the process of integrating AntiSamy. Each step is detailed in the next section, but the high level overview follows:
- Download AntiSamy from Maven
- Choose one of the standard policy files that matches as close to the functionality you need:
- Tailor the policy file according to your site’s rules
- Call the API from the code
Stage 1 - Downloading AntiSamy
First, add the dependency from Maven:
<dependency> <groupId>org.owasp.antisamy</groupId> <projectId>antisamy</projectId> </dependency>
Stage 2 - Choosing a base policy file
Chances are that your site’s use case for AntiSamy is at least roughly comparable to one of the predefined policy files. They each represent a “typical” scenario for allowing users to provide HTML (and possibly CSS) formatting information. Let’s look into the different policy files:
Slashdot is a techie news site that allows users to respond anonymously to news
posts with very limited HTML markup. Now, Slashdot is not only one of the coolest sites around, it’s also
one that’s been subject to many different successful attacks. Even more unfortunate is the fact that most
of the attacks led users to the infamous goatse.cx picture (please don’t go look it up). The rules for
Slashdot are fairly strict: users can only submit the following HTML tags and no CSS:
Accordingly, we’ve built a policy file that allows fairly similar functionality. All text-formatting tags that operate directly on the font, color or emphasis have been allowed.
eBay is the most popular online auction site in the universe, as far as I can tell.
It is a public site so anyone is allowed to post listings with rich HTML content. It’s not surprising that
given the attractiveness of eBay as a target that it has been subject to a few complex XSS attacks. Listings
are allowed to contain much more rich content than, say, Slashdot- so it’s attack surface is considerably
larger. The following tags appear to be accepted by eBay (they don’t publish rules):
Stage 3 - Tailoring the policy file
Smaller organizations may want to deploy AntiSamy in a default configuration, but it’s equally likely that a site may want to have strict, business-driven rules for what users can allow. The discussion that decides the tailoring should also consider attack surface - which grows in relative proportion to the policy file.
You may also want to enable/modify some “directives”, which are basically advanced user options. This page tells you what the directives are and which versions support them.
Stage 4 - Calling the AntiSamy API
Using AntiSamy is easy. Here is an example of invoking AntiSamy with a policy file:
import org.owasp.validator.html.*; Policy policy = Policy.getInstance(POLICY_FILE_LOCATION); AntiSamy as = new AntiSamy(); CleanResults cr = as.scan(dirtyInput, policy); MyUserDAO.storeUserProfile(cr.getCleanHTML()); // some custom function
There are a few ways to create a Policy object. The
getInstance() method can take any of the following:
- a String filename
- a File object
- an InputStream
Policy files can also be referenced by filename by passing a second argument to the AntiSamy:scan() method as the following examples show:
AntiSamy as = new AntiSamy(); CleanResults cr = as.scan(dirtyInput, policyFilePath);
Finally, policy files can also be referenced by File objects directly in the second parameter:
AntiSamy as = new AntiSamy(); CleanResults cr = as.scan(dirtyInput, new File(policyFilePath));
Stage 5 - Analyzing CleanResults
The CleanResults object provides a lot of useful stuff.
getErrorMessages() - a list of String error messages getCleanHTML() - the clean, safe HTML output getCleanXMLDocumentFragment() - the clean, safe XMLDocumentFragment which is reflected in getCleanHTML() getScanTime() - returns the scan time in seconds