I was pushed to put XSS protections into CrowdVine when one of the Foo Camper’s released this XSS crack into the Foo Camp social network. It causes a person to friend everyone in the network and then inserts itself into your profile. It was brutal. Worse it was a very simple and readable 49 lines of code. I took one glance at it and realized that even I know enough javascript to write one of these.
Looking around I saw two approaches for Rails. Run Safe ERB and force yourself to validate each individual input or run Rick Olson’s white list plugin.
I decided to use the white_list plugin to clean all values in params. It required a little bit of tweaking. Here’s the details.
Install the white list plugin:
script/plugin install -x http://svn.techno-weenie.net/projects/plugins/white_list/
Edit vendor/plugins/white_list/init.rb so that white_list is available in the Controller:
require 'white_list_helper' ActionController::Base.send :include, WhiteListHelper
Add a filter to application.rb in order to walk the params hash:
before_filter :sanitize_params def sanitize_params # TODO: 2007-06-20 -- I found that this didn't # work when called with params instead of @params. I assume # I'm clueless in some important regard. (Many important regards?) @params = walk_hash(@params) if @params and !site_owner? end def walk_hash(hash) hash.keys.each do |key| if hash[key].is_a? String hash[key] = white_list(hash[key]) elsif hash[key].is_a? Hash hash[key] = walk_hash(hash[key]) elsif hash[key].is_a? Array hash[key] = walk_array(hash[key]) end end hash end def walk_array(array) array.each_with_index do |el,i| if el.is_a? String array[i] = white_list(el) elsif el.is_a? Hash array[i] = walk_hash(el) elsif el.is_a? Array array[i] = walk_array(el) end end array end
Does this look right to people? Is there a more idiomatic ruby/rails way to do this? I’m a bit worried about how this will perform on very large chunks of data or on deeply nested hashes.