XSS Countermeasures

It is very important to filter malicious input, but when it comes to user agent injection, it is also important that the output does not contain executable code. I will introduce input filters afterwards. In general, it checks the user input to be of a specific format, and if not, rejects it with an error message. But importantly, the error message should not be too specific and should not re-display the input without output filtration.

Output filtration most commonly happens in Rails' view part of the application. If there is absolutely no HTML allowed in the user input, you can filter it with Ruby's escapeHTML() (or its alias h()) function which replaces the possibly malicious input characters &,",<,> with its uninterpreted representations in HTML (&amp;, &quot;, &lt;, &gt;). However, it can easily happen that the programmer forgets to use it just in one place, and so the web application is vulnerable again. It is therefore recommended to use the Safe ERB (http://www.kbmj.com/~shinya/rails/) plugin which will throw an error if so-called tainted strings are not escaped. In Ruby, a string will be tainted if it comes from an external source (for example, from the database, a file or via the Internet) and can be untainted by Safe ERB's modified escapeHTML() function or the Object.untaint() function.

However, if your application's users need text formatting in their input, it is best to use a markup language which is not interpreted by the user agent, but by the web application. For example there is RedCloth (http://whytheluckystiff.net/ruby/redcloth/) for this.

As for character encoding, you should enforce the user's browser to use the encoding you chose. In Rails you can enforce ISO-8859-15 by adding the following lines to application.rb:

after_filter :set_charset

private
 def set_charset
   content_type = @headers["Content-Type"] || 'text/html'
   if /^text\//.match(content_type)
      @headers["Content-Type"] = "#{content_type}; charset=ISO-8859-15"
   end
 end

And in the controller you can convert encodings, for example ISO-8859-15 to UTF-8, with this:

sconvcomment = Iconv.new('ISO-8859-15', 'UTF-8').iconv(params[:comment])