Why is my view being flagged as an XSS vulnerability? - ruby-on-rails

I have a show route that displays the contents of my article
Controller:
def show
#article = Article.find(params[:id])
end
View:
...
<li class="content"><%= #article.content.html_safe %></li>
...
When running Brakeman, it flags the above as a potential Cross-Site Scripting (XSS) vulnerability
Unescaped model attribute near line 34: Article.find(params[:article_id]).content
I'm trying to figure out what XSS really is and what makes this vulnerable? If someone injected some malicious text or input into the params[:id] field in the route (e.g. /articles/BAD_INPUT) then Article.find() would not find the article and raise an error
The only way the view renders is if a valid Article record is found, right? How else can the user manipulate this?
Thanks!
Edit: I should definitely protect agains the case when an Article is not found and an error is raised, but I figured that's more of a bad design rather than a security vulnerability

Brakeman is warning because the code is taking information from the database and outputting it in a view without escaping it. By default, Brakeman treats values from the database as potentially dangerous. In this case, you probably know the article content is intended to be HTML and is safe to output without escaping it. If you wish to not warn about XSS with values from the database, you can use the --ignore-model-output option.
(The issue you linked in your answer is not really related. Brakeman is expected to warn about uses of raw/html_safe with potentially dangerous values.)

Ok found the answer after some digging.
It apparently has to do with html_safe and raw (which is just an alias for html_safe). The issue is specific to Brakeman and outlined here
That thread says the issue is acknowledged and solved, but it still didn't work for me using the latest version.
I solved it as follows
Controller:
def show
#article = Article.find(params[:id])
#article_content = view_context.raw(#article.content)
end
View:
...
<li class="content"><%= #article_content %></li>
...
Essentially we're marking the Article content as html_safe (using the alias raw()) beforehand so it doesn't cause an issue in the view.
Messier than I'd like, but it works

If you are storing html on your model and you are on Rails 4.2++, you could consider using the sanitize helper (docs).
For example, you can allow specific tags (e.g. links):
<%= sanitize #article.content, tags: %w(a), attributes: %w(href) %>
The docs have a lot of good examples.
Here's another write-up if you want some more information.

Related

XSS vulnerability in Rails (merged params)

I recently discovered (thanks to a bug bounty user) that my site has XSS vulnerabilities. After some playing around, I discovered the cause is in our locale switch in the navbar, such as:
<%= link_to "日本語", params.merge(locale: "ja", only_path: true), target:"_self", onClick:"ga('send','event','Navbar','Link Clicked','Language setting ja');" %>
This allows malicious code to be freely injected but I cannot for the life of me work out how to fix it without either breaking the locale switch or the creation of alternative issues.
I have checked other XSS related posts on here but I think the issue is a little bit above my ability to even comprehend, let alone fix right now. If anyone has any advice on how to prevent this XSS problem, I will be extremely grateful as it has become quite stressful!
The answer ended up being ridiculously simple so I will post here just in case anyone else ever searches for a similar problem.
As per http://guides.rubyonrails.org/i18n.html#setup-the-rails-application-for-internationalization I started by adding this to the application_controller.rb file:
def default_url_options(options={})
{ locale: I18n.locale }
end
With that, I could simply delete most of the original (vulnerable) locale switch and now have:
<li><%= link_to "日本語", locale: "ja" %></li>
Vulnerability removed yet functionality retained. Really was that simple. Dealing with legacy code can be a pain at times, thankfully this wasn't anywhere near as complicated as I initially assumed.

Allows user to edit pages that contain variables [duplicate]

