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:
<b>Hello</b>
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><script>alert('Hacked!');</script></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.