Rails .html_safe - The Hidden XSS Trap

posted Originally published at medium.com 2 min read

Ruby on Rails has your back when it comes to cross-site scripting (XSS) - by default, it escapes all strings in your views so malicious HTML and JavaScript can't execute in your users' browsers.
But there's a sharp tool in Rails' toolbox called .html_safe. Used incorrectly, it can silently remove that protection and leave your app wide open to attack.


What .html_safe Does
Normally, if you try to output a string with HTML in Rails:

<%= "<b>Hello</b>" %>

Renders as:

&lt;b&gt;Hello&lt;/b&gt;

If you call .html_safe:

<%= "<b>Hello</b>".html_safe %>

Renders as:

<b>Hello</b>

That's fine for safe, static strings - but deadly for user input.


The Vulnerability in Action
Let's say you have a simple comments feature.

Controller (comments_controller.rb):

class CommentsController < ApplicationController
  def index
    @comments = Comment.all
  end

  def create
    Comment.create!(body: params[:comment][:body])
    redirect_to comments_path
  end
end

Routes (routes.rb)

Rails.application.routes.draw do
  resources :comments, only: [:index, :create]
end

View (views/comments/index.html.erb):

<h1>Comments</h1>
<%= form_with url: comments_path, method: :post do %>
  <%= text_area_tag 'comment[body]' %>
  <%= submit_tag 'Post Comment' %>
<% end %>
<ul>
  <% @comments.each do |comment| %>
    <li><%= comment.body.html_safe %></li> <!-- Dangerous -->
  <% end %>
</ul>

Exploiting the Bug
An attacker submits this as a comment:

<script>alert('Hacked!');</script>

With .html_safe, the browser sees:

<li><script>alert('Hacked!');</script></li>

…and executes the script - you've got an XSS vulnerability.
Without .html_safe, Rails would render:

<li>&lt;script&gt;alert('Hacked!');&lt;/script&gt;</li>

…showing the script tag as plain text and keeping your users safe.


How to Fix It

Never mark raw user input as .html_safe.
Instead:
Safe auto-escaping (Rails default):

<li><%= comment.body %></li>

If you allow some HTML formatting:

<li><%= sanitize(comment.body) %></li>

Rails' sanitize strips dangerous tags while keeping safe ones.


Final Thoughts

Rails' .html_safe is like turning off your seatbelt because it feels restrictive - sure, you have more "freedom," but one mistake and you're flying through the windshield.
Use it only when you are 100% certain the content is safe. When in doubt, let Rails escape it or sanitize it.

If you read this far, tweet to the author to show them you care. Tweet a Thanks
0 votes
0 votes

More Posts

Feature Flags | Controlled Chaos in Production

BrazenBraden - Sep 9

Better Sidekiq Classes

BrazenBraden - Aug 28

JuggleBee’s Great Leap - Rebuilding a Rails 4 App in Rails 8 (Part 1)

BrazenBraden - Aug 21

Welcome to the Ruby & Rails Community

BrazenBraden - Oct 1

Rails releases security updates for ReDoS vulnerabilities—upgrade now to the latest versions for optimal protection!

ShahZaib - Oct 15, 2024
chevron_left