Rails' Insecure Defaults - Code Climate Blog
Rails’ reputation as a relatively secure Web framework is well deserved. Out-of-the-box, there is protection against many common attacks: cross site scripting (XSS), cross site request forgery (CSRF) and SQL injection. Core members are knowledgeable and genuinely concerned with security.
However, there are places where the default behavior could be more secure. This post explores potential security issues in Rails 3 that are fixed in Rails 4, as well as some that are still risky. I hope this post will help you secure your own apps, as w...
How to set the user agent in tests
The User-Agent
HTTP header identifies the client and is sent by "regular" browsers, search engine crawlers, or other web client software.
Cucumber
In Rack::Test
, you can set your user agent like this on Capybara:
Given /^my user agent is "(.+)"$/ do |agent|
page.driver.browser.header('User-Agent', agent)
# Or, for older Capybaras:
# page.driver.header('User-Agent', agent)
end
For Selenium tests with Firefox, it seems you can set the general.useragent.override
profile setting to your preferred value. [See StackOver...
Test redirects to an external URL with Cucumber/Capybara
When a controller action redirects to an external URL (like http://somehost.com/some/path
) you will find that this is hard to test with Cucumber and Capybara:
- A non-Javascript Rack::Test scenario will just ignore the host and try to open
/some/path
in your local application - A Selenium test will actually follow the redirect, which you probably don't want either
There are two workarounds for this. You can use either, or a combination of both.
- Write a controller spec
Controller specs can test if a resp...
How to disable cookies in cucumber tests
Unfortunately, Capybara does not offer a switch to disable cookies in your test browser. However, you can work around that by using a tiny Rack middleware -- it works for both Selenium and non-Selenium tests.
Wouldn't it be nice to say something like this?
Given cookies are disabled
When I try to sign in
Then I should see "Can't sign you in. Please enable cookies."
You can! Put the code below into some place like lib/rack/cookie_stripper.rb
.
module Rack
class CookieStripper
ENABLED = false
...
Cucumber step to set cookies in your Capybara session
To set a cookie in your test browser for cucumber tests, you need to know which driver you are using. Use the step below according to your driver.
Rack::Test
Given /^I have a "([^\"]+)" cookie set to "([^\"]+)"$/ do |key, value|
headers = {}
Rack::Utils.set_cookie_header!(headers, key, value)
cookie_string = headers['Set-Cookie']
Capybara.current_session.driver.browser.set_cookie(cookie_string)
end
Note that Rack::Utils
is only used to find out the correct cookie header string (you don't want to generate it yours...
Comparing Rails' flash hashes will not respect their internal lists of used entries
Rails flashes (FlashHash
) track a list of used keys, which is not respected when comparing flash hashes.
This does not concern you under most circumstances.
Basics
When ActionController
picks up a flash object, it will call the #sweep
method once; that method checks the list of used flash entries and deletes those. All other entries are flagged as used. This means they will be deleted on the next request, but are still be available for rendering during the current request.
Fun facts: When redirecting, this does not happen. Also,...
Tips And Tricks For Print Style Sheets
Smashing Magazine lists some handy tricks for print style sheets, all with CSS:
- Expand External Links For Print
- Print QR Codes For Easy URL References
- Use CSS filters to improve the result of printed graphics (i.e. images with transparent parts)
- Use your DevTools to preview the print styles in your browser
Capistrano: Bundler stalls and asks for "Username"
Given you use Capistrano together with bundler to automatically install your gems when deploying.
I recently had the problem that Capistrano stalled like this:
[err :: host.name.tld] Username:
It turned out that I this originated from GitHub. We had a gem in our Gemfile that explicitly pointed to a GitHub URL like that:
gem 'foogem', :git => 'https://github.com/blubb/foogem.git'
The URL was returning a 404 which caused the problems. You have to get another gem or point to a fork on GitHub.
How to fix: Session hash does not get updated when using "merge!"
tl;dr: Do not use merge!
for session hashes. Use update
instead.
Outline
Let's assume you're modifying the Rails session. For simplicity, let's also assume your session is empty when you start (same effect when there is data):
# In our example, we're in a Rack middleware
request = Rack::Request.new(env)
request.session.merge! :hello => 'Universe'
request.session
=> {}
Even worse: When you inspect your request.session
like above (e.g. in a debugger shell, o...
How to fix: "Error Bundler::HTTPError during request to dependency API"
If bundle install
shows the following message for you ...
Error Bundler::HTTPError during request to dependency API
... upgrade to Bundler ≥ 1.2.4:
gem install bundler
Apparently, it just hides the message.
Prevent double clicks on link_to_remote (simple case)
This works well in the simplified case, when your link disappears after it was clicked.
Let link_to_remote behave as „disabled“ after the first click. Use the :before
hook to replace the orignal link with a link that does nothing but looks like the original link:
:ruby
label = "do_something"
dummy_link = link_to(label)
other_attributes_hash = { :url => ..., :method => ..., ... }
disable_link_option = { :before => "$('your_link_selector').html('#{escape_javascript(dummy_link)}'" } # jquery
= link_to_remote(label, other_att...
CSS: Matching against attributes and their values (or parts of them)
You probably know that you can use CSS selectors to match against elements and their attributes, such as:
a[title] { /* any <a> that has a "title" */ }
a[data-fancy="true"] { /* any <a> that has their "data-fancy" attribute set to "true" */ }
But there is more: You do not need to match against "full" attribute values but can match against parts of them.
They work in all somewhat modern browsers, and IE9 or later.
Match variants
Exact match (CSS2)
[foo="bar"]
(matches `<div foo="b...
CSS: Set content from other attributes
You can use the content
CSS attribute to set an element's content -- which is especially useful for the :before
and :after
pseudo elements:
a:before {
content: 'Click me: ';
}
The above example would prepend "Click me:" to any link on the page.
Note that you can also refer the contents of other attributes of the element. So, if your links have a helpful title
set, you could do this:
a:before {
content: attr(title) ": ";
}
There also is a jsFiddle for the examp...
What The Rails Security Issue Means For Your Startup
January has been a very bad month for Ruby on Rails developers, with two high-severity security bugs permitting remote code execution found in the framework and a separate-but-related compromise on rubygems.org, a community resource which virtually all Ruby on Rails developers sit downstream of. Many startups use Ruby on Rails. Other startups don’t but, like the Rails community, may one day find themselves asking What Do We Do When Apocalyptically Bad Things Happen On Our Framework of Choice? I thought I’d explain that for the general c...
Amazon Elastic Transcoder
Amazon Elastic Transcoder is video transcoding in the cloud. It is designed to be a highly scalable, easy to use and a cost effective way for developers and businesses to convert (or “transcode”) video files from their source format into versions that will playback on devices like smartphones, tablets and PCs.
This might be a good alternative for services like Panda which charge a large monthly fee just to be available for your encoding requests. Amazon's service bills by usage instead:
A 10 minute sourc...
Consul: Querying a power that might be nil
Consul 0.6.1+ gives your Power
class a number of static methods that behave neutrally in case Power.current
is nil
. This allows you to create authorization-aware models that still work when there is no user at the end of a web browser, e.g. on the console, during tests or during batch processes.
You will often want to access Power.current
from another model, to e.g. iterate through the list of accessible users:
class UserReport
def data
Power.current.users.c...
Understanding race conditions with duplicate unique keys in Rails
validates_uniqueness_of
is not sufficient to ensure the uniqueness of a value. The reason for this is that in production, multiple worker processes can cause race conditions:
- Two concurrent requests try to create a user with the same name (and we want user names to be unique)
- The requests are accepted on the server by two worker processes who will now process them in parallel
- Both requests scan the
users
table and see that the name is available - Both requests pass validation and create a user with the seemingly available name...
Geocoding Strategies - Google Maps API
The attached article outlines considerations when choosing client-side vs. server-side implementations of the Google Geocoding APIs (geocoder, directions, not maps drawing). The main points are:
- On the server side you only get a fixed daily request quota
- On the client side the quota is per-client, so basically unlimited
- When implementing APIs on the server-side, be aware that quota is measured by IP. When hosting in the cloud **you don't always know which other services might...
Phusion Passenger 4 Technology Preview: Out-Of-Band Work – Phusion Corporate BlogPhusion Corporate Blog
The Out-of-Band Work feature allows one to perform arbitrary long-running work outside the request/response cycle without blocking HTTP clients. The primary use case is to run the garbage collector in between cycles so that your requests will finish faster because they will not be interrupted by the garbage collector.
Using Apache Benchmark (ab) on sites with authentication
Apache HTTP server benchmarking tool (ab
) is a nice tool to test performance on sites delivered by HTTP. If the site you're about to test is placed behind a login, follow these steps to successfully use ab
on it.
- Open the site to test in the browser of your choice. Do not login yet.
- Use developer tools to show all cookies used by the site. (Chrome: Ctrl+Shift+i, open the 'Resources' tab and click on the site below 'Cookies' on the left. Firefox: Right-click on the site, open 'We...
Ruby: How to ensure a Tempfile's extension
If you use Tempfile
and pass your own filename containing an extension, it will just be consumed by the Tempfile's filename:
>> Tempfile.new('foobar.xlsx').path
=> "/tmp/foobar.xlsx20130115-19153-4ykpwm-0"
If you want to keep the file extension, pass filename and extension as an array:
>> Tempfile.new([ 'foobar', '.xlsx' ]).path
=> "/tmp/foobar20130115-19153-1xhbncb-0.xlsx"
How to get the hostname of the current machine in Rails or a Ruby script
Use Socket.gethostname
. So for a machine whose hostname is "happycat", it will look like this:
>> Socket.gethostname
=> "happycat"
That should work right away for your Rails application. For plain Ruby, you first need to do:
require 'socket'
If you don't want to use Socket
for some reason, you can still just use the hostname
command, at least on non-Windows machines. Keep in mind that you need to remove trailing white space from the result of the system call.
>> `hostname`
=> "happycat\n"
>> `hostname`.stri...
Why your browser loses cookies when following hyperlinks from an Excel spreadsheet or Word document
Microsoft Office pre-fetches hyperlinks using an internal DLL (which doesn't know about your cookies), follows all redirects and opens your browser with the result. This is because of stupidity.
The "fix" is to not redirect but just render a text like "access denied" with 200 OK when you see that request.env['HTTP_USER_AGENT'].include?('ms-office')
.
Force GitHub Pull Requests to update the diff against its target branch
When you have a Pull Request on GitHub that includes commits from another Pull Request, you will still see them after the "child" PR has been merged. Unfortunately, GitHub won't automatically update the diff (or commit list).
Here is what worked for me:
- Check out the target branch
git checkout my-target-branch
- Make sure you are up to date against origin (e.g.
git fetch
andgit status
). You should be 0 commits ahead or behind.
- Add and commit a file
touch .please-update
git add .please-update
- `gi...