Yes exactly On Thursday, October 4, 2012 12:38:34 PM UTC-4, Massimo Di Pierro wrote: > > 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'] orrequest >>>>> .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 >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>>
--