2009-11-02 24 views
23

Sto mantenendo un sito Ruby on Rails e sono confuso su come eseguire i reindirizzamenti agli URL relativi utilizzando il protocollo https.Rails reindirizza con https

posso creare correttamente un reindirizzamento a un URL relativo utilizzando http, ad esempio:

redirect_to "/some_directory/" 

ma non riesco a discernere come creare un reindirizzamento a un URL utilizzando il protocollo HTTPS. Sono stato solo in grado di farlo utilizzando URL assoluti, ad esempio:

redirect_to "https://mysite.com/some_directory/" 

Vorrei mantenere il mio codice pulito, e utilizzando gli URL relativi sembra una buona idea. Qualcuno sa come raggiungerlo in Rails?

+0

Posso ottenere un chiarimento sulla tua domanda. Vuoi forzare le persone a utilizzare sempre HTTPS sul tuo sito o solo per alcuni URL? Essere predefinito RAILS continuerà a utilizzare l'HTTPS se la richiesta corrente è HTTPS. – scottd

risposta

8

Siete probabilmente meglio utilizzare ssl_requirement e non preoccuparsi se un link o redirec t sta o non sta usando https. Con ssl_requirement, dichiari quali azioni richiedono SSL, quali sono in grado di SSL e quali sono richieste per non usare SSL.

Se stai reindirizzando da qualche parte al di fuori della tua app Rails, quindi specificando il protocollo come suggerisce Olly funzionerà.

+14

In questi giorni guardiamo a 'force_ssl', disponibile nei binari 3.1 e successivi. –

+3

Matt se hai fatto il tuo commento una risposta separata, voterei a favore. – Spundun

-3

Aprire la classe che ha redirect_to e aggiungere un metodo redirect_to_secure_of con un'implementazione appropriata. Quindi chiamare:

redirect_to_secure_of "/some_directory/" 

mettere questo metodo nella directory lib o da qualche parte utile.

+0

Grazie, ma sto cercando un po 'più di dettaglio di "un'implementazione appropriata". –

+0

Scusa, ho pensato che stavi chiedendo come rendere pulito il codice * calling *. – yfeldblum

-1

Gli URL relativi, per definizione, utilizzano il protocollo e l'host correnti. Se si desidera modificare il protocollo in uso, è necessario fornire l'URL assoluto. Vorrei prendere il consiglio di Giustizia e creare un metodo che fa per voi:

def redirect_to_secure(relative_uri) 
    redirect_to "https://" + request.host + relative_uri 
end 
35

Il metodo ActionController::Base#redirect_to prende un hash opzioni, uno dei parametri di cui una :protocol che consente di chiamare:

redirect_to :protocol => 'https://', 
      :controller => 'some_controller', 
      :action => 'index' 

Vedere la definizione per #redirect_to e #url_for per ulteriori informazioni sulle opzioni.


In alternativa, e soprattutto se SSL deve essere utilizzata per tutte le azioni di controllo, si potrebbe adottare un approccio più dichiarativo utilizzando un before_filter. In ApplicationController potreste definire il seguente metodo:

def redirect_to_https 
    redirect_to :protocol => "https://" unless (request.ssl? || request.local?) 
end 

È quindi possibile aggiungere filtri nei tuoi quei controllori che hanno SSL azioni che richiedono, ad esempio:

class YourController 
    before_filter :redirect_to_https, :only => ["index", "show"] 
end 

Oppure, se avete bisogno di SSL in tutta l'intera applicazione , dichiara il filtro in ApplicationController:

class ApplicationController 
    before_filter :redirect_to_https 
end 
+0

Puoi usare ': protocol => 'https: //'' se stai passando un hash a 'redirect_to' se stai passando un URL relativo come nella domanda sopra, non funzionerà. Sono d'accordo che un 'before_filter' è il modo migliore per andare in generale. –

3

Se si desidera controllare globalmente il protocollo degli URL generati nei controller, è possibile eseguire l'override del metodo url_options nel controller dell'applicazione.Si potrebbe forzare il protocollo degli URL generati in base alla rotaie ENV in questo modo:

def url_options 
    super 
    @_url_options.dup.tap do |options| 
     options[:protocol] = Rails.env.production? ? "https://" : "http://" 
     options.freeze 
    end 
    end 

