proper way to submit a form via https?

Hi, I am trying to find the 'recommended' way of doing this and have
had little success so far.
I have a login form. I want this to submit via https. What I have
found I had to do was this:

<% form_for :user, :url =>{ :controller => :account, :action
=> :login, :only_path => false, :protocol => "https://" } do |form| %>
(get login and password)
<%end%>

This looks kind of crap and I am thinking there must be a nicer way! I
am using the SSLRequirement plugin but that really doesn't work for
forms as all you can do is a redirect, by which time it is too late.

Also, it would be nice if there was a way to only include the protocol
attribute when I was in production mode as it is a hassle under dev!

Help!

Thanks.

Hi, I am trying to find the 'recommended' way of doing this and have
had little success so far.
I have a login form. I want this to submit via https. What I have
found I had to do was this:

<% form_for :user, :url =>{ :controller => :account, :action
=> :login, :only_path => false, :protocol => "https://" } do |form| %>
(get login and password)
<%end%>

This looks kind of crap and I am thinking there must be a nicer way! I
am using the SSLRequirement plugin but that really doesn't work for
forms as all you can do is a redirect, by which time it is too late.

I'm not terribly experienced in this matter, but I believe that is the correct way.

Also, it would be nice if there was a way to only include the protocol
attribute when I was in production mode as it is a hassle under dev!

You can try something like this in application.rb

  def require_ssl
    if request.ssl? || local_request?
      return true
    else
      redirect_to :protocol => "https://" and return false
    end
  end

which says if the request is already ssl, or it's local, return true. Then in your controllers,

before_filter :require_ssl

it works for me.

Peace,
Phillip

How about setting :only_path => true ?
That way, the forms action attribute will contain only the relative
url to the target controller which implies https given that the
display of the form happened through https.

You can try something like this in application.rb

        def require_ssl
                if request.ssl? || local_request?
                        return true
                else
                        redirect_to :protocol => "https://" and return false
                end
        end

which says if the request is already ssl, or it's local, return
true. Then in your controllers,

before_filter :require_ssl

it works for me.

Peace,
Phillip

But won't the end result of this be that the form will be submitted
via http (ergo having everything transmitted in clear text) and then
have it go 'whoops, this should be https' and redirect to https? This
method is what ssl_requirement does, which is good for secure pages,
just not secure form submissions.

Maybe I am doing something wrong, but, with :only_path => false I get:
<form action="https://localhost:3002/es/account/signup"
method="post">

but with it true I get:
<form action="/es/account/signup" method="post">

Which looks like it is ignoring the protocol completely!
(I am running under rails 1.2.5 btw)

I am not displaying the form via https, as really there is no need.
The information on login (and registration) is not sensitive - just
the stuff the user fills in and submits.

If your initial submission is to https, then you shouldn't have a problem. So in your view, if you have as the action of the form

action="https://some_url"

then it should be encrypted on the way. I actually wrestled with this some and decided to go the way of PayPal. If you go to http://www.paypal.com, you are immediately redirected to https://www.paypal.com. I found that it's easier to just run the whole site in https than switch back and forth. And with my site being more an online application than a "web site", it makes more sense.

Peace,
Phillip

Maybe I am doing something wrong, but, with :only_path => false I get:
<form action="https://localhost:3002/es/account/signup"
method="post">

but with it true I get:
<form action="/es/account/signup" method="post">

This is correct. A relative path infers the missing pieces from the
current documents url.
Thus it's not "ignoring" the protocol, but using the very same which
you used to display your form.

I am not displaying the form via https, as really there is no need.
The information on login (and registration) is not sensitive - just
the stuff the user fills in and submits.

Yes but if you display the form via https (maybe even enforcing that
with ssl_requirement), the form can infer the protocol. Try installing
the Firebug and TamperData extensions for Firefox to play around with
this stuff and see how the forms action is derived from the action
attribute and the documents url.

SSL Requirement if fine when the container page is already secure.

I normally use Secure Actions:

    http://agilewebdevelopment.com/plugins/secure_actions

which allows declaration of secure actions as well and hooks into URL generation to get the right protocol. I blogged some details about the plugin here:

     http://www.advogato.org/person/fxn/diary/470.html

-- fxn

Along the same lines of this thread, I have a secure online form that
I am using to create and save accounts. I'm using SSL to process the
new accounts (ultimately we'll be accepting credit cards). My
experience with using SSL is practically nil (at least in Rails). So
the account creation works locally AND in production when I refrain
from using SSL. However when I go through the https:// URL, the form
does not process, doesn't provide any error messages, and simply
displays the "new" action again as if the data errorred out. I am
using the ssl_required plugin, too. All to no avail.

I have a temporary Comodo certified certificate and I do not get the
invalid certificate prompt at our production URL. It feels like I
might just be missing a detail pertaining to the SSL certificate or
whatnot that is a quick fix. At least I hope that's the problem. If
you have any thoughts, feel free to share!

Thanks,

-e

But if I have/want a login widget at the top of every page the only
nice way to get that working with this method is to have every page
ssl'd. Which is ridiculous.

What I am doing now is this:
  <% unless RAILS_ENV == 'production'
      url_options = { :controller => :account, :action => :do_login }
    else
      url_options = { :controller => :account, :action
=> :do_login, :protocol => 'https://', :only_path => false }
    end %>

It's a hack, but it works.

I decided to write this:
  def secure_form_for(record_or_name_or_array, *args, &proc)
    unless RAILS_ENV == 'production'
      url_options = {}
    else
      url_options = {:protocol => 'https://', :only_path => false }
    end

    options = args.last.is_a?(Hash) ? args.pop : {}
    options = url_options.merge options

    return form_for(record_or_name_or_array, options, &proc)
  end

Anyone think this is crazy? Seems to work so far.

Actually, that didn't quite work. This does:
  def secure_form_for(record_or_name_or_array, *args, &proc)
    unless RAILS_ENV == 'production'
      url_options = {}
    else
      url_options = {:protocol => 'https://', :only_path => false }
    end

    options = args.last.is_a?(Hash) ? args.pop : {}
    if !options[:url].nil?
      options[:url] = url_options.merge options[:url]
    else
      options[:url] = url_options
    end

    return form_for(record_or_name_or_array, options, &proc)
  end