Monday, October 20, 2008

XSS Terminate

There may eventually be a time when you need to allow people to write some HTML on your site. Maybe you have a bulletin board or something or else possibly you are writing some kind of email app or wiki.

The main problem with this is that you potentially open your site to Cross Site Scripting (XSS) attacks (more info at Basically in an XSS attack, someone puts in a little JavaScript in your site via a user input and when someone else looks at the page the first person steals the other's cookies. In fact you can unwittingly open your site to XSS attacks just by forgetting to encode all your user inputted text by using the h() function.

This is essentially a backwards approach to the problem however. What you really want to do is close everything down by default and then make a mental effort to allow it to use HTML. Even then, you may run into the problem of people running script tags.

Enter XSS Terminate. This is a handy little plug in which will basically strip all HTML tags out of any field going to and from the database by default. You can additionally apply either Rails' sanitize method to strip out scripts while allowing some basic HTML tags or else use html5lib_sanitize to strip out the scripts while allowing for normal browser quirks (sanitize requires strict XHTML standards).

In any case, the real reason I am writing this is that the XSS terminate usage instructions contain a couple of errors.

Where the site says

  class Review < ActiveRecord::Base
xss_sanitize :sanitize => [ :body, :author_name]
I found this didn't work for me. After some trial and error I found the following fix

  class Review < ActiveRecord::Base
xss_terminate :sanitize => [ :body, :author_name]
I also had problems with
class Message < ActiveRecord::Base
xss_terminate :except => [ :body ], :sanitize => [ :title ]
instead I did the following

class Message < ActiveRecord::Base
xss_terminate :except => [ :body, :title ]
xss_terminate :sanitize => [ :title ]
Basically though you probably don't want to use :except without using :sanitize or :html5lib_sanitize. Ever... And I mean it... Otherwise the next email you get will be from your sysadmin (if you are lucky) or your lawyer (if you are not).

In any case, the next problem is that the HTML elements entered by the user will probably read your default stylesheets. While you may want this, you may actually want the HTML to look kind of clean and distinct from your site (to highlight the fact that it is user generated content).

There is no easy way unfortunately to reset the css in a specific section so I had to search around for a default browser stylesheet online and then manually prefix every element with .text-entry. Because I am a nice guy, I am including it here (just do a text replace on .text-entry to use your own class name).


.text-entry div,.text-entry dl,.text-entry dt,.text-entry dd,.text-entry ul,.text-entry ol,.text-entry li,.text-entry h1,.text-entry h2,.text-entry h3,.text-entry h4,.text-entry h5,.text-entry h6,.text-entry pre,.text-entry form,.text-entry fieldset,.text-entry input,.text-entry textarea,.text-entry p,.text-entry blockquote,.text-entry th,.text-entry td {
margin:0 !important ;
padding:0 !important ;
background:none !important;
background-color:none !important ;
border:none !important ;
font-family: Arial, Helvetica, sans-serif !important ;
color:#000 !important;
text-transform:none !important;
.text-entry table {
.text-entry fieldset,.text-entry img {
.text-entry address,.text-entry caption,.text-entry cite,.text-entry code,.text-entry dfn,.text-entry em,.text-entry strong,.text-entry th,.text-entry var {
.text-entry caption,.text-entry th {
.text-entry h1,.text-entry h2,.text-entry h3,.text-entry h4,.text-entry h5,.text-entry h6 {
.text-entry q:before,.text-entry q:after {
.text-entry abbr,.text-entry acronym { border:0;

.text-entry address,
.text-entry blockquote,
.text-entry dd, .text-entry div,
.text-entry dl, .text-entry dt, .text-entry fieldset, .text-entry form,
.text-entry h1, .text-entry h2, .text-entry h3, .text-entry h4,
.text-entry h5, .text-entry h6,
.text-entry ol, .text-entry p, .text-entry ul, .text-entry center,
.text-entry dir, .text-entry hr, .text-entry menu, .text-entry pre { display: block }
.text-entry li { display: list-item; margin-left: 10px !important }
.text-entry head { display: none }
.text-entry table { display: table }
.text-entry tr { display: table-row }
.text-entry thead { display: table-header-group }
.text-entry tbody { display: table-row-group }
.text-entry tfoot { display: table-footer-group }
.text-entry col { display: table-column }
.text-entry colgroup { display: table-column-group }
.text-entry td, .text-entry th { display: table-cell }
.text-entry caption { display: table-caption }
.text-entry th { font-weight: bolder; text-align: center }
.text-entry caption { text-align: center }
.text-entry body { margin: 8px }
.text-entry h1 { font-size: 2em; margin: .67em 0 }
.text-entry h2 { font-size: 1.5em; margin: .75em 0 }
.text-entry h3 { font-size: 1.17em; margin: .83em 0 }
.text-entry h4, .text-entry p,
.text-entry blockquote, .text-entry ul,
.text-entry fieldset, .text-entry form,
.text-entry ol, .text-entry dl, .text-entry dir,
.text-entry menu { margin: 1.12em 0 }
.text-entry h5 { font-size: .83em; margin: 1.5em 0 }
.text-entry h6 { font-size: .75em; margin: 1.67em 0 }
.text-entry h1, .text-entry h2, .text-entry h3, .text-entry h4,
.text-entry h5, .text-entry h6, .text-entry b,
.text-entry strong { font-weight: bolder }
.text-entry blockquote { margin-left: 40px; margin-right: 40px }
.text-entry i, .text-entry cite, .text-entry em,
.text-entry var, .text-entry address { font-style: italic }
.text-entry pre, .text-entry tt, .text-entry code,
.text-entry kbd, .text-entry samp { font-family: monospace }
.text-entry pre { white-space: pre }
.text-entry button, .text-entry textarea,
.text-entry input, .text-entry select { display: inline-block }
.text-entry big { font-size: 1.17em }
.text-entry small, .text-entry sub, .text-entry sup { font-size: .83em }
.text-entry sub { vertical-align: sub }
.text-entry sup { vertical-align: super }
.text-entry table { border-spacing: 2px; }
.text-entry thead, .text-entry tbody,
.text-entry tfoot { vertical-align: middle }
.text-entry td, .text-entry th { vertical-align: inherit }
.text-entry s, .text-entry strike, .text-entry del { text-decoration: line-through }
.text-entry hr { border: 1px inset }
.text-entry ol, .text-entry ul, .text-entry dir,
.text-entry menu, .text-entry dd { margin-left: 40px }
.text-entry ol { list-style-type: decimal }
.text-entry ol ul, .text-entry ul ol,
.text-entry ul ul, .text-entry ol ol { margin-top: 0; margin-bottom: 0 }
.text-entry u, .text-entry ins { text-decoration: underline }
.text-entry br:before { content: "\A" }
.text-entry :before, .text-entry :after { white-space: pre-line }
.text-entry center { text-align: center }
.text-entry :link, .text-entry :visited { text-decoration: underline }
.text-entry :focus { outline: thin dotted invert }
.text-entry a {color:blue !important;}
.text-entry a:visited {color:purple !important;}


Anthony B said...

I just wanted to make an update to this article...

It appears that the standard Rails 2 sanitize function is a little heavy handed for use with Yahoo's Rich Text Editor. Yes it allows HTML and strips out any script tags, but it also strips out the style attribute as well.

In any case, replacing it with html5lib_sanitize seems to do with trick...

Anonymous said...

I found this site using [url=][/url] And i want to thank you for your work. You have done really very good site. Great work, great site! Thank you!

Sorry for offtopic

Anonymous said...

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!

Anonymous said...

[url= ]vendo cialis online[/url] [url= ] viagra en espana[/url] [url= ] cialis online[/url] [url= ]comprar viagra [/url] [url= ]vendo cialis [/url] [url= ]comprar viagra en espana[/url]

Anonymous said...

[url=]cialis erection[/url]

Anonymous said...

Very nicce!