So... would replaing this in gluon.main.py is_https = env.wsgi_url_scheme in ['https', 'HTTPS'] or env.https=='on')
with is_https = env.wsgi_url_scheme in ['https', 'HTTPS'] or env.https=='on' or request.env.http_x_forwarded_proto in ['https', 'HTTPS'] address the first issue? Massimo On Thursday, 4 October 2012 07:05:17 UTC-5, Yarin wrote: > > I'm revising my stance on this. After further digging around, I'm gonna go > with Niphlod's position that securing only the login traffic without > securing the entire session is for the most part pretty worthless. While > this might have value to some sites that have to deal with mixed content, > the complexity it introduces isn't worth it. > > I'm also taking back my recommendation that we need to have a setting to > explicitly allow SSL traffic. I think it's fine to just check the headers > for forwarded SSL traffic and trust that it is. Yes, headers can be > spoofed, but I can't think of how this could be exploited on the user end- > > So that leaves only *two recommended changes:* > > - When checking whether HTTPS, check for forwarded SSL headers with if > request.env.http_x_forwarded_proto in ['https', 'HTTPS']: > - Add a auth.secure = True convenience setting, which would call > requires_https() while the user is logged in, and on all > login/registration > methods. > > I'll update the ticket > > > > On Friday, September 21, 2012 2:26:36 PM UTC-4, Yarin wrote: >> >> Done http://code.google.com/p/web2py/issues/detail?id=1023 >> >> On Friday, September 21, 2012 2:05:41 PM UTC-4, Massimo Di Pierro wrote: >>> >>> Yarin, please open an issue on google code as suggested enhancement so >>> ti does not get lost. Also feel free to move the discussion on web2py >>> developers. >>> >>> >>> On Friday, 21 September 2012 12:22:57 UTC-5, Yarin wrote: >>>> >>>> Here's a complete example of our own implementation (simplified, >>>> untested) using the proposed auth settings: >>>> >>>> *In our model:* >>>> >>>> def force_https(trust_proxy = False, secure_session = False): >>>> """ Enforces HTTPS in appropriate environments >>>> >>>> Args: >>>> trust_proxy: Can we trust proxy header 'http_x_forwarded_proto' >>>> to determine SSL. >>>> (Set this only if ALL your traffic comes via trusted proxy.) >>>> secure_session: Secure the session as well. >>>> (Do this only when enforcing SSL throughout the session) >>>> """ >>>> >>>> # If cronjob or scheduler, exit: >>>> cronjob = request.global_settings.cronjob >>>> cmd_options = request.global_settings.cmd_options >>>> if cronjob or (cmd_options and cmd_options.scheduler): >>>> return >>>> >>>> # If local host, exit: >>>> if request.env.remote_addr == "127.0.0.1": >>>> return >>>> >>>> # If already HTTPS, exit: >>>> if request.env.wsgi_url_scheme in ['https', 'HTTPS']: >>>> if secure_session: >>>> current.session.secure() >>>> return >>>> >>>> # If HTTPS request forwarded over HTTP via a SSL-terminating >>>> proxy, exit: >>>> if trust_proxy and request.env.http_x_forwarded_proto in ['https', >>>> 'HTTPS']: >>>> if secure_session: >>>> current.session.secure() >>>> return >>>> >>>> # Redirect to HTTPS: >>>> redirect(URL(scheme='https', args=request.args, vars=request.vars)) >>>> >>>> # If a login function, force SSL: >>>> if request.controller == 'default' and request.function == 'user' andauth >>>> .settings.force_ssl_login: >>>> force_https(trust_proxy = auth.settings.is_proxied, secure_session >>>> = auth.settings.force_ssl_session) >>>> # If user is logged in and we're enforcing a full SSL session: >>>> elif auth.is_logged_in() and auth.settings.force_ssl_session: >>>> force_https(trust_proxy = auth.settings.is_proxied, secure_session >>>> = True) >>>> >>>> def on_login(form): >>>> """ Post login redirection""" >>>> >>>> # If we're enforcing SSL on login only, redirect from HTTPS to >>>> HTTP immediately after login: >>>> if auth.settings.force_ssl_login is True and >>>> auth.settings.force_ssl_session >>>> is False: >>>> if request.env.wsgi_url_scheme in ['https', 'HTTPS'] or request >>>> .env.http_x_forwarded_proto in ['https', 'HTTPS']: >>>> >>>> # Extract the post-login url value from auth >>>> # (hack - look at end of login() function in tools.py. >>>> This belongs in Auth itself.): >>>> login_next_path = auth.next or auth.settings.login_next >>>> # Build an absolute, HTTP url from it: >>>> login_next_url = URL(scheme='http',c='default',f='index') >>>> +login_next_path >>>> [1:] >>>> # Redirect to the HTTP URL: >>>> redirect(login_next_url) >>>> >>>> auth.settings.login_onaccept = on_login >>>> >>>> >>>> >>>> >>>> On Friday, September 21, 2012 12:35:37 PM UTC-4, Yarin wrote: >>>>> >>>>> You can't detect this- it must be a setting. Please see my previous >>>>> answer: >>>>> https://groups.google.com/forum/#!msg/web2py/me1e5d6Dudk/VQRQhdiryccJ >>>>> >>>>> "you cannot detect whether proxied traffic is real because headers are >>>>> unreliable. Instead you must securely set up a server behind a proxy and >>>>> set the .is_proxied flag explicitly." >>>>> >>>>> "you can't mix direct and proxied traffic. To be able to handle >>>>> proxy-terminated SSL, we need to know that *all* the traffic is via a >>>>> trusted proxy." >>>>> >>>>> >>>>> On Friday, September 21, 2012 12:05:56 PM UTC-4, Massimo Di Pierro >>>>> wrote: >>>>>> >>>>>> Yes but how do you detect if is_proxied reliably? >>>>>> >>>>>> On Friday, 21 September 2012 10:28:26 UTC-5, Yarin wrote: >>>>>>> >>>>>>> FYI this is the enforcer function we wrote for our implementation- >>>>>>> basically a rewrite of request.requires_https(): >>>>>>> >>>>>>> def force_https(trust_proxy = False): >>>>>>> """ Enforces HTTPS in appropriate environments >>>>>>> >>>>>>> Args: >>>>>>> trust_proxy: Can we trust proxy header 'http_x_forwarded_proto' >>>>>>> to determine SSL. >>>>>>> (Set this only if ALL your traffic comes via trusted proxy.) >>>>>>> """ >>>>>>> >>>>>>> # If cronjob or scheduler, exit: >>>>>>> cronjob = request.global_settings.cronjob >>>>>>> cmd_options = request.global_settings.cmd_options >>>>>>> if cronjob or (cmd_options and cmd_options.scheduler): >>>>>>> return >>>>>>> >>>>>>> # If local host, exit: >>>>>>> if request.env.remote_addr == "127.0.0.1": >>>>>>> return >>>>>>> >>>>>>> # If already HTTPS, exit: >>>>>>> if request.env.wsgi_url_scheme in ['https', 'HTTPS']: >>>>>>> return >>>>>>> >>>>>>> # If HTTPS request forwarded over HTTP via SSL-terminating proxy, >>>>>>> exit: >>>>>>> if trust_proxy and request.env.http_x_forwarded_proto in ['https', >>>>>>> 'HTTPS']: >>>>>>> return >>>>>>> >>>>>>> # Redirect to HTTPS: >>>>>>> redirect(URL(scheme='https', args=request.args, vars=request.vars)) >>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> On Friday, September 21, 2012 9:53:36 AM UTC-4, Yarin wrote: >>>>>>>> >>>>>>>> The completely naive approach would be to do: >>>>>>>> >>>>>>>> if request.env.http_x_forwarded_for and \ >>>>>>>> request.env.http_x_forwarded_proto in ['https', 'HTTPS']: >>>>>>>> # Is HTTPS... >>>>>>>> >>>>>>>> But you cannot detect whether proxied traffic is real because >>>>>>>> headers are unreliable. Instead it is up to the user to securely set >>>>>>>> up a >>>>>>>> server behind a proxy and set the .is_proxied flag themselves. >>>>>>>> >>>>>>>> *Example:* >>>>>>>> We put our app server behind an SSL-terminating load balancer on >>>>>>>> the cloud. The domain app.example.com points to the loadbalancer, >>>>>>>> so we configure app server's Apache to allow traffic from that domain >>>>>>>> only, >>>>>>>> and block any outside direct traffic. Then we set * >>>>>>>> auth.settings.is_proxied* to tell web2py "this proxy traffic is >>>>>>>> legit" >>>>>>>> >>>>>>>> HTTPS/443 requests will hit the loadbalancer, and be transformed to >>>>>>>> HTTP/80 traffic with *http_x_forwarded_for* and * >>>>>>>> http_x_forwarded_proto* headers set. Now we can confidently check: >>>>>>>> >>>>>>>> if auth.settings.is_proxied and \ >>>>>>>> request.env.http_x_forwarded_proto in ['https', 'HTTPS']: >>>>>>>> # Is HTTPS... >>>>>>>> >>>>>>>> In other words *http_x_forwarded_for* header is useless and you >>>>>>>> can't mix direct and proxied traffic. To be able to handle >>>>>>>> proxy-terminated >>>>>>>> SSL, we need to know that *all* the traffic is via a trusted proxy. >>>>>>>> >>>>>>>> >>>>>>>> On Friday, September 21, 2012 8:40:35 AM UTC-4, Massimo Di Pierro >>>>>>>> wrote: >>>>>>>>> >>>>>>>>> Can you suggest a way to detect that? >>>>>>>>> >>>>>>>>> On Thursday, 20 September 2012 13:56:55 UTC-5, Yarin wrote: >>>>>>>>>> >>>>>>>>>> @Massimo - that'd be great. >>>>>>>>>> >>>>>>>>>> One more kink to throw in is recognizing proxied SSL calls. This >>>>>>>>>> requires knowing whether you can trust the traffic headers (e.g. >>>>>>>>>> having >>>>>>>>>> apache locked down to all traffic except your load balancer), so >>>>>>>>>> maybe we >>>>>>>>>> need a trust_proxied_ssl or is_proxied setting somewhere? >>>>>>>>>> >>>>>>>>>> if request.env.http_x_forwarded_for and >>>>>>>>>> request.env.http_x_forwarded_proto >>>>>>>>>> in ['https', 'HTTPS'] and auth.settings.is_proxied: >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Thursday, September 20, 2012 12:52:22 PM UTC-4, Massimo Di >>>>>>>>>> Pierro wrote: >>>>>>>>>>> >>>>>>>>>>> I think we should do something like this. >>>>>>>>>>> >>>>>>>>>>> I think we should have auth.settings.force_ssl_login >>>>>>>>>>> and auth.settings.force_ssl_login. >>>>>>>>>>> We could add secure=True option to existing requires validators. >>>>>>>>>>> >>>>>>>>>>> This should not be enforced from localhost. >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> On Thursday, 20 September 2012 09:07:14 UTC-5, Yarin wrote: >>>>>>>>>>>> >>>>>>>>>>>> A proposal for improving SSL support in web2py >>>>>>>>>>>> >>>>>>>>>>>> For authenticated web applications, there are two "grades" of >>>>>>>>>>>> SSL implementions: Forcing SSL on login, vs forcing SSL on the >>>>>>>>>>>> entire >>>>>>>>>>>> authenticated session. >>>>>>>>>>>> >>>>>>>>>>>> In the first case, HTTPS is forced on login/registration, but >>>>>>>>>>>> reverts back to HTTP upon authentication. This protects against >>>>>>>>>>>> passwords >>>>>>>>>>>> from being sent unencrypted, but won't prevent session hijacking >>>>>>>>>>>> as the >>>>>>>>>>>> session cookie can still be compromised on subsequent HTTP >>>>>>>>>>>> requests. (See >>>>>>>>>>>> Firesheep <http://codebutler.com/firesheep> for details). >>>>>>>>>>>> Nonetheless, many sites choose this approach for performance >>>>>>>>>>>> reasons, as >>>>>>>>>>>> SSL-delivered content is not cached by browsers as efficiently >>>>>>>>>>>> (discussed >>>>>>>>>>>> on 37signals >>>>>>>>>>>> blog<http://37signals.com/svn/posts/1431-mixed-content-warning-how-i-loathe-thee> >>>>>>>>>>>> ). >>>>>>>>>>>> >>>>>>>>>>>> In the second case, the entire authenticated session is secured >>>>>>>>>>>> by forcing all traffic to go over HTTPS while a user is logged in >>>>>>>>>>>> *and* by securing the session cookie so that it will only be >>>>>>>>>>>> sent by the browser over HTTPS. >>>>>>>>>>>> >>>>>>>>>>>> (Also discussed in web2py users group - Auth over >>>>>>>>>>>> SSL<https://groups.google.com/d/msg/web2py/7qoHMs-4Va8/jRFOqYHri4gJ> >>>>>>>>>>>> ) >>>>>>>>>>>> >>>>>>>>>>>> web2py should make it easier to deal with these scenarios. I >>>>>>>>>>>> just implemented a case-1 type solution and it took quite a bit of >>>>>>>>>>>> work. >>>>>>>>>>>> >>>>>>>>>>>> Moreover, web2py currently provides two SSL-control functions, >>>>>>>>>>>> which, taken on their own, can lead to problems for the >>>>>>>>>>>> uninitiated: >>>>>>>>>>>> >>>>>>>>>>>> - session.secure() will ensure that the session cookie is >>>>>>>>>>>> only transmitted over HTTPS, but doesn't force HTTPS, so that >>>>>>>>>>>> for any >>>>>>>>>>>> subsequent session calls made over HTTP will simply not have >>>>>>>>>>>> access to the >>>>>>>>>>>> auth session, but this is not obvious (Correct me if I'm wrong) >>>>>>>>>>>> - request.requires_https() (undocumented?) is a misnomer, >>>>>>>>>>>> because if forces HTTPS but then assumes a case-2 scenario >>>>>>>>>>>> and secures the session cookie >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> *Proposals:* >>>>>>>>>>>> >>>>>>>>>>>> - SSL auth settings >>>>>>>>>>>> - auth.settings.force_ssl_login - Forces HTTPS for >>>>>>>>>>>> login/registration >>>>>>>>>>>> - auth.settings.force_ssl_session - Forces HTTPS >>>>>>>>>>>> throughout an authenticated session, and secure the session >>>>>>>>>>>> cookie (If >>>>>>>>>>>> True, force_ssl_login not necessary) >>>>>>>>>>>> - Other more granular controls >>>>>>>>>>>> - @requires_https() - decorator for controller functions >>>>>>>>>>>> that forces HTTPS for that function only >>>>>>>>>>>> - 'secure=True' option on forms ensures submission over >>>>>>>>>>>> HTTPS >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> --