<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>config/initializers/readonly_records.rb</filename>
    </added>
    <added>
      <filename>spec/models/readonly_spec.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -43,7 +43,10 @@ class AccountController &lt; ApplicationController
   def activate
     self.current_user = site.user_by_token(params[:id])
     if logged_in?
-      current_user.reset_token!
+      # TODO - See security comments on AuthenticatedSystem#login_from_cookie.
+      ActiveRecord::Base.with_writable_records do
+        current_user.reset_token!
+      end
     else
       flash[:error] = &quot;Invalid token.  Try resending your forgotten password request.&quot;
     end</diff>
      <filename>app/controllers/account_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,7 @@ class Admin::AssetsController &lt; Admin::BaseController
   skip_before_filter :login_required
   before_filter :find_asset, :except =&gt; [:index, :new, :create, :latest, :search, :upload, :clear_bucket]
   before_filter :login_required
+  before_filter :protect_action, :except =&gt; [:index, :new, :latest, :search]
 
   def index
     search_assets 24</diff>
      <filename>app/controllers/admin/assets_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,6 +3,7 @@ class ApplicationController &lt; ActionController::Base
 
   include Mephisto::CachingMethods
   before_filter  :set_cache_root
+  around_filter  :get_requests_are_readonly
   helper_method  :site
   attr_reader    :site
 
@@ -95,6 +96,21 @@ class ApplicationController &lt; ActionController::Base
       end
     end
 
+    # Much of a web browser's built-in protection against CSRF attacks
+    # assumes that all GET requests are &quot;safe&quot;.  Since we don't want to
+    # rely on getting this 100% right in every controller and plugin, let's
+    # just enforce this policy globally.  You can override this using
+    # &lt;code&gt;skip_filter :get_requests_are_readonly&lt;/code&gt;.
+    def get_requests_are_readonly
+      if request.method == :get
+        ActiveRecord::Base.with_readonly_records do
+          yield
+        end
+      else
+        yield
+      end
+    end
+
     def with_site_timezone
       old_tz = ENV['TZ']
       ENV['TZ'] = site.timezone.name</diff>
      <filename>app/controllers/application.rb</filename>
    </modified>
    <modified>
      <diff>@@ -74,7 +74,17 @@ module AuthenticatedSystem
     def login_from_cookie
       return unless cookies[:token] &amp;&amp; !logged_in?
       self.current_user = site.user_by_token(cookies[:token])
-      cookies[:token] = { :value =&gt; self.current_user.reset_token! , :expires =&gt; self.current_user.token_expires_at } if logged_in?
+      # TODO - We allow the token to be changed on GET requests and we log
+      # the user in.  I haven't fully analyzed the consequences of allowing
+      # session and token updates on hostile GET requests triggered by CSRF
+      # attacks.  If this helps out in some kind of attack, it would affect
+      # almost every single web application in existence.
+      ActiveRecord::Base.with_writable_records do
+        cookies[:token] = {
+          :value =&gt; self.current_user.reset_token!,
+          :expires =&gt; self.current_user.token_expires_at
+        } if logged_in?
+      end
       true
     end
 </diff>
      <filename>lib/authenticated_system.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ff0113a3c8ed63746af2a6c8398f29969330b439</id>
    </parent>
  </parents>
  <author>
    <name>Eric Kidd</name>
    <email>git@randomhacks.net</email>
  </author>
  <url>http://github.com/emk/mephisto/commit/c500bf8e05c250d02672c30d079a0bdeb66f0569</url>
  <id>c500bf8e05c250d02672c30d079a0bdeb66f0569</id>
  <committed-date>2008-12-20T06:10:37-08:00</committed-date>
  <authored-date>2008-12-20T06:10:37-08:00</authored-date>
  <message>Security: Force all GET requests to be read-only

The W3C makes a clear distinction between GET and POST requests.  GET
requests should only cause &quot;safe&quot; actions, and the user should never be
held accountable for making GET requests.  See the following for an
overview:

  http://www.w3.org/2001/tag/doc/whenToUseGet.html

The Rails 'protect_against_forgery' function (and possibly some web
browsers) rely on the distinction between GET and POST to provide
protection against CSRF attacks.  See:

  http://en.wikipedia.org/wiki/Cross-site_request_forgery
  http://guides.rubyonrails.org/security.html#_csrf_countermeasures

Unfortunately, enforcing these rules in rather difficult, especially in
a large application with lots of controllers and plugins.  So this patch
applies a rather heavy-handed fix: We globally block database writes
during GET requests, and specifically override that policy in one or two
places.

All of our current overrides invoke User#reset_token!.  I haven't
performed a full security analysis of allowing User#reset_token! (or
updates to session[:user] based on our &quot;remember me&quot; token) in a GET
request.  For now, I'm going to go ahead and allow this activity--if we
actually have some sort of vulnerability here, it affects a wide range
of web applications.

Note that this patch may break some part of the /admin interface.  I've
tried posting articles and other basic stuff, but I haven't used the
lesser-known corners of /admin since making these changes.  Please
report any problems.</message>
  <tree>58b206f97ec48143ffae2256ba548a18b1e21765</tree>
  <committer>
    <name>Eric Kidd</name>
    <email>git@randomhacks.net</email>
  </committer>
</commit>
