Secure session cookies for rails application - ruby-on-rails-3

I have the following configuration in my session_store.rb
Fuel::Application.config.session_store :cookie_store,
:key => "_secure_session",
:secure => !(Rails.env.development? || Rails.env.test?),
:domain => :all
In application_controller.rb
def default_url_options
return { :only_path => false, :port => 443, :protocol => 'https' }
end
I am using devise and my rails3 server is running behind HAProxy. HAProxy terminates the HTTPS traffic and passes HTTP requests to Rails. My problem is when i turn on :secure => true in session_store.rb, the user is redirected back to the sign in page with the message "Unauthorized". I have tried debugging it a lot, not sure how to get it working.
Its a situation where HAProxy is the reverse proxy terminating all the secure traffic and passing non-secure traffic to rails. When rails sets the cookie to secure, somehow it itself is not able to access it.

For your normal session cookie, your doing this correctly. You should see the '_secure_session' cookie properly set as secure in your browser. For the Devise "remember me" cookie you'll need to set that in the devise config. In config/initializers/devise.rb you'll find a line somewhere around line 133 that looks like
# Options to be passed to the created cookie. For instance, you can set
# :secure => true in order to force SSL only cookies.
# config.cookie_options = {}
I changed that to:
config.rememberable_options = {:secure => Rails.env.production?}

If Set-cookie is not being sent to the browser on initial authentication, then it sounds like a devise problem.
If Set-cookie is going to the browser, but not being sent back on the next https:// request, then it's probably a mismatch on :secure => setting.
If the cookie is sent by the browser, but not passed along by HAProxy, then it's a HAProxy configuration problem.
If the cookie is in the ruby environment, and being ignored due to policy, then it's a problem somewhere in Ruby code - at a guess, around secure/not-secure cookie-matching.

Related

With google auth, how do I setup asp.net core to request https redirects when asp.net core is running http only, but reverse proxy runs https only?

The above configuration has an asp.net core app using google authentication. But for some reason the authentication redirect to Google was sending a redirect URI using http instead of https. No matter where I look, there doesn't seem to have a way to change this in the middleware.
On the apache side, I followed a tutorial that supposedly forwards the protcol using
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
However it does not work correctly. More information below and interesting security ramifications.
Now, since kestrel is running http it defaults to setting the "redirect URI" for google authentication to use the http protocol.
I have the above scenario working with http redirect URIs. I've wiresharked this and it works with the HTTP redirect URI! Hmm, I'm only allowing https in though. So I tcpdump the interaction on my server and I find that because apache requires https, it throws an HTTP 301 moved permanently to https. Great that's what I would expect. What I didn't expect was Google to redirect to the https protocol. OK, not ideal, but it works so why am I asking? It sends the data over http first to get the 301 so I've lost encryption at that point. If someone is snooping, they can read Google's entire response. I.e. I can see google post back on http with tcpdump.
Below is the only code relating to Authentication:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddGoogle(options =>
{
options.ClientId = Configuration["Authentication:Google:ClientId"];
options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
})
.AddCookie(options =>
{
options.LoginPath = "/auth/signin";
});
Note, that in my code I am NOT using app.UseHttpsRedirection();
Although, I'm still interested in finding a way to change the value of the Redirect URI sent to google from asp.net core using the ".AddGoogle" authentication, I was able to solve the problem by changing the apache configuration to:
RequestHeader set "X-Forwarded-For"
RequestHeader set "X-Forwarded-Proto" "https"
Essentially hardcoding the protocol forced it to pass "https" to the asp.net core middleware and it now works correctly.

Subdomain constraint (Rails 3) makes local server (thin) SO SLOW

I recently added a subdomain constraint to my Rails routes file
constraints(:subdomain => 'new') do
devise_for :customers do
get "/customers/sign_up" => "registrations#new"
post "/customers" => "registrations#create"
put "/customers/:id" => "registrations#update"
end
match '/' => 'roxy#index'
namespace :roxy, :path => '/' do
resources :customers
resources :surveys
end
end
In order to test the subdomain routing constraint locally, I added this line to my hosts file.
127.0.0.1 new.localhost.local
Now, I test my app in my browser at the URL new.localhost.local:3000. It takes about 10 - 15 seconds to load every page, which is unreasonably slow. If I remove the subdomain constraint and just go to 127.0.0.1:3000, everything is zippy and fast again.
What am I doing wrong? I'm new to Rails, so please tell me if there is a better way to do subdomain routing in rails, or if there is a setting I need to configure.
Figured it out. It's nothing to do with Rails or subdomains or thin. Turns out, unlike other unixy-things, OS X reserves the .local TLD for mDNS functionality. For every page, the DNS resolution was timing out before loading my app. So I just changed my /etc/hosts file to
127.0.0.1 new.localhost.dev
and everything's working great now.
Read more: http://www.justincarmony.com/blog/2011/07/27/mac-os-x-lion-etc-hosts-bugs-and-dns-resolution/

how can I create secure controller

