<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: Taking the unsafe GETs out of Rails</title>
    <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>Taking the unsafe GETs out of Rails</title>
      <description>&lt;div class="update"&gt;

	&lt;p&gt;&lt;em&gt;Update 2005-06-17:&lt;/em&gt; The &lt;em&gt;button_to&lt;/em&gt; helper, introduced below, has been incorporated into the Rails framework and will be a part of the Rails 1.0 release. See &lt;a href="http://community.moertel.com/ss/space/start/2005-06-16/1"&gt;Good news: The button_to helper is now part of Rails!&lt;/a&gt; for more.&lt;/p&gt;


	&lt;p&gt;&lt;em&gt;Update 2005-05-28:&lt;/em&gt; I now have &lt;a href="http://community.moertel.com/ss/space/start/2005-05-08/1/button_to.rb"&gt;a more-recent version of the &lt;em&gt;button_to&lt;/em&gt; code&lt;/a&gt;, which adds support for the disabled &lt;span class="caps"&gt;HTML&lt;/span&gt; attribute. Thanks to Sean T Allen for the great idea and initial implementation.&lt;/p&gt;


&lt;/div&gt;

	&lt;p&gt;As I &lt;a href="http://community.moertel.com/ss/space/start/2005-05-06/1#Google_Web_Accelerator_offers_web_developers_an_important_opportunity"&gt;wrote earlier&lt;/a&gt;, it&amp;#8217;s time for web developers to do away with the fundamentally broken practice of using hypertext links to trigger dangerous events such as deleting things. One of the first places we ought to clean house is in the burgeoning &lt;a href="http://www.rubyonrails.org/"&gt;Rails web-application framework&lt;/a&gt;, where this practice is pervasive.&lt;/p&gt;


	&lt;p&gt;The primary culprit in Rails is the all-too-easy &lt;em&gt;link_to&lt;/em&gt; method, which is (presently) the orthodox means of creating links to any action, even unsafe ones. For example:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;link_to "Destroy", :controller =&amp;gt; 'accounts',
        :action =&amp;gt; 'destroy', :id =&amp;gt; 6
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The above code generates the following &lt;span class="caps"&gt;HTML&lt;/span&gt; hypertext link, which when followed will merrily delete account number 6:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;lt;a href="/accounts/destroy/6"&amp;gt;Destroy&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Because this practice is dangerous and contrary to the decade-old convention that links be safe, the &lt;em&gt;link_to&lt;/em&gt; method thoughtfully lets us request that a Javascript confirmation dialog be tacked onto the link for added protection:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;link_to "Destroy", ...,  :confirm =&amp;gt; "Are you sure?" 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The resulting &amp;#8220;safe&amp;#8221; &lt;span class="caps"&gt;HTML&lt;/span&gt;:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;lt;a href="/accounts/destroy/6" 
   onclick="return confirm('Are you sure?');"&amp;gt;Destroy&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Unfortunately, the Javascript protection doesn&amp;#8217;t work. First, not all web browsers care about it. Lots of people surf with Javascript turned off. Second, a whole slew of things besides web browsers live on the Internet, and almost all of them are oblivious to Javascript. Web crawlers fall into this category. They will be more than happy to follow any link you feed to them. &amp;#8220;Hey, Googlebot just deleted every account in our database!&amp;#8221; Oops.&lt;/p&gt;


	&lt;p&gt;Thus another layer of protection is commonly used: authorization. The theory is that dangerous links can be safely corralled in the private parts of a web application, where the public and web crawlers cannot go. Only authorized users can get into those parts, and those users will be smart enough not to click on the truly dangerous links unless they really mean it.&lt;/p&gt;


	&lt;p&gt;The problem is, any number of intermediary agents can be operating &lt;em&gt;on behalf of an authorized user&lt;/em&gt;, and these agents are free to do anything the user is allowed to do, such as follow dangerous links. Google&amp;#8217;s Web Accelerator is one such agent. It tries to make your surfing faster by (among other things) pre-fetching the resources that are linked to on the pages you visit. And what happens if you, an authorized user, visit a page containing dangerous links? That&amp;#8217;s right, Web Accelerator will fetch the &amp;#8220;resources&amp;#8221; those links point to &amp;#8211; and delete a bunch of your stuff.&lt;/p&gt;


	&lt;p&gt;I hope by this point that I have argued convincingly that using links for unsafe actions is a bad idea. Even if you feel justified in ignoring the applicable parts of the &lt;span class="caps"&gt;HTTP&lt;/span&gt; RFCs, it&amp;#8217;s a bad idea. Even if you tack on Javascript confirmations and hide your links in authorization-protected zones of your site, it&amp;#8217;s a bad idea. It is, all around, a bad idea. Don&amp;#8217;t do it.&lt;/p&gt;


	&lt;p&gt;So what alternatives are there?  Read on for one possibility, &lt;em&gt;button_to&lt;/em&gt;.&lt;/p&gt;&lt;h3&gt;A &lt;em&gt;link_to&lt;/em&gt; alternative: &lt;em&gt;button_to&lt;/em&gt;&lt;/h3&gt;


	&lt;p&gt;If you shouldn&amp;#8217;t use links for unsafe actions, what should you use instead? Form buttons. Forms can be submitted via &lt;span class="caps"&gt;HTTP POST&lt;/span&gt; requests, and &lt;span class="caps"&gt;POST&lt;/span&gt; requests are understood to do potentially unsafe things. Web crawlers will not try to click your buttons. Intermediary user agents will not try to pre-submit your forms.&lt;/p&gt;


	&lt;p&gt;So, how do we make doing the right thing as easy as creating a link? My answer is 
