How to view content security policy violation reports in rails app? - ruby-on-rails

I used secure_headers gem https://github.com/twitter/secureheaders and i configure the csp as
config.csp = {
:enforce => true,
:default_src => 'http://* inline',
:report_uri => "/report",
:connect_src => 'self',
:style_src => 'self inline',
:script_src => 'self inline eval',
:font_src => 'self'
}
but still i can't view the reports in my http://localhost:3000/report and page is not redirecting

EDIT:
https://report-uri.io/ offers CSP reporting capabilities. They give you a report-uri, and they manage the incoming reports!
Currently the gem does not have any built-in support for aggregating/viewing the reports. This question got me thinking so I filed https://github.com/twitter/secureheaders/issues/71
Please add your thoughts. I don't think it is a trivial task to build something meaningful, but I'm beginning to see how valuable it could be. There's also a good amount of low hanging fruit that might be good enough for the time being.

The Secure Headers Gem does not provide a reporting endpoint for CSP violations. It is something you would have to build yourself or use a solution that provides both out of the box.
I posted an overview of the different ways of deploying a Content Security Policy with Ruby on Rails, including SecureHeaders Gem and Templarbit (which includes a reporting endpoint): https://www.templarbit.com/blog/2018/03/14/content-security-policy-with-ruby-on-rails

Related

How can I disable Hotwire / Turbo (the Turbolinks replacement) for all forms in a Rails application?