questo esempio funziona in Rails 3.2.1, io non sono esattamente sicuro per le future versioni precedenti o.

23

Se volete che il vostro intera applicazione per essere servito su HTTPS poi dal Rails 4.0 il modo migliore per farlo è quello di consentire force_ssl nel file di configurazione in questo modo:

# config/environments/production.rb 
Rails.application.configure do 
    # [..] 

    # Force all access to the app over SSL, use Strict-Transport-Security, 
    # and use secure cookies. 
    config.force_ssl = true 
end 

Per default l'opzione è già presente nello config/environments/production.rb nelle app appena generate, ma è commentata.

Come dice il commento, questo non solo reindirizzerà a https, ma imposta anche l'intestazione Strict-Transport-Security (HSTS) e si assicura che il flag di sicurezza sia impostato su tutti i cookie. Entrambe le misure aumentano la sicurezza della tua applicazione senza inconvenienti significativi. Utilizza ActionDispatch:SSL.

Le impostazioni di scadenza dell'HSTS sono impostate su un anno per impostazione predefinita e non includono sottodomini, il che probabilmente va bene per la maggior parte delle applicazioni. È possibile configurare questa con l'opzione hsts:

config.hsts = { 
    expires: 1.month.to_i, 
    subdomains: false, 
} 

se si sta eseguendo Rails 3 (> = 3.1) o non vogliono usare https per l'intera applicazione, quindi è possibile utilizzare il metodo force_ssl in un controllore:

class SecureController < ApplicationController 
    force_ssl 
end 

Questo è tutto. Puoi impostarlo per controller o nel tuo ApplicationController. È possibile forzare https in modo condizionale utilizzando le familiari opzioni if o unless; ad esempio:

# Only when we're not in development or tests 
force_ssl unless: -> { Rails.env.in? ['development', 'test'] } 
+0

force_ssl non è una grande opzione se stai sviluppando dietro un tunnel. Ad esempio, un tunnel ngrok tls consente di specificare un certificato. Quindi ngrok termina la connessione tls con il cert specificato e inoltra non crittografato alla tua app (quindi la tua app pensa che sia tutto http). Ciò significa che non devi configurare un proxy nginx e fare https sulla tua casella di sviluppo. –

+0

@MichaelJohnston Questo è il motivo per cui è possibile usare 'unless' per non forzare SSL in dev (vedere l'ultima riga nella mia risposta). – Carpetsmoker

2

In rotaie 4 si può usare il force_ssl_redirect before_action applicare SSL per un singolo controller. Si prega di notare che utilizzando questo metodo i cookie non saranno contrassegnati come sicuri e non verrà utilizzato HSTS.

+0

Potete per favore elaborare l'ultima frase? Per qualcuno che non ha familiarità con "contrassegnare i cookie come sicuri" e HSTS. –

1

Questa risposta è in qualche modo tangente alla domanda originale, ma la registro nel caso in cui altri finiscano qui in circostanze simili a me stesso.

Avevo una situazione in cui era necessario che Rails utilizzasse il proto https negli helper URL, ecc., Anche se l'origine di tutte le richieste non è criptata (http).

Ora, di solito in questa situazione (che è normale quando Rails è dietro un proxy o di bilanciamento del carico inversa, ecc), l'intestazione x-forwarded-proto è stabilito dal proxy inverso o qualsiasi altra cosa, quindi, anche se le richieste sono in chiaro tra il proxy & rotaie (probabilmente non consigliabile in produzione tra l'altro) rotaie pensa che tutto sia in https.

Avevo bisogno di correre dietro un tunnel tpl ngrok. Volevo che ngrok terminasse i tls con i certificati di licenza che ho specificato. Tuttavia, quando lo fa, ngrok non offre la possibilità di personalizzare le intestazioni, inclusa l'impostazione x-forwarded-proto (sebbene questa funzione sia pianificata in futuro).

La soluzione si è rivelata piuttosto semplice: Rotaie non dipende né il protocollo di origine o se x-forwarded-proto è impostato direttamente, ma sul rack env var rack.url_scheme. Quindi ho solo bisogno di aggiungere questo middleware Rack in sviluppo:

class ForceUrlScheme 
    def initialize(app) 
    @app = app 
    end 

    def call(env) 
    env['rack.url_scheme'] = 'https' 
    @app.call(env) 
    end 
end 
Problemi correlati