I'm new to rails. I want to create secure control.
here what I did :
I created a secure and change routes.rb as
scope :constraints => {:protocol => 'https'} do
get "secure/index"
end
but, I.m having this error
[2012-10-08 12:07:07] ERROR bad URI \x12p\x00\x00H\x00��'.
[2012-10-08 12:07:07] ERROR bad URIpqn���|�լ%[�y���\x00\x00H\x00��'.
when I request https://localhost:3000/secure
thanx..
i think that you have a misunderstanding of secure http communication!
http and https are two different things. thats why they usually work on two different ports! http is 80 and https is usually 443.
https needs a signed certificate and which is usually handled by your webserver (apache, nginx etc). it's also possible to handle the https stuff within rails and there are some nice gems to handle configuring ssl-enforcement.
have a look at this post to get started: http://www.simonecarletti.com/blog/2011/05/configuring-rails-3-https-ssl/

how should I go about making all Devise paths use https?

Related: Rails 3 SSL routing redirects from https to http (unfortunately didn't work).
Duplicate, but the answer didn't work for me: setting up ssl on devise
I have a web app that's been working fine for a while now but I need to add SSL to the login/edit acct paths. I'm using Devise for authentication. I found an entry in the devise wiki that made the process seem pretty simple, but damn if I can get it to work. The simple part was this:
#in config/environments/production.rb
config.to_prepare { Devise::SessionsController.force_ssl }
config.to_prepare { Devise::RegistrationsController.force_ssl }
And then there's about 25 lines of code in this gist: https://gist.github.com/1040964
I got that to work well enough, but when ever I sign out I get a 301 from the sessions DELETE action that sends me to a GET.
Started DELETE "/users/sign_out" for 98.246.164.160 at 2012-03-02 01:45:42 +0000
[02 Mar 01:45 10886 INFO] Processing by Devise::SessionsController#destroy as HTML
[02 Mar 01:45 10886 INFO] Parameters: {"authenticity_token"=>"fI4VZ4V0Go2Civo3sJz8Dv5/Wtaa90ynaYr+xxx="}
[02 Mar 01:45 10886 DEBUG] Parameters: {"_method"=>"delete", "authenticity_token"=>"fI4VZ4V0Go2Civo3sJz8Dv5/Wtaa90ynaYr+xxxx=", "action"=>"destroy", "controller"=>"devise/sessions"}
[02 Mar 01:45 10886 INFO] Redirected to https://ec2-xx-xx-106-255.us-west-2.compute.amazonaws.com/users/sign_out
[02 Mar 01:45 10886 INFO] Completed 301 Moved Permanently in 3ms
Started GET "/users/sign_out" for xx.xx.164.160 at 2012-03-02 01:45:42 +0000
[02 Mar 01:45 10886 FATAL]
ActionController::RoutingError (No route matches [GET] "/users/sign_out"):
So I think I need to start over from scratch. What's the simplest way to make any Devise path use https, but the rest of the paths in my app use http? I tried this (from the SO post at the top):
#devise routes
scope :protocol => 'https://', :constraints => { :protocol => 'https://' } do
devise_for :users, :controllers => { :registrations => :registrations }
devise_for :admins
end
But no go. I need a better suggestion.
No answers yet, so here's what I concluded:
Once you access a site via https, don't access it via http until the user signs out (firesheep attack). There's a lot of stuff on Devise in the article linked above that discusses only having https on the sign in / out page. Bad idea.
All you really need is this:
#in config/environments/production.rb
config.to_prepare { Devise::SessionsController.force_ssl }
config.to_prepare { Devise::RegistrationsController.force_ssl }
I had a ton of issues surrounding 'after_sign_in_path' from Devise. It turns out that after_sign_out_path_for is expecting a path to be returned -- it's not an event, it is asking where the user should be directed. So I returned root_path :protocol => 'http://' and that took care of it.
Try making your whole app use HTTPS by adding:
#in config/environments/production.rb
config.force_ssl = true
I had quite the same problem. Sometimes I sign out fine sometimes I got 301 from DELETE action and redirect to GET. For me this was the problem.
Make sure you use https in all your Devise links (this avoids the force_ssl redirect).
In your routes.rb (only applied in production environment):
scope defaults: (Rails.env.production? ? { protocol: 'https' } : {}) do
devise_for :users
end
Now in your application use:
destroy_user_session_url # use _url instead of _path so the protocol is added!
Now your logout / sign out link (and other devise links) will point directly to https. The force_ssl rewrite from HTTP DELETE to HTTPS GET is avoided. It all works :)

switched to ssl and devise tokens are invalid

We just switched our rails 3 app over to SSL, and later noticed that password recovery tokens aren't working in production any longer. It says "invalid token" when a user tries to reset their password using the emailed link.
I'm using rails 3.0.0, devise 1.3.4, and our user model has:
devise :database_authenticatable, :invitable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
I'm not using anything like ssl_requirement, because we just did ssl universally across the app. I expired old tokens to make sure it wasn't somehow not expiring old tokens or something. I'm baffled.
This was a problem with our nginx config, and entirely unrelated to Devise. But in case anyone else ever finds themselves in a similar position, here's what went down. We set up nginx to redirect the plain http urls to https.. Specifically we had a double rewrite when someone went from domain.com to www.domain.com to https://www.domain.com, and the reset_code was getting added to the end a second time so that reset_code was coming through to the app as ?reset_code=12345?reset_code=12345.
So we changed our nginx config so:
# rewrite ^ https://$server_name$request_uri permanent;
rewrite ^(.*) https://$host$1 permanent;
and then just an optimization
rewrite ^(.*)$ https://www.domain.com$1 permanent;
and all better now.
The answer provided above is correct. But it is a temporary solution and it works only for devise. However the problem arises when you manually send a confirmation token via email.
You can fix this permanently.Go to environment/production.
and change this line
config.action_mailer.default_url_options = { :host => 'domainname', :protocol => "http"}
to
config.action_mailer.default_url_options = { :host => 'domainname', :protocol => "https"}