Product SiteDocumentation Site

3.1.2. Cross site scripting (XSS)

Cross site scripting (usually abbreviated as XSS) is a special type of command injection, where attacker supplied malicious payload is interpreted within the context of victim page. The weakness that is a cause for this vulnerability is embedding untrusted data in the web page without sanitization. In the most trivial example, imagine social networking website which displays names of users without sanitization to others. If the name is pasted into resulting page as-is, one can change his name to
<script>alert('Hello');</script>
This code embedded in the html sent by the server will be parsed in client's browsers and interpreted as javascript code, which runs and displays a popup with a greeting.

3.1.2.1. Types of XSS

Over the time XSS attack evolved and based on the way payload is delivered we recognize three types.
Reflected XSS is the most common type, when data sent by the user is directly embedded into a web page by server and returned back to user. For example think of query parameters which are displayed in a web page:
https://bugzilla.redhat.com/show_bug.cgi?id=<script>alert('Hello')</script>
The attack vector in this example is attacker tricking a user into clicking on this malicious URL. If the server returns back user supplied bugzilla ID without escaping, it would get interpreted in the context of bugzilla.redhat.com. Looking at the source of actual response, we find that value provided is escaped:
'&lt;script&gt;alert(10)&lt;/script&gt;' is not a valid bug number nor an alias to a bug.
In Stored XSS the payload is saved on the server and later displayed to the users. Examples include unescaped user profile data on social networking site, unescaped posts on internet forums, unescaped filenames on file sharing sites etc.
DOM based XSS is the newest and so far least common type of XSS. With more and more webpage processing moving over to client side in form of javascript code and frameworks, users can browse sites and change them without sending data to be processed by the server. For example hypothetical vulnerable webpage would contain a javascript to display current URL of the page:
<script>
document.write("URL is: " + document.location.href);
<script>
If attacker appends fragment with script to the URL, script running in client's browser would inject URL as-is along with the payload. The URL would look like this:
http://vulnerable.com/#<script>alert('Hello')</script>
Note that fragment part of URL is not usually sent to the server in the HTTP request.

3.1.2.2. XSS protection in Rails

In past Ruby on Rails required developers to explicity escape any potentially unsafe strings with html_escape() method (or alias h()). This is methodically bad approach to security, since any omission of the method on critical place lead to potentiall exploitable weakness.
Since Rails 3 instead or relying on developer to provide html escaped output Rails automatically escapes everything. This is implemented in ActiveSupport::SafeBuffer class, which extends String and represents escaped (i.e. html safe) version of a string. The most important aspect of SafeBuffer is that it stays html safe even when standard String methods are called with unsafe inputs:
> sb = ActiveSupport::SafeBuffer.new
 => "" 
> sb << "<string>"
 => "&lt;string&gt;"
> sb + "</string>"
 => "&lt;string&gt;&lt;/string&gt;"
This way a response to the client can be built part by part without allowing unsafe characters. If a string is safe and can be rendered without escaping, developer has to mark it html safe explicitly using html_safe method. This method actually returns an instance of SafeBuffer:
> safe =  "  <b>HTML safe</b> string".html_safe
 => "  <b>HTML safe</b> string" 
> safe.class
 => ActiveSupport::SafeBuffer 
> sb << safe
 => "&lt;string&gt;&lt;/string&gt;  <b>HTML safe</b> string" 

3.1.2.3. Real world examples

With automatic escaping of output XSS vulnerabilities in Rails are much less common, but still occur. One common cause is misuse of html_safe() on untrusted strings. Example of this is CVE-2014-3531: XSS flaw in operating system name/description in Foreman. Weakness in this case was present in helper method, which returned name of the OS:
def os_name record, opts = {}
  "#{icon(record, opts)} #{record.to_label}".html_safe
end
The first part of string, icon(record, opts) returns html safe string, but second part is untrusted and may contain html unsafe characters. Calling html_safe on the resulting String caused vulnerability and the fix was straightforward:
def os_name record, opts = {}
  icon(record, opts).html_safe << record.to_label
end
Another way to create XSS vulnerability in Rails is to bypass the automatic ERB escaping by rendering response directly. Example of this is CVE-2014-3492: XSS from stored YAML in Foreman. Vulnerable code serialized untrusted data into YAML and directly rendered it on page as preview:
begin
  respond_to do |format|
    format.html { render :text => "<pre>#{@host.info.to_yaml}</pre>" }
    format.yml { render :text => @host.info.to_yaml }
  end
rescue
...
In this case output has to be explicitly escaped before returning to the client:
begin
  respond_to do |format|
    format.html { render :text => "<pre>#{ERB::Util.html_escape(@host.info.to_yaml)}</pre>" }
    format.yml { render :text => @host.info.to_yaml }
  end
rescue
...

3.1.2.4. References