InvalidAuthenticityToken for in_place_editing?

There is a problem with InvalidAuthenticityToken errors that are raised in the methods for the in_place_editing plugin. This happens in Rails 2.0.2 (and possibly earlier versions). It's because there is no authenticity_token sent at all. You can apply this patch until there is a new version out.

If you have something like this:

<%= in_place_editor("title", {:url => url_for(:action => "update_title" …)}) %>

the update_title method will throw an error. Apply the patch to make it work.

SafeErb for Rails 2

Update: See this comment for how to fix problems with HelperMethods.

You might have noticed that the SafeErb plugin does not work in Rails 2 applications. That is because of old method signatures used in the plugin. The author has put up a blog post (in japanese) about a new version created by Aaron Bedra which points to this plugin installer (possibly replace http by svn):

./script/plugin install http://safe-erb.rubyforge.org/svn/plugins/safe_erb

The author has tested it with Rails 2.0.2 and it works fine. On my system however, it has problems with methods from the FormHelper (text_field and so on), most likely because of the output values in the value parameter. Does this happen on your system, as well? I hope to find a fix for that. Apart from that, the plugin works fine for Rails 2 applications.

Thanks to hurx for sending me his version. A happy new year to you all. 

Rails 1.2.6 security update

The rails core team has released ruby on rails 1.2.6 to address a bug in the fix for session fixation attacks (CVE-2007-5380). The CVE Identifier for this new issue is CVE-2007-6077. You should upgrade to this new release if you do not take specific session-fixation counter measures in your application.  

1.2.6 also fixes some regressions when working with has_many associations on unsaved ActiveRecord objects.

As with other 1.2.x releases, this is intended as a drop in upgrade for users of earlier versions in the 1.2 series.

 From the Rails log.

Rails 2.0 cookies (updated)

 

Rails 2.0 will include a new default session storage, the CookieStore (source source). What it does is store the clear text “marshalled” session object in a cookie which will be stored on the client side. Here is an example of a new cookie value:

BAh7BzoMdXNlcl9pZGkKIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVyOjpG%250
AbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA–be9c1e802c6cf126c722c680
02ccbd5684a96dd9

Well, it is actually not clear text, but Base64 encoding. So everything you store in the session object can be seen by the client. This is especially bad if you store secrets in the session, because you thought they will be safe there. But it should be safe for saving an id, for example. Update: You can implement an EncryptedCookieStore very easily, so no one can see the information. But again, the password for this encryption has to be strong.
When the cookie travels back from the client to the application, the session data will be “unmarshalled” and made available by means of the session method (e.g. session[:user_id]).

The second part of the cookie (after the –) is a hash (HMAC-SHA1 by default), calculated of the session object along with the secret server side password. On the server side the session value will be checked against this hash, so no one can tamper with it…unless the attacker knows the password (secret) which is in your environment.rb:

  config.action_controller.session = {
    :session_key => ‘_cookies2_session’,
    :secret      => ‘secret-secret’  }

You already know what may happen: the user is able to locally brute-force the secret as he has the clear text and the hash of it (Update: whether or not this will be successful depends on the strength of the secret. A sufficiently long secret is safe.). A weak secret is a short one, or one included in a word list (there are some with 40 million entries). You can try to brute-force your secret with John the Ripper which is very fast (I checked over 105 million passwords in less than half an hour). Update: This means the secret should be a strong password (over 40 characters, no words from dictionaries). The latest Rails release forces you to use a secret with at least 30 characters, and it generates a sufficiently long secret by default.

Corey Benninger presented my book and the most dangerous attack methods for Rails at the OWASP AppSec conference in San Jose. And he wrote a nice Ruby script to crack a Rails cookie (with a too short secret!) here and here.

Update: Replay attacks might be another security issue with client-side cookies. If you store state (is_admin, points, money, amount_to_pay, whatever) in a session, it may be replayed. That means an attacker can resend the state in the cookie and thus possibly reduce a price, get more points or become an admin (if the cookie is from someone else who is an admin). Solutions for this are welcome. In any case, it is not a good idea to save such information in sessions, no matter whether it’s a client- or server-side session store.

There are different cookie stores, depending on your needs. If you use CookieStore, please set a very long secret (over 40 characters, no words from dictionaries), preferably some sort of hash as the Rails generator for new project proposes. Also, keep in mind that an attacker might use the knowledge from the cookie somewhere else in your application.

restful_authentication login security

There is a serious security leak in the restful_authentication plugin regarding the activation of an account. You can use it to log in w/o user credentials or impersonate someone else.

The “activate” method of the controller accepts an empty activation code parameter like this (depending on your routes):
http://localhost:3006/user/activate or http://localhost:3006/activate/?activation_code=

Which will create this SQL:
SELECT * FROM users WHERE (users.`activation_code` IS NULL) LIMIT 1

An attacker will be able to log in w/o password and use the first account found with an empty activation_code (activated users)!

This works for everyone in and outside the app, because you’d normally have a skip_before_filter :login_required, :only => [:activate] in the controller. Even if you don’t (rarely), registered users can impersonate someone else!

The author has been informed, and thankfully reacted with a new version of the plugin, replace the first line of the method with this (depending on your model names):

