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