def issue_aft_token(): """ Issues a new antiforgery token to include in a page request. If it doesn't exist in the request, sets a cookie in the response. @param request @returns {string} the newly defined """ # check if the session is defined inside the request if not request.session: raise Error("missing session inside the request object; use the AntiforgeryValidate after the membership provider") encryption_key = request.session.guid cookie_token = request.cookies.get(cookie_name) new_token_defined = False if not cookie_token: #define a new token cookie_token = uuid.uuid1() new_token_defined = True else: can_decrypt, value = AesEncryptor.try_decrypt(cookie_token, encryption_key) if not can_decrypt: cookie_token = uuid.uuid1() new_token_defined = True else: # use the same value of before cookie_token = value if new_token_defined: cookie_token = str(cookie_token) encrypted_token = AesEncryptor.encrypt(cookie_token, str(encryption_key)) # the cookie will be set in response object inside global_handlers function request.set_aft_cookie = encrypted_token # return the token encrypted with AES; many calls always return a different value return AesEncryptor.encrypt(cookie_token, encryption_key)
def set_auth_cookie(response): set_session_cookie = hasattr(request, "set_session_cookie") unset_session_cookie = hasattr(request, "unset_session_cookie") set_aft_cookie = hasattr(request, "set_aft_cookie") if set_session_cookie: session = request.session session_guid = str(session.guid) # encrypt: session_token = AesEncryptor.encrypt(session_guid, ENCRYPTION_KEY) response.set_cookie(session_cookie, value=session_token, expires=session.expiration, httponly=True, secure=SECURE_COOKIES) if unset_session_cookie: # instruct the browser to delete the session cookie response.set_cookie(session_cookie, value="", expires=0) if set_aft_cookie: session = request.session aft = request.set_aft_cookie # set the cookie in the response response.set_cookie("aftck", value=aft, expires=session.expiration, httponly=True, secure=SECURE_COOKIES) # force XSS protection, otherwise it would be pointless to issue an antiforgery token response.headers["X-Frame-Options"] = "SAMEORIGIN" return response
def authenticate_request(*args, **kwargs): session_key = request.cookies.get(session_cookie) if session_key is None: # initialize an anonymous session initialize_anonymous_session() else: # decrypt session_guid = AesEncryptor.decrypt(session_key, ENCRYPTION_KEY) # try to perform login by session key success, result = membership_provider.try_login_by_session_key( session_guid) if success: # result is a principal object principal = result["principal"] session = result["session"] # set information inside the request setattr(request, "user", principal) setattr(request, "session", session) else: # set a flag to unset session cookie request.unset_session_cookie = True # initialize an anonymous session initialize_anonymous_session()
async def initialize_anonymous_session(self, request: Request): """ Initializes an anonymous session for the given request. :param request: incoming request to a resource that is related to this logical area. """ client_ip = self.get_client_ip(request) result = await self.membership.initialize_anonymous_session( client_ip, client_data=request.headers.get("User-Agent")) # set a flag to set a session cookie session = result.session session_cookie_name = self.config.session_cookie_name encryption_key = self.config.encryption_key session_cookie_value = AesEncryptor.encrypt(str(session.guid), encryption_key) request.set_session_cookie = True request.cookies_to_set.append( CookieToken(session_cookie_name, session_cookie_value, httponly=True, secure=self.secure_cookies)) # store user and session information in the request object request.user = result.principal request.session = session
def validate_aft(request): """ Validates the AFT of a given request. Utilizes dual token strategy (session based encrypted token, issued in both cookie and page); sent at each request in both cookie and request header, for ajax requests or form values. """ # ignore get, options, head if request.method in AFT_IGNORE_METHODS: return # requires the request to have a session if not request.session: raise RuntimeError( "Missing session inside the request object; cannot issue an AFT without session." ) # expect the request session to have a guid encryption_key = request.session.guid # get the tokens: one is always in the cookie; the second may be inside form or request header # request header is more important, assuming that pages implement AJAX requests, rather than form submission cookie_token = request.cookies.get(cookie_name) second_token = request.headers[ header_name] if header_name in request.headers else None if second_token is None: # try to read from form second_token = request.form[ form_name] if form_name in request.form else None # are the tokens present? if not cookie_token or not second_token: raise InvalidAntiforgeryTokenException() # decrypt the tokens; decryption will fail if the tokens were issued for another session; a, cookie_token = AesEncryptor.try_decrypt(cookie_token, encryption_key) b, second_token = AesEncryptor.try_decrypt(second_token, encryption_key) if not a or not b or not cookie_token or not second_token: raise InvalidAntiforgeryTokenException() # are the tokens identical? if cookie_token != second_token: raise InvalidAntiforgeryTokenException()
def issue_aft(request): """ Issues a new session based antiforgery token. This solution implements the double token strategy: the same antiforgery token is serialized twice, hence producing two different encrypted strings. One is set as response cookie; one is returned to be rendered inside the html view. """ # check if the session is defined inside the request if not hasattr(request, "session"): # missing session context; use the AntiforgeryValidate after the membership provider raise ValueError("missing session context") encryption_key = str(request.session.guid) cookie_token = request.cookies.get(cookie_name) if not cookie_token: #define a new token cookie_token = uuid.uuid1() else: can_decrypt, value = AesEncryptor.try_decrypt(cookie_token, encryption_key) if not can_decrypt: cookie_token = uuid.uuid1() else: # use the same value of before cookie_token = value # will set a new cookie in the response object cookie_token = str(cookie_token) encrypted_token = AesEncryptor.encrypt(cookie_token, encryption_key) # the cookie will be set in response object inside global_handlers function request.cookies_to_set.append( CookieToken(cookie_name, encrypted_token.decode("utf-8"), httponly=True, secure=configuration.secure_cookies)) # return the token encrypted with AES; many calls always return a different value v = AesEncryptor.encrypt(cookie_token, encryption_key) return v.decode("utf-8")
def wrapped(*args, **kwargs): if re.match("^(get|options|head)$", request.method, re.IGNORECASE): return f(*args, **kwargs) # check if the session is defined inside the request if not request.session: raise Error("missing session inside the request object; use the AntiforgeryValidate after the membership provider") encryption_key = request.session.guid # get the tokens: one is always in the cookie; the second may be inside form or request header # request header is more important, assuming that pages implement AJAX requests, rather than form submission cookie_token = request.cookies.get(cookie_name) second_token = request.headers[header_name] if header_name in request.headers else None if second_token is None: # try to read from form second_token = request.form[form_name] if form_name in request.form else None # are the tokens present? if not cookie_token or not second_token: return handle_unauthorized() # decrypt the tokens; decryption will fail if the tokens were issued for another session; a, cookie_token = AesEncryptor.try_decrypt(cookie_token, encryption_key) b, second_token = AesEncryptor.try_decrypt(second_token, encryption_key) if not a or not b or not cookie_token or not second_token: return handle_unauthorized() # are the tokens identical? if cookie_token != second_token: return handle_unauthorized() # validation succeeded # continue normally return f(*args, **kwargs)
async def _authenticate_user(self, request: Request): """ If the area features membership, it invokes the methods of the underlying membership provider to authenticate the user, supporting anonymous authentication. :param request: request to authenticate. """ request.user = None encryption_key = self.config.encryption_key membership = self.membership set_anonymous_session = False if self.membership: # does the request contains the session cookie for this area? session_cookie_name = self.config.session_cookie_name session_key = request.cookies.get(session_cookie_name) if session_key: # try to load the session # decrypt the session key success, session_guid = AesEncryptor.try_decrypt( session_key, encryption_key) if success: # try to perform login by session key success, result = await membership.try_login_by_session_key( session_guid) if success: # result is a principal object request.user = result.principal request.session = result.session else: # the login by session cookie failed: the session could be expired set_anonymous_session = True else: # session key decryption failed set_anonymous_session = True else: # the request does not contain a session cookie for this area set_anonymous_session = True if set_anonymous_session: # initialize an anonymous session await self.initialize_anonymous_session(request) return self
def authenticate_request(*args, **kwargs): session_key = request.cookies.get(session_cookie) if session_key is None: # initialize an anonymous session initialize_anonymous_session() else: # decrypt session_guid = AesEncryptor.decrypt(session_key, ENCRYPTION_KEY) # try to perform login by session key success, result = membership_provider.try_login_by_session_key(session_guid) if success: # result is a principal object principal = result["principal"] session = result["session"] # set information inside the request setattr(request, "user", principal) setattr(request, "session", session) else: # set a flag to unset session cookie request.unset_session_cookie = True # initialize an anonymous session initialize_anonymous_session()