self.current_user = params[:activation_code].blank? ? :false : User.find_by_activation_code(params[:activation_code]) 

HTTP Authentication and Feed Security

In the context of looking for a secure way to send out feeds (RSS, Atom, …), I found several options:

  • Use basic access authentication to prompt a user name and password before granting access. This is supported by quite a lot feed readers and browsers (where you have to enter your credentials).
    • Advantage: Easy to use, Rails 2.0 provides a method for it: authenticate_or_request_with_http_basic
    • Disadvantage: Very insecure as the user name and password is send in plain text over the net (encoding is not encryption), everyone could sniff the network traffic and read the login credentials. Also, it is vulnerable to phishing.
  • Digest access authentication works basically the same, but encrypts the user name/password and other values using MD5 before sending.
    • Advantage: No clear text passwords will be transmitted, much more secure than basic authentication, but it is not intended to replace strong authentication
    • Disadvantages: The clear text password or the HA1 hash (MD5(user:realm:password)) must be stored on the server. If someone gets access to it, he may use rainbow tables to compute the password. This takes very very long for such long strings, but fortunately the user name and realm is known and the password can be found relatively fast, especially if it is a weak password from a dictionary.
      Also it is vulnerable to Man in the middle attacks.
    • Do not prompt the user name/password that the user uses to login to the web application. Maybe you set up another special user name which is allowed to view the feed, only. Keep in mind to turn off sessions for feeds (session :off). If you don't, and someone gets hold of the special user's credentials, he will have a valid session not only for the feed but (possibly) for the application, too.
    • There is a plugin for Rails: htdigest :user=>"maiha", :pass=>"812b1d067e9ce1e44f09215339e3cd69", :type=>:crypted
      or in a table: htdigest :class=>"FeedUser", :user=>"login", :pass=>"ha1"
    • Digest authentication is far better, but has its weaknesses. If you use it to authenticate access to a feed, it will be alright. Consider using a different user model though.
  • Update: Create a long URL which grants access to the feed. However, the URL becomes the password and can be seen in plain text traveling through the net. It's good over SSL, though.
  • This is an interesting solution. The plugin Greasemonkey is evil though, well if you don't know the particular script.
  • Use basic access authentication over SSL. Probably the best solution, as it encrypts the feed and the user name/password in transit. The tradeoff, however, is the slower speed

Any comments?

Rails 1.2.5 security release

There is another security release which addresses once again the to_json vulnerability. It now has a CVE. If you used to_json in a page you generate:

<script type="text/javascript">
var customers = <%= @customers.to_json %>;
</script>
 
you should upgrade to 1.2.5. Besides it fixes some bugs from 1.2.4.

Rails 1.2.4 Maintenance release, security

The release of Ruby on Rails 1.2.4 addresses some potential security issues, all users of earlier versions are advised to upgrade to 1.2.4.

The following issues have been addressed:

  • URL-based sessions are no longer enabled by default, as it allowed users to provide their session_id in the URL as well as cookies.  The functionality could be exploited by a malicious user to obtain an authenticated session.
    Use config.action_controller.session_options[:cookie_session_id_only] = false to re-enable it
  • Changed the JSON encoding algorithms to avoid potential XSS issues when using ActiveRecord::Base#to_json
  • Potential Information Disclosure or DoS with Hash#from_xml: Maliciously crafted requests to a Rails application could cause the XML parser to read files from the server's disk or the network. 1.2.4 removes this functionality entirely.

ActionPack: Security

The Rails 2.0 Preview Release is available now, which is great news. The announcement includes a paragraph on security:

  • "we now ship we a built-in mechanism for dealing with CRSF attacks", yes it works fine
  • "The old TextHelper#sanitize method has gone from a black list (very hard to keep secure) approach to a white list approach." Very good news. This is in fact the white_list plugin which has been merged. A test with my private XSS list worked fine. It even has an easier way to allow tags directly in the method:
    sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
  • "Finally, we’ve added support for HTTP only cookies. They are not yet supported by all browsers, but you can use them where they are." Http only cookies can be used from IE v6.SP1 and recently Firefox v2.0.0.5. Http only cookies cannot be accessed by document.cookie anymore. However, you have to keep in mind that there are other ways to get the cookie. But still, it shuts down the most obvious way of getting at the cookies.

Plugins merged and Ruby’s Net::HTTPS

Good news: The csrf_killer plugin has been merged by Rick for Rails 2.0, so it is available in the current trunk. Go here for the changeset, and here for some documentation.

Furthermore, the insecure text helper methods strip_links, strip_tags and sanitize have been updated, mostly to strip nested tags. Still, I don't recommend using them, as new tickets (same applies for strip_tags) are coming in for this fresh change.

And for those of you using the Ruby Net::HTTP and Net::HTTPS libraries, here is a security vulnerability in it (it's for Ruby, not Rails):

  • A vulnerability results from the Net::HTTPS library failing to validate the name on the SSL certificate against the DNS name requested by the user. By not validating the name, the library allows an attacker to present a cryptographically valid certificate with an invalid CN.

Update: There's a post on the official Ruby site now.