From d02033db16d47c6237dbf1039b9b33654ad63063 Mon Sep 17 00:00:00 2001 From: Michael Trim Date: Tue, 3 Jun 2008 18:59:54 +0100 Subject: [PATCH] Add :protect_against_forgery option to form_tag This option prevents the CSRF (Cross-Site Request Forgery) protection token being included for an individual form, whilst still being enabled. This is intented only for situations where the form is being submitted to a third-party (e.g. an external search). In such cases, CSRF protection is not needed and revealing the token to the third party is a security risk as they could then submit requests as the user. Signed-off-by: Michael Trim --- actionpack/CHANGELOG | 2 ++ .../lib/action_view/helpers/form_tag_helper.rb | 16 +++++++++------- .../controller/request_forgery_protection_test.rb | 9 +++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 66af5fd..3be1804 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Added :protect_against_forgery option to form_tag [Michael Trim] + * Added page.reload functionality. Resolves #277. [Sean Huber] * Fixed Request#remote_ip to only raise hell if the HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR doesn't match (not just if they're both present) [Mark Imbriaco, Bradford Folkens] diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index ca58f4b..ac46ecc 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -17,6 +17,11 @@ module ActionView # * :method - The method to use when submitting the form, usually either "get" or "post". # If "put", "delete", or another verb is used, a hidden input with name _method # is added to simulate the verb over post. + # * :protect_against_forgery - If set to false, no hidden input is included + # for cross-site request forgery prevention. This is intended only for use + # when the form is being submitted to a different site (e.g. for an + # external search facility) to avoid revealing the token to third parties. + # If set to true or unspecified, the default setting for the controller is used. # * A list of parameters to feed to the URL the form will be posted to. # # ==== Examples @@ -422,16 +427,17 @@ module ActionView end def extra_tags_for_form(html_options) + include_csrf_token = (html_options.delete("protect_against_forgery") != false) && protect_against_forgery? case method = html_options.delete("method").to_s when /^get$/i # must be case-insentive, but can't use downcase as might be nil html_options["method"] = "get" '' when /^post$/i, "", nil html_options["method"] = "post" - protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : '' + include_csrf_token ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : '' else html_options["method"] = "post" - content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0') + content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + (include_csrf_token ? token_tag : ""), :style => 'margin:0;padding:0') end end @@ -448,11 +454,7 @@ module ActionView end def token_tag - unless protect_against_forgery? - '' - else - tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) - end + tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end end end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index f7adaa7..cc55fd3 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -18,6 +18,10 @@ module RequestForgeryProtectionActions render :inline => "<%= form_tag('/') {} %>" end + def index_without_token + render :inline => "<%= form_tag('/', :protect_against_forgery => false) {} %>" + end + def show_button render :inline => "<%= button_to('New', '/') {} %>" end @@ -82,6 +86,11 @@ module RequestForgeryProtectionTests assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token end + def test_should_render_form_without_token_tag + get :index_without_token + assert_select 'form>div>input[name=?]', 'authenticity_token', false + end + def test_should_render_button_to_with_token_tag get :show_button assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token -- 1.5.4.3