This question already has an answer here:
How to render a string as an erb file?
(1 answer)
Closed 9 years ago.
I have some editable pages that are stored as text in my database. These pages will be called in my view like...
#app/views/static_pages/scheduling_text.html.erb
<%= Page.find_by_name("New Registration").content %>
Page.content is of type 'text' (not string). A portion of the text that contains variables would look like...
You have successfully registered for New Student Orientation on #{<%= #registration.orientation.class_date %>} at...
If course when I call this content in the view, I just get the text, not the model values. How can I make these pages access the model values? I also tried adding #{} around the text without success.
This seems to be a duplicate of Rails: storing erb templates in database
Given that, this should do the trick for you, or at least be close enough to get you started:
<%= sanitize ERB.new(Page.find_by_name("New Registration").content).run %>
Additionally, you can remove the sanitize if content is not user-supplied (this is primarily a security concern).
I've done something exactly like this using HAML processing:
= sanitize Haml::Engine.new(article.content).render
For reference, here's the appropriate ERB documentation: http://ruby-doc.org/stdlib-2.0.0/libdoc/erb/rdoc/ERB.html
OK, after much wailing and gnashing of teeth here is the the solution I cam up with. First I am using mustache. This is much safer than storing erb in the templates and prevents malicious injection of sql into your app. I simply added 'mustache' to my gemfile rather than mustache-rails as it seems to be more up to date. I then created a simple Page model with two attributes: :name and :content. I am using the page model to store the raw mustache code.
Here are the relevant files...
In my controller I call...
#app/controllers/registrations_controller.rb
def create
#registration = Registration.new(params[:registration])
respond_to do |format|
if #registration.save
if #registration.orientation != nil
format.html { render "scheduling_text.html.erb" }
Then my view looks like...
#app/views/registrations/scheduling_text.html.erb
<%= Mustache.render(Page.find_by_name("New Registration").content, {:registration => #registration }).html_safe %>
<%= link_to 'Back', orientations_path %>
...
Then in my page model I have something like...
You have successfully registered for New Student Orientation on {{ registration.orientation.class_date }} at {{ registration.orientation.class_time}}. Please arrive 10 minutes prior to your scheduled Orientation. Remember, you must attend this Orientation session before you may register for classes. ...
Using a page model with scaffolding like this works well because it gives you the new, update, and create actions that will allow users to edit content. Note, they can easily mess up your ruby variables, so thats the downside. Just let your users know to not munk with anything that is between {{}}.
Hope this helps someone else out.

How to secure link_to #variable cross site scripting vulnerabilities

I've just started using the brakeman gem to explore my rails app for security vulnerabilities.
I've managed to get everything tidy except for several cross site scripting warnings.
These all share the following in common:
They're all link_to tags
They all have instance variables in the class, alt or title
attributes
The instance variables all represent an active record query that
includes associated models
The instance variables are all "commentable". This describes a polymorphic association for user generated comments, similar in approach to the revised version of this Railscast.
e.g
<%= link_to "Click" , :class=> #model.association.attribute, :alt=> #model.association.attribute, :title=> #model.association.attribute, #model.association %>
where
#model = #commentable = Model.includes(:association1, association2: {:nested-association1, :nested-association2}).find(params[:id])
Is this something I need to be concerned about/ take action for? I thought Rails 3.2 escapes these by default.
I'd welcome advice to help me understand this issue better, and identify what steps I should take, if any.
I was unable to reproduce any warnings from the code you provided. What version of Brakeman are you using? What was the actual warning (redacted as necessary)?
I suspect you are getting warnings because user input is being detected in the href value of the link. See this pull request for more information about why this can be dangerous.
Unfortunately, without more information, I cannot tell if this is a false positive that needs to be fixed or a legitimate warning.
Edit:
Okay, now I am seeing the warning when testing with #model = #commentable = ... This is a problem with how Brakeman is handling the assignment.
If you are linking to an instance of a model, there should be no warning. If you are linking to a model attribute then this is counted as user input.
Yes, Rails will escape HTML, but it does not deal with links beginning with javascript: or data: which can be used for XSS.

ID from Controller#show

I would like to retrieve the id of the current item in a Controller#show page. How is this done? I can't seem to figure this out or find it anywhere.
You're probably looking for:
<%= params[:id] %>
There are lots of things wrong with doing this, one of which is it can open you up to CSRF attacks. Make sure to escape your output (this is done automatically in rails 3.)

Any danger in calling flash messages html_safe?

I want a flash message that looks something like:
"That confirmation link is invalid or expired. Click here to have a new one generated."
Where "click here" is of course a link to another action in the app where a new confirmation link can be generated. Two drawbacks: One, since link_to isn't defined in the controller where the flash message is being set, I have to put the link html in myself. No big deal, but kind of messy.
Number two: In order for the link to actually display properly on the page I have to html_safe the flash display function in the view, so now it looks like (using Haml):
- flash.each do |name, message|
= content_tag :div, message.html_safe
This gives me pause. Everything else I html_safe has been HTML I've written myself in helpers and whatnot, but the contents of the flash hash are stored in a cookie client-side, and could conceivably be changed. I've thought through it, and I don't see how this could result in an XSS attack, but XSS isn't something I have a great understanding of anyway.
So, two questions:
1. Is there any danger in always html_safe-ing all flash contents like this?
2. The fact that this solution is so messy (breaking MVC by using HTML in the controller, always html_safe-ing all flash contents) make me think I'm going about this wrong. Is there a more elegant, Rails-ish way to do this?
I'm using Rails 3.0.0.beta3.
It depends on how sure you are where the contents for the message come from. If there is any possibility that any user could manipulate that message, then you should not do this!
I wouldn't do it either way. Because it could happen that you now know that every string is safe, but than you change one controller and add a message which could contain user input, than you have a possible vulnerability.
I would set any message html_safe when it is added to the flash and you know for sure it is safe.
For example
class SomeController < ApplicationController
def some_action
flash[:info] = 'Some safe text!'.html_safe
flash[:unsecure] = User.find(1).signature #//evil code
end
end
And in your view you can do it like this:
- flash.each do |name, message|
= content_tag :div, message
This way you make sure that if you add a new flash message that isn't safe, it would be made safe in the view by mistake.
In this case the flash[:info] message is printed as html_safe and flash[:unsecure] will be escaped, so the user evil javascript code will not be executed.
If you know there is no possibility that there is any unfiltered user input in the message it should be safe to use html_safe on the flash messages.
I didn't want to tempt fate by html_safe-ing all flash messages universally, so I decided to just redirect failed confirmation link attempts directly to the url I would have linked them to anyway. It's a simpler, more elegant solution, I think.

Resources