@@ -929,9 +929,38 @@ def handle_csrf(self, xmlrpc=False):
929929 debugging seems to be an advantage.
930930
931931 '''
932+ # Create the otks handle here as we need it almost immediately.
933+ # If this is perf issue, set to None here and check below
934+ # once all header checks have passed if it needs to be opened.
935+ otks = self .db .getOTKManager ()
936+
932937 # Assume: never allow changes via GET
933938 if self .env ['REQUEST_METHOD' ] not in ['POST' , 'PUT' , 'DELETE' ]:
939+ if "@csrf" in self .form :
940+ # We have a nonce being used with a method it should
941+ # not be. If the nonce exists, report to admin so they
942+ # can fix the nonce leakage and destroy it. (nonces
943+ # used in a get are more exposed than those used in a
944+ # post.) Note, I don't attempt to validate here since
945+ # existance here is the sign of a failure. If nonce
946+ # exists try to report the referer header to try to
947+ # find where this comes from so it can be fixed. If
948+ # nonce doesn't exist just ignore it. Maybe we should
949+ # report, but somebody could spam us with a ton of
950+ # invalid keys and fill up the logs.
951+ if 'HTTP_REFERER' in self .env :
952+ referer = self .env ['HTTP_REFERER' ]
953+ else :
954+ referer = self ._ ("Referer header not available." )
955+ key = self .form ['@csrf' ].value
956+ if otks .exists (key ):
957+ logger .error (
958+ self ._ ("csrf key used with wrong method from: %s" ),
959+ referer )
960+ otks .destroy (key )
961+ self .db .commit ()
934962 return True
963+
935964 config = self .instance .config
936965 user = self .db .getuid ()
937966
@@ -1045,7 +1074,6 @@ def handle_csrf(self, xmlrpc=False):
10451074 logger .error (self ._ ("csrf X-REQUESED-WITH xmlrpc required header check failed for user%s." ), user )
10461075 return False
10471076
1048- otks = self .db .getOTKManager ()
10491077 enforce = config ['WEB_CSRF_ENFORCE_TOKEN' ]
10501078 if "@csrf" not in self .form :
10511079 if enforce == 'required' :
@@ -1061,8 +1089,14 @@ def handle_csrf(self, xmlrpc=False):
10611089 key = self .form ['@csrf' ].value
10621090 uid = otks .get (key , 'uid' , default = None )
10631091 sid = otks .get (key , 'sid' , default = None )
1092+ if __debug__ :
1093+ ts = otks .get (key , '__timestamp' , default = None )
1094+ print ("Found key %s for user%s sess: %s, ts %s, time %s" % (key , uid , sid , ts , time .time ()))
10641095 current_session = self .session_api ._sid
1065- # validate against user and session
1096+
1097+ # The key has been used or compromised. Delete it to prevent replay.
1098+ otks .destroy (key )
1099+ self .db .commit ()
10661100
10671101 '''
10681102 # I think now that LogoutAction redirects to
@@ -1100,20 +1134,19 @@ def handle_csrf(self, xmlrpc=False):
11001134 self.add_error_message("Reload window before logging in.")
11011135 '''
11021136
1103- # The key has been used or compromised. Delete it to prevent replay.
1104- otks .destroy (key )
1105- self .db .commit ()
1106-
1137+ # validate against user and session
11071138 if uid != user :
11081139 if enforce in ('required' , "yes" ):
11091140 self .add_error_message (self ._ ("Invalid csrf token found: %s" )% key )
1141+ logger .error (self ._ ("csrf uid mismatch for user%s." ), user )
11101142 return False
11111143 elif enforce == 'logfailure' :
11121144 logger .warning (self ._ ("csrf uid mismatch for user%s." ), user )
11131145 if current_session != sid :
11141146 if enforce in ('required' , "yes" ):
11151147 self .add_error_message (self ._ (
11161148 "Invalid csrf session found: %s" )% key )
1149+ logger .error (self ._ ("csrf session mismatch for user%s." ), user )
11171150 return False
11181151 elif enforce == 'logfailure' :
11191152 logger .warning (self ._ ("csrf session mismatch for user%s." ), user )
0 commit comments