&lt;em&gt;button_to&lt;/em&gt;, a method that takes the same parameters as the ever-popular &lt;em&gt;link_to&lt;/em&gt; but creates a tiny form that contains a single button instead of a link:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;button_to "Destroy", { :action =&amp;gt; 'destroy', :id =&amp;gt; 6 },
          :confirm =&amp;gt; "Are you sure?" 
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The resulting &lt;span class="caps"&gt;HTML &lt;/span&gt;(reformatted for your viewing pleasure):&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;lt;form method="post" action="/accounts/destroy/6" class="button-to"&amp;gt;
  &amp;lt;div&amp;gt;&amp;lt;input onclick="return confirm('Are you sure?');" 
              value="Destroy" type="submit"&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;The forms I create are given the class &lt;em&gt;button-to&lt;/em&gt;, which makes it easy to apply styles to them. With a little work, the buttons can look pretty darn good:&lt;/p&gt;


	&lt;p&gt;&lt;img src="http://community.moertel.com/ss/space/start/2005-05-08/1/button_to.png" title="screen capture showing Edit and Destroy buttons created with the button_to method" alt="screen capture showing Edit and Destroy buttons created with the button_to method" /&gt;&lt;/p&gt;


	&lt;p&gt;So that&amp;#8217;s my plea: Use a button. It&amp;#8217;s a simple solution to a potentially ugly problem. There&amp;#8217;s no need for Ajax or other non-portable Javascript trickery. Just use a button.&lt;/p&gt;


	&lt;p&gt;And it&amp;#8217;s easy, too. In a few minutes, I was able to &amp;#8220;clean house&amp;#8221; on the Rails application I&amp;#8217;m developing.&lt;/p&gt;


	&lt;h3&gt;The code&lt;/h3&gt;


	&lt;p&gt;If you&amp;#8217;re interested, here&amp;#8217;s the code for &lt;em&gt;button_to&lt;/em&gt;. It&amp;#8217;s only ten lines, but the docs make it look much longer.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;# Generates a form containing a sole button that submits to the URL
# given by _options_.  Use this method instead of +link_to+ for
# dangerous actions that do not have the safe HTTP GET semantics
# implied by using a hypertext link.
#
# The parameters are the same as for +url_to+.  Any _html_options_
# that you pass will be applied to the inner +input+ element.  The
# generated form element is given the class 'button-to', to which
# you can attach CSS styles for display purposes.
#
# Example 1:
#
#   # inside of controller 'feeds'
#   button_to "Edit", :action =&amp;gt; 'edit', :id =&amp;gt; 3
#
# Generates the following HTML (sans formatting):
#
#   &amp;lt;form method="post" action="/feeds/edit/3" class="button-to"&amp;gt;
#     &amp;lt;div&amp;gt;&amp;lt;input value="Edit" type="submit"&amp;gt;&amp;lt;/div&amp;gt;
#   &amp;lt;/form&amp;gt;
#
# Example 2:
#
#   button_to "Destroy", { :action =&amp;gt; 'destroy', :id =&amp;gt; 3 },
#             :confirm =&amp;gt; "Are you sure?" 
#
# Generates the following HTML (sans formatting):
#
#   &amp;lt;form method="post" action="/feeds/destroy/3" class="button-to"&amp;gt;
#     &amp;lt;div&amp;gt;&amp;lt;input onclick="return confirm('Are you sure?');" 
#                 value="Destroy" type="submit"&amp;gt;
#     &amp;lt;/div&amp;gt;
#   &amp;lt;/form&amp;gt;
# 
# *NOTE*: This method generates HTML code that represents a form.
# Forms are "block" content, which means that you should not try to
# insert them into your HTML where only inline content is expected.
# For example, you can legally insert a form inside of a +div+ or +td+
# element or in between +p+ elements, but not in the middle of a run
# of text.  (Bottom line:  Always validate your HTML before going
# public, especially if this paragraph seems confusing.)

def button_to(name, options = {}, html_options = nil)
  html_options = (html_options || {}).stringify_keys
  convert_confirm_option_to_javascript!(html_options)
  url, name = options.is_a?(String) ? 
    [ options,  name || options ] :
    [ url_for(options), name || url_for(options) ]
  html_options.merge!("type" =&amp;gt; "submit", "value" =&amp;gt; name)
  "&amp;lt;form method='post' action='#{h url}' class='button-to'&amp;gt;&amp;lt;div&amp;gt;" +
    tag("input", html_options) + "&amp;lt;/div&amp;gt;&amp;lt;/form&amp;gt;" 
end
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Thanks for reading and happy unsafe-link hunting!&lt;/p&gt;</description>
      <pubDate>Sun, 08 May 2005 12:00:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:2e55c892774276d8ec6a19d9921e39d7</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails</link>
      <category>ruby</category>
      <category>web development</category>
      <category>rails</category>
      <category>rails</category>
      <category>get</category>
      <category>gwa</category>
      <category>safe</category>
      <category>unsafe</category>
      <category>post</category>
      <category>link_to</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/41</trackback:ping>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Joyous</title>
      <description>&lt;p&gt;This is a great article.&lt;/p&gt;


	&lt;p&gt;Hey what would be the equivalent of a link_to_remote ?&lt;/p&gt;</description>
      <pubDate>Thu, 06 Mar 2008 05:52:35 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:f41d1633-d56a-4ea3-a291-406b50c40090</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-704</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Tom Moertel</title>
      <description>&lt;p&gt;pointer&amp;#8212;If you want to personalize the look and feel of the buttons, use &lt;a href="http://www.w3.org/Style/CSS/"&gt;Cascading Style Sheets&lt;/a&gt;. The outer &lt;code&gt;form&lt;/code&gt; element of &lt;code&gt;button_to&lt;/code&gt;-created buttons has the class &amp;#8220;button-to&amp;#8221;, which makes a convenient hook for attaching styles.&lt;/p&gt;


	&lt;p&gt;&amp;#8212;Tom&lt;/p&gt;</description>
      <pubDate>Tue, 12 Jun 2007 11:51:56 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:e4215a90-6c4a-4125-b316-eda6656bc057</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-476</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by pointer</title>
      <description>&lt;p&gt;great, could i ask that if i want to personalise my look and feel style of button, am i supposed to overload this method directly in my application helper or corresponding controller helper?&lt;/p&gt;</description>
      <pubDate>Mon, 11 Jun 2007 21:51:16 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:19a4d135-cffc-4171-87f7-11354799e91c</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-475</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Kjell</title>
      <description>&lt;p&gt;(Saw this on reddit, just wanted people to know it&amp;#8217;s been a non issue for quite awhile&amp;#8230;)&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://www.loudthinking.com/arc/000529.html"&gt;How Rails is prepared for GWA II: Vengeance&lt;/a&gt;. At a controller level you can use &lt;code&gt;verify :method =&amp;gt; :post, :only =&amp;gt; %w( delete update )&lt;/code&gt; which ensures that you&amp;#8217;re posting to your destructive actions, and at a view level &lt;code&gt;link_to('post me', {url_options}, :method =&amp;gt; :post)&lt;/code&gt; (which lets you post to destructive actions).&lt;/p&gt;</description>
      <pubDate>Thu, 20 Jul 2006 21:34:46 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-142</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Kai MacTane</title>
      <description>&lt;p&gt;Whoops, just noticed the date on your blog post (having just come here from reddit.com). Kudos to you for spotting the GET problem way before others.&lt;/p&gt;


	&lt;p&gt;Regardless of when you said it, I still agree with your point. :)&lt;/p&gt;</description>
      <pubDate>Thu, 20 Jul 2006 13:23:33 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-141</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Kai MacTane</title>
      <description>&lt;p&gt;You&amp;#8217;re preaching to the choir about GET-based deletions being a bad idea, and the idea of &amp;#8220;safetying&amp;#8221; them with JavaScript is laughable. In fact, this exact sort of problem was featured on TheDailyWTF.com many months ago:&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://thedailywtf.com/forums/65974/ShowPost.aspx"&gt;http://thedailywtf.com/forums/65974/ShowPost.aspx&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Has anyone told DHH about the Daily WTF?&lt;/p&gt;</description>
      <pubDate>Thu, 20 Jul 2006 13:22:06 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-140</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Tom Moertel</title>
      <description>&lt;p&gt;Simon, thanks for your comment. I had not considered the
