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
>>>>>>>>>>>>    
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>

-- 



Reply via email to