Repository: gratipay/gratipay.com

Issue:
login requires Referer
This just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.

My browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0

login requires Referer
This just started recently.  When I try to log in I get a blank page.  Inspecting the response in my console shows I'm getting a 500 from the server.

My browser (ABrowser): Mozilla/5.0 (X11; Linux i686; rv:33.0) Gecko/20100101 Firefox/33.0



Discussion:
Thanks for the report, @singpolyma. Sorry for the trouble.

Might be this? https://app.getsentry.com/gratipay/gratipay-com/group/39866655/ (restricted)

@whit537 hard to say, since I can't see the details in sentry myself :)

I set `network.http.sendRefererHeader` to 1 instead of 0 in my browser, and now it works.  But it seems odd to require a referer on that page?

Thanks for debugging, @singpolyma.

A lack of Referer can result in a 403 because of the CSRF protection, but I don't see how it could cause a 500.

@singpolyma Firefox has [finer-tuned Referer control](http://seenthis.net/messages/261416) now.

Thanks for the report, @singpolyma. Sorry for the trouble.

Might be this? https://app.getsentry.com/gratipay/gratipay-com/group/39866655/ (restricted)

@whit537 hard to say, since I can't see the details in sentry myself :)

I set `network.http.sendRefererHeader` to 1 instead of 0 in my browser, and now it works.  But it seems odd to require a referer on that page?

Thanks for debugging, @singpolyma.

A lack of Referer can result in a 403 because of the CSRF protection, but I don't see how it could cause a 500.

@singpolyma Firefox has [finer-tuned Referer control](http://seenthis.net/messages/261416) now.


Resolving patch:
diff --git a/gratipay/security/authentication.py b/gratipay/security/authentication.py
--- a/gratipay/security/authentication.py
+++ b/gratipay/security/authentication.py
@@ -58,8 +58,6 @@ def _turn_off_csrf(request):
     csrf_token = csrf._get_new_csrf_key()
     request.headers.cookie['csrf_token'] = csrf_token
     request.headers['X-CSRF-TOKEN'] = csrf_token
-    if 'Referer' not in request.headers:
-        request.headers['Referer'] = 'https://%s/' % csrf._get_host(request)
 
 def set_request_context_user(request):
     """Set request.context['user']. This signs the user in.
diff --git a/gratipay/security/csrf.py b/gratipay/security/csrf.py
--- a/gratipay/security/csrf.py
+++ b/gratipay/security/csrf.py
@@ -10,8 +10,6 @@
 
 from datetime import timedelta
 import re
-import urlparse
-from aspen import log_dammit
 
 
 #from django.utils.cache import patch_vary_headers
@@ -36,20 +34,9 @@ def patch_vary_headers(response, newheaders):
     response.headers['Vary'] = ', '.join(vary_headers + additional_headers)
 
 
-#from django.utils.http import same_origin
-def same_origin(url1, url2):
-    """
-    Checks if two URLs are 'same-origin'
-    """
-    p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
-    return (p1.scheme, p1.hostname, p1.port) == (p2.scheme, p2.hostname, p2.port)
-
-
 from aspen import Response
 from crypto import constant_time_compare, get_random_string
 
-REASON_NO_REFERER = "Referer checking failed - no Referer."
-REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
 REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
 REASON_BAD_TOKEN = "CSRF token missing or incorrect."
 
@@ -72,15 +59,6 @@ def _sanitize_token(token):
         return _get_new_csrf_key()
     return token
 
-def _is_secure(request):
-    import gratipay
-    return gratipay.canonical_scheme == 'https'
-
-def _get_host(request):
-    """Returns the HTTP host using the request headers.
-    """
-    return request.headers.get('X-Forwarded-Host', request.headers['Host'])
-
 
 
 def get_csrf_token_from_request(request):
@@ -99,32 +77,6 @@ def get_csrf_token_from_request(request):
     # Assume that anything not defined as 'safe' by RC2616 needs protection
     if request.line.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
 
-        if _is_secure(request):
-            # Suppose user visits http://example.com/
-            # An active network attacker (man-in-the-middle, MITM) sends a
-            # POST form that targets https://example.com/detonate-bomb/ and
-            # submits it via JavaScript.
-            #
-            # The attacker will need to provide a CSRF cookie and token, but
-            # that's no problem for a MITM and the session-independent
-            # nonce we're using. So the MITM can circumvent the CSRF
-            # protection. This is true for any HTTP connection, but anyone
-            # using HTTPS expects better! For this reason, for
-            # https://example.com/ we need additional protection that treats
-            # http://example.com/ as completely untrusted. Under HTTPS,
-            # Barth et al. found that the Referer header is missing for
-            # same-domain requests in only about 0.2% of cases or less, so
-            # we can use strict Referer checking.
-            referer = request.headers.get('Referer')
-            if referer is None:
-                raise Response(403, REASON_NO_REFERER)
-
-            good_referer = 'https://%s/' % _get_host(request)
-            if not same_origin(referer, good_referer):
-                reason = REASON_BAD_REFERER % (referer, good_referer)
-                log_dammit(reason)
-                raise Response(403, reason)
-
         if csrf_token is None:
             raise Response(403, REASON_NO_CSRF_COOKIE)
 


Test patch:
diff --git a/tests/py/test_hooks.py b/tests/py/test_hooks.py
--- a/tests/py/test_hooks.py
+++ b/tests/py/test_hooks.py
@@ -38,12 +38,12 @@ def test_canonize_canonizes(self):
         assert response.code == 302
         assert response.headers['Location'] == b'https://gratipay.com/'
 
-    def test_session_cookie_isnt_overwritten_by_canonizer(self):
-        # https://github.com/gratipay/gratipay.com/issues/940
-
+    def test_no_cookies_over_http(self):
+        """
+        We don't want to send cookies over HTTP, especially not CSRF and
+        session cookies, for obvious security reasons.
+        """
         self.make_participant('alice')
-
-        # Make a request that canonizer will redirect.
         redirect = self.client.GET( "/"
                                   , auth_as='alice'
                                   , HTTP_X_FORWARDED_PROTO=b'http'
@@ -51,11 +51,7 @@ def test_session_cookie_isnt_overwritten_by_canonizer(self):
                                   , raise_immediately=False
                                    )
         assert redirect.code == 302
-        assert SESSION not in redirect.headers.cookie
-
-        # This is bad, because it means that the user will be signed out of
-        # https://gratipay.com/ if they make a request for
-        # http://gratipay.com/.
+        assert not redirect.headers.cookie
 
     def test_session_cookie_not_set_under_API_key_auth(self):
         alice = self.make_participant('alice', claimed_time='now')