form-in-a-form case. But, now that I think about it, I don&amp;#8217;t think
it&amp;#8217;s as limiting as it might seem. Let me explain.&lt;/p&gt;


	&lt;p&gt;When we want to trigger an action and we&amp;#8217;re already inside of a form,
there are really two cases to consider. The first case is when the
action depends on the state of the entire form. This case is actually
the common case for plain old forms: Click a submission button and the
entire form is processed. For this case, just create a submission
button with the usual X/HTML markup:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&amp;lt;input type="submit" value="Process your Order" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;No need to use &lt;em&gt;button_to&lt;/em&gt; or anything like it. Normal markup works
just fine.&lt;/p&gt;


	&lt;p&gt;The second case is the interesting one. It&amp;#8217;s when we want the action
to operate only upon local state, acting in effect as an escape hatch
from the form. (E.g., consider the Add To Cart buttons in Amazon Wish
Lists.) That is, we do not care about the state of the entire form,
just the local context of the action (e.g., a particular item in the
wish list to add to the cart). This is where many people turn to
hypertext links, encoding the local state into the URL. But there are
other options.&lt;/p&gt;


	&lt;p&gt;One option is to use a normal form-submission button, just like in the
common case, but with a twist: We additionally encode the local state
into the button (e.g., via its name attribute). When the server
processes the request, it simply ignores the form state and processes
the encoded local state. The downside is that if the form state is
large, the POST requests will be correspondingly
wasteful. Nevertheless, in many cases the waste may be within
acceptable limits.&lt;/p&gt;


	&lt;p&gt;Another option is to use a link inside of the form to confirm &amp;#8211; not to
trigger &amp;#8211; the local action. The link points to a confirmation page
that summarizes the action to be performed in a form. If the user
confirms the action, the form is posted, triggering the action. The
upside is that if the action is potentially unsafe, users will have a
real opportunity to confirm it (which Javascript-based approaches do
not guarantee). The downside is that if the typical usage scenario
involves a user having to perform the action frequently, the
confirmation round trips may become burdensome. (On the other hand, if
that&amp;#8217;s the typical scenario, we might be better off redesigning the
interface to allow for batch operations.)&lt;/p&gt;


	&lt;p&gt;Yes, the corner case does exist. But it&amp;#8217;s not as big as it initially
seems.&lt;/p&gt;


	&lt;p&gt;Cheers,&lt;br /&gt;
Tom&lt;/p&gt;</description>
      <pubDate>Mon, 09 May 2005 01:28:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-110</link>
    </item>
    <item>
      <title>"Taking the unsafe GETs out of Rails" by Simon Willison</title>
      <description>&lt;p&gt;This is great, great, great &amp;#8211; except for one thing: you can&amp;#8217;t nest forms inside other forms. While this will work fine for many cases, any time you want a &lt;em&gt;button_to&lt;/em&gt; button inside an already existing form things will break in unfortunate ways.&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s still worth having of course, but the rather nasty edge case needs to be documented.&lt;/p&gt;</description>
      <pubDate>Sun, 08 May 2005 19:26:17 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:</guid>
      <link>http://blog.moertel.com/articles/2005/05/08/taking-the-unsafe-gets-out-of-rails#comment-109</link>
    </item>
  </channel>
</rss>