In Rails 7, Turbolinks is replaced by Hotwire / Turbo. This patches up web links so that they become AJAX-style requests just like Turbolinks did, but also patches up forms. On the whole, we found this broke swathes of our application.
Forms that already had some kind of JS behaviour attached conflicted.
Standard Rails error handling idioms - "set the flash and render :new/render :edit" - break, astonishingly; one must add status: :unprocessable_entity and cross fingers.
If the controller handling a form submission redirects to a URL that includes a patch anchor (...#foo), Turbo strips it.
In the end, there's only so much data: {turbo: false} it makes sense to scatter all over your code base. Worse, we're using SimpleForm, so this becomes the even-more-cumbersome html: {data: {turbo: false}}.
Is there a way to _globally disable Turbo just for forms (ideally all forms, whatever their origin - just leave the <form> tag and everything inside it completely alone please) but leave the rest of its behaviour unchanged?
I posted this Q&A on Stack Overflow as I couldn't find any other solutions out there. Clearly, there are numerous shortcomings and I would much prefer to see a simple configuration flag for Turbo that would make it ignore forms - this would be an overwhelmingly preferable approach.
I still don't have a full solution either way, because forms generated internally by Rails for e.g. a link_to(...method: delete) are still a problem, but I did work around some of it using some monkey patching.
On the one hand there are Rails forms:
Apparently, a data-turbo attribute's value apples to that node and all children so one can wrap e.g. a Rails-generated 'dynamic' form from e.g. a link_to(...method: delete) in a DIV with that attribute set and, at least, work around those problems on a case-by-case basis - though note, I'm having trouble making this work in some cases still.
If you have a lot of these, though, that's going to be intrusive and ugly, so it'd still be nice to have a way to have Turbo just ignore forms completely.
On the other hand there are SimpleForm forms:
SimpleForm provides no way to globally configure data attributes that will be added to form elements that it constructs. Previous requests for this in GitHub issues have so far been explicitly refused.
SimpleForm appears to provide no way to configure a wrapper that would go around the form as a whole, only custom wrappers for inputs within a form. So we can't easily just e.g. write a wrapper DIV as mentioned above.
I happened to be involved in a gem called Hoodoo that provides monkey patching facilities that lets you write patch modules which are more like subclasses - super is available to call up to the patched implementation - and, further, patches can be enabled or disabled dynamically and easily. Hoodoo is actually a Rack application service framework, so this is something of a sledgehammer - I always intended to one day extract this into its own gem, but at the time of writing, I have not got around to it (and several years have gone by) - but we can require just the part we need and ignore the rest.
Here, I patch SimpleForm's builder methods. These just call Rails' form helpers under the hood, so I might have a go at patching lower down in Rails instead, but anyway, the following worked.
In your Gemfile, declare the Hoodoo dependency but don't load all of its component parts as you won't want most of them.
# Hoodoo's monkey patch module is useful sometimes:
# https://rubygems.org/gems/hoodoo
#
# MUST use 'require: false' so that the Rack components of Hoodoo middleware
# do not get activated; this is a Rails app, not a Hoodoo service!
#
gem 'hoodoo', '~> 2.12', require: false
...then write something like config/initializers/simple_form_monkey_patch.rb which looks something like this:
require 'hoodoo/monkey'
module SimpleFormMonkey
module InstanceExtensions
def simple_form_for(record, options = {}, &block)
modified_options = {html: {data: {turbo: false}}}
modified_options.deep_merge!(options)
super(record, modified_options, &block)
end
end
end
Hoodoo::Monkey.register(
extension_module: SimpleFormMonkey,
target_unit: SimpleForm::ActionViewExtensions::FormHelper
)
Hoodoo::Monkey.enable(
extension_module: SimpleFormMonkey
)
...that'll do it. This has some risk as we're patching things which are - in terms of the module name & nesting - technically private to SimpleForm, but the method signature itself is at least public. You could patch ActionView::Helpers::FormHelper with an override for form_for instead, if you wanted to go lower level and patch an API that's been stable for a very long time. The code would be almost identical as the method signatures are the same.
I used Javascript to solve this problem:
document.querySelectorAll('form').forEach(function (el) {
el.dataset.turbo = false
})
No more flaky system tests due to randomly missing flash messages/alerts after form submissions and Devise also works perfectly again.
#kamilpogo your solution worked like a charm! ty!!! currently learning and following this GoRails tutorial before i ran into some turbo trouble trying to render "Thanks!" for anyone else out there.
basically, i added the javascript to in /layouts/application.html.erb like below:
<script>
document.querySelectorAll('form').forEach(function (el) {
el.dataset.turbo = false
})
</script>
<script src="js-bootstrap"></script>
</body>
</html>
UPDATE: comments in the videos have a different work around by setting turbo to 'false' and local to 'true' inside the form:
form_with model: #user, url: sign_up_path, local: true, data: { turbo:false } do |form|

(JSON::ParserError) "{N}: unexpected token at 'alihack<%eval request(\"alihack.com\")%>

I have the website on Ruby on Rails 3.2.11 and Ruby 1.9.3.
What can cause the following error:
(JSON::ParserError) "{N}: unexpected token at 'alihack<%eval request(\"alihack.com\")%>
I have several errors like this in the logs. All of them tries to eval request(\"alihack.com\").
Part of the log file:
"REMOTE_ADDR" => "10.123.66.198",
"REQUEST_METHOD" => "PUT",
"REQUEST_PATH" => "/ali.txt",
"PATH_INFO" => "/ali.txt",
"REQUEST_URI" => "/ali.txt",
"SERVER_PROTOCOL" => "HTTP/1.1",
"HTTP_VERSION" => "HTTP/1.1",
"HTTP_X_REQUEST_START" => "1407690958116",
"HTTP_X_REQUEST_ID" => "47392d63-f113-48ba-bdd4-74492ebe64f6",
"HTTP_X_FORWARDED_PROTO" => "https",
"HTTP_X_FORWARDED_PORT" => "443",
"HTTP_X_FORWARDED_FOR" => "23.27.103.106, 199.27.133.183".
199.27.133.183 - is CLoudFlare IP.
"REMOTE_ADDR" => "10.93.15.235" and "10.123.66.198" and others, I think, are fake IPs of proxy.
Here's a link guy has the same issue with his web site from the same ip address(23.27.103.106).
To sum up, the common ip from all errors is 23.27.103.106 and they try to run the script using ruby's eval.
So my questions are:
What type of vulnerability they try to find?
What to do? Block the ip?
Thank you in advance.
Why it happens?
It seems like an attempt to at least test for, or exploit, a remote code execution vulnerability. Potentially a generic one (targeting a platform other than Rails), or one that existed in earlier versions.
The actual error however stems from the fact that the request is an HTTP PUT with application/json headers, but the body isn't a valid json.
To reproduce this on your dev environment:
curl -D - -X PUT --data "not json" -H "Content-Type: application/json" http://localhost:3000
More details
Rails action_dispatch tries to parse any json requests by passing the body to be decoded
# lib/action_dispatch/middleware/params_parser.rb
def parse_formatted_parameters(env)
...
strategy = #parsers[mime_type]
...
case strategy
when Proc
...
when :json
data = ActiveSupport::JSON.decode(request.body)
...
In this case, it's not a valid JSON, and the error is raised, causing the server to report a 500.
Possible solutions
I'm not entirely sure what's the best strategy to deal with this. There are several possibilities:
you can block the IP address using iptables
filter (PUT or all) requests to /ali.txt within your nginx or apache configs.
use a tool like the rack-attack gem and apply the filter there. (see this rack-attack issue )
use the request_exception_handler gem to catch the error and handle it from within Rails (See this SO answer and this github issue)
block PUT requests within Rails' routes.rb to all urls but those that are explicitly allowed. It looks like that in this case, the error is raised even before it reaches Rails' routes - so this might not be possible.
Use the rack-robustness middleware and catch the json parse error with something like this configuration in config/application.rb
Write your own middleware. Something along the lines of the stuff on this post
I'm currently leaning towards options #3, #4 or #6. All of which might come in handy for other types of bots/scanners or other invalid requests that might pop-up in future...
Happy to hear what people think about the various alternative solutions
I saw some weird log entries on my own site [which doesn't use Ruby] and Google took me to this thread. The IP on my entries was different. [120.37.236.161]
After poking around a bit more, here is my mostly speculation/educated guess:
First, in my own logs I saw a reference to http://api.alihack.com/info.txt - checked this link out; looked like an attempt at a PHP injection.
There's also a "register.php" page there - submitting takes you to an "invite.php" page.
Further examination of this domain took me to http://www.alihack.com/2014/07/10/168.aspx (page is in Chinese but Google Translate helped me out here)
I expect this "Black Spider" tool has been modified by script kiddies and is being used as a carpet bomber to attempt to find any sites which are "vulnerable."
It might be prudent to just add an automatic denial of any attempt including the "alihack" substring to your configuration.
I had a similar issue show up in my Rollbar logs, a PUT request to /ali.txt
Best just to block that IP, I only saw one request on my end with this error. The request I received came from France -> http://whois.domaintools.com/37.187.74.201
If you use nginx, add this to your nginx conf file;
deny 23.27.103.106/32;
deny 199.27.133.183/32;
For Rails-3 there is a special workaround-gem: https://github.com/infopark/robust_params_parser

In Rails, how can I set a cookie's Max-Age?

In Rails 3.2.8, you can write a cookie like this:
cookies[:login] = {
:value => "XJ-122",
:expires => 1.hour.from_now
}
The docs say that these are the available option symbols:
:value
:path
:domain
:expires
:secure
:httponly
I would expect :max_age to be available as an option too, but perhaps user agent support is not widespread enough yet (?) to warrant including it.
So how should I set a cookie's Max-Age in Rails?
I read over the Rails source code for ActionDispatch::Cookies. If you look at how the handle_options method is used you will see that even options not specified in the documentation are passed through. Rails usually passes options around quite liberally, with the philosophy that, somewhere down the line, a method will know what to do with the left-over options.
So, I would suggest that you give it a try with the :max_age option, even though it is not documented, and see what happens.
Note: Rails relies on Rack to set the cookie header, so if for some reason the "Max-Age" "Set-Cookie" header is being passed to Rack but is not being passed through, I would ask over on the Github Rack issue tracker.
Update #1: there has been at least one pull request having to do with Max-Age and Rack, but I'm not sure it is relevant. If the above doesn't work, I think you may want to discuss on the Rack ticket tracker as I mention above.
Update #2: Have you looked at the Rack::Cache middleware? It may be of use.

Is there any way to display Rails' log files in the browser?

This might be (read: probably is) a dumb question, but here goes...
Is there a simple, preferably non third-party, way to display Rails logs in the browser? It gets kind of old opening a log file, then closing it and re-opening it on the next error. It would be superfantastic to just to go domain.tld/logs, which would require user authentication, of course.
For reference, there is a very simple way to do this. However, you would want to protect this page with some sort of authentication/authorization.
Controller:
lines = params[:lines]
if Rails.env == "production"
#logs = `tail -n #{lines} log/production.log`
else
#logs = `tail -n #{lines} log/development.log`
end
Log File View:
<pre><%= #logs %></pre>
View to display link:
<%= link_to "log file", log_path(lines: 100) %>
Routes:
get 'logs/:lines' => "pages#log", as: "log"
All you need is to open log file and put its content into browser.
Realted topic: ruby: all ways to read from file.
Also you should know that your logs grow very fast, and it's not a good idea to show whole log in browser. You can just open last 100-200 rows. Related topic: Reading the last n lines of a file in Ruby?
Also you can try this solution: http://newrelic.com/. It is more complex and little oftopic, but quite useful.
I created a for this purpose called browserlog.
Installing is just a matter of adding the gem to the Gemfile:
gem 'browserlog'
Afterwards all you need is to mount the engine on the route you want:
MyApp::Application.routes.draw do
mount Browserlog::Engine => '/logs'
end
Once that's set up, accessing /logs/development (or production, test, etc) will show a window like this:
(source: andredieb.com)
Pimp my Log can be used to visualization many types of logs including Ruby on Rails.
It also includes the management of users and notification.
[Edited]
By default, the PimpMyLog does not support Ruby on Rails logs. It's necessary to implement the regex to works with it.
[/Edited]

Rails Binary Stream support

I'm going to be starting a project soon that requires support for large-ish binary files. I'd like to use Ruby on Rails for the webapp, but I'm concerned with the BLOB support. In my experience with other languages, frameworks, and databases, BLOBs are often overlooked and thus have poor, difficult, and/or buggy functionality.
Does RoR spport BLOBs adequately? Are there any gotchas that creep up once you're already committed to Rails?
BTW: I want to be using PostgreSQL and/or MySQL as the backend database. Obviously, BLOB support in the underlying database is important. For the moment, I want to avoid focusing on the DB's BLOB capabilities; I'm more interested in how Rails itself reacts. Ideally, Rails should be hiding the details of the database from me, and so I should be able to switch from one to the other. If this is not the case (ie: there's some problem with using Rails with a particular DB) then please do mention it.
UPDATE: Also, I'm not just talking about ActiveRecord here. I'll need to handle binary files on the HTTP side (file upload effectively). That means getting access to the appropriate HTTP headers and streams via Rails. I've updated the question title and description to reflect this.
As for streaming, you can do it all in an (at least memory-) efficient way. On the upload side, file parameters in forms are abstracted as IO objects that you can read from; on the download side, look in to the form of render :text => that takes a Proc argument:
render :content_type => 'application/octet-stream', :text => Proc.new {
|response, output|
# do something that reads data and writes it to output
}
If your stuff is in files on disk, though, the aforementioned solutions will certainly work better.
+1 for attachment_fu
I use attachment_fu in one of my apps and MUST store files in the DB (for annoying reasons which are outside the scope of this convo).
The (one?) tricky thing dealing w/BLOB's I've found is that you need a separate code path to send the data to the user -- you can't simply in-line a path on the filesystem like you would if it was a plain-Jane file.
e.g. if you're storing avatar information, you can't simply do:
<%= image_tag #youruser.avatar.path %>
you have to write some wrapper logic and use send_data, e.g. (below is JUST an example w/attachment_fu, in practice you'd need to DRY this up)
send_data(#youruser.avatar.current_data, :type => #youruser.avatar.content_type, :filename => #youruser.avatar.filename, :disposition => 'inline' )
Unfortunately, as far as I know attachment_fu (I don't have the latest version) does not do clever wrapping for you -- you've gotta write it yourself.
P.S.
Seeing your question edit - Attachment_fu handles all that annoying stuff that you mention -- about needing to know file paths and all that crap -- EXCEPT the one little issue when storing in the DB. Give it a try; it's the standard for rails apps. IF you insist on re-inventing the wheel, the source code for attachment_fu should document most of the gotchas, too!
You can use the :binary type in your ActiveRecord migration and also constrain the maximum size:
class BlobTest < ActiveRecord::Migration
def self.up
create_table :files do |t|
t.column :file_data, :binary, :limit => 1.megabyte
end
end
end
ActiveRecord exposes the BLOB (or CLOB) contents as a Ruby String.
I think your best bet is the attachment_fu plug-in:
http://github.com/technoweenie/attachment_fu/tree/master
UPDATE: Found some more info here http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/a81beffb93708bb3
Look into the plugin, x_send_file too.
"The XSendFile plugin provides a simple interface for sending files via the X-Sendfile HTTP header. This enables your web server to serve the file directly from disk, instead of streaming it through your Rails process. This is faster and saves a lot of memory if you‘re using Mongrel. Not every web server supports this header. YMMV."
I'm not sure if it's usable with Blobs, it may just be for files on the file system. But you probably need something that doesn't tie up the web server streaming large chunks of data.

Resources