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' and auth > .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 >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> --

