Beispiel #1
0
def check_csrf_token(request,
                     token='csrf_token',
                     header='X-CSRF-Token',
                     raises=True):
    """ Check the CSRF token in the request's session against the value in
    ``request.params.get(token)`` or ``request.headers.get(header)``.
    If a ``token`` keyword is not supplied to this function, the string
    ``csrf_token`` will be used to look up the token in ``request.params``.
    If a ``header`` keyword is not supplied to this function, the string
    ``X-CSRF-Token`` will be used to look up the token in ``request.headers``.

    If the value supplied by param or by header doesn't match the value
    supplied by ``request.session.get_csrf_token()``, and ``raises`` is
    ``True``, this function will raise an
    :exc:`pyramid.exceptions.BadCSRFToken` exception.
    If the check does succeed and ``raises`` is ``False``, this
    function will return ``False``.  If the CSRF check is successful, this
    function will return ``True`` unconditionally.

    Note that using this function requires that a :term:`session factory` is
    configured.

    .. versionadded:: 1.4a2
    """
    supplied_token = request.params.get(token, request.headers.get(header))
    if supplied_token != request.session.get_csrf_token():
        if raises:
            raise BadCSRFToken('check_csrf_token(): Invalid token')
        return False
    return True
Beispiel #2
0
def check_csrf_token(request,
                     token='csrf_token',
                     header='X-CSRF-Token',
                     raises=True):
    """ Check the CSRF token returned by the
    :class:`pyramid.interfaces.ICSRFStoragePolicy` implementation against the
    value in ``request.POST.get(token)`` (if a POST request) or
    ``request.headers.get(header)``. If a ``token`` keyword is not supplied to
    this function, the string ``csrf_token`` will be used to look up the token
    in ``request.POST``. If a ``header`` keyword is not supplied to this
    function, the string ``X-CSRF-Token`` will be used to look up the token in
    ``request.headers``.

    If the value supplied by post or by header cannot be verified by the
    :class:`pyramid.interfaces.ICSRFStoragePolicy`, and ``raises`` is
    ``True``, this function will raise an
    :exc:`pyramid.exceptions.BadCSRFToken` exception. If the values differ
    and ``raises`` is ``False``, this function will return ``False``.  If the
    CSRF check is successful, this function will return ``True``
    unconditionally.

    See :ref:`auto_csrf_checking` for information about how to secure your
    application automatically against CSRF attacks.

    .. versionadded:: 1.4a2

    .. versionchanged:: 1.7a1
       A CSRF token passed in the query string of the request is no longer
       considered valid. It must be passed in either the request body or
       a header.

    .. versionchanged:: 1.9
       Moved from :mod:`pyramid.session` to :mod:`pyramid.csrf` and updated
       to use the configured :class:`pyramid.interfaces.ICSRFStoragePolicy` to
       verify the CSRF token.

    """
    supplied_token = ""
    # We first check the headers for a csrf token, as that is significantly
    # cheaper than checking the POST body
    if header is not None:
        supplied_token = request.headers.get(header, "")

    # If this is a POST/PUT/etc request, then we'll check the body to see if it
    # has a token. We explicitly use request.POST here because CSRF tokens
    # should never appear in an URL as doing so is a security issue. We also
    # explicitly check for request.POST here as we do not support sending form
    # encoded data over anything but a request.POST.
    if supplied_token == "" and token is not None:
        supplied_token = request.POST.get(token, "")

    policy = request.registry.getUtility(ICSRFStoragePolicy)
    if not policy.check_csrf_token(request, text_(supplied_token)):
        if raises:
            raise BadCSRFToken('check_csrf_token(): Invalid token')
        return False
    return True
Beispiel #3
0
    def csrf_valid(self):
        if check_csrf_token(self.request, raises=False) == False:
            log.debug('CSRF token did not match.')
            log.debug('Expected token: {}'.format(
                self.request.session.get_csrf_token()))
            log.debug('Got token: {}'.format(
                self.request.headers['x-csrf-token'] if 'x-csrf-token' in
                self.request.headers else None))

            raise BadCSRFToken()
Beispiel #4
0
def check_csrf_token(request,
                     token='csrf_token',
                     header='X-CSRF-Token',
                     raises=True):
    """ Check the CSRF token in the request's session against the value in
    ``request.POST.get(token)`` (if a POST request) or
    ``request.headers.get(header)``. If a ``token`` keyword is not supplied to
    this function, the string ``csrf_token`` will be used to look up the token
    in ``request.POST``. If a ``header`` keyword is not supplied to this
    function, the string ``X-CSRF-Token`` will be used to look up the token in
    ``request.headers``.

    If the value supplied by post or by header doesn't match the value
    supplied by ``request.session.get_csrf_token()``, and ``raises`` is
    ``True``, this function will raise an
    :exc:`pyramid.exceptions.BadCSRFToken` exception.
    If the values differ and ``raises`` is ``False``, this function will
    return ``False``.  If the CSRF check is successful, this function will
    return ``True`` unconditionally.

    Note that using this function requires that a :term:`session factory` is
    configured.

    See :ref:`auto_csrf_checking` for information about how to secure your
    application automatically against CSRF attacks.

    .. versionadded:: 1.4a2

    .. versionchanged:: 1.7a1
       A CSRF token passed in the query string of the request is no longer
       considered valid. It must be passed in either the request body or
       a header.
    """
    supplied_token = ""
    # If this is a POST/PUT/etc request, then we'll check the body to see if it
    # has a token. We explicitly use request.POST here because CSRF tokens
    # should never appear in an URL as doing so is a security issue. We also
    # explicitly check for request.POST here as we do not support sending form
    # encoded data over anything but a request.POST.
    if token is not None:
        supplied_token = request.POST.get(token, "")

    # If we were unable to locate a CSRF token in a request body, then we'll
    # check to see if there are any headers that have a value for us.
    if supplied_token == "" and header is not None:
        supplied_token = request.headers.get(header, "")

    expected_token = request.session.get_csrf_token()
    if strings_differ(bytes_(expected_token), bytes_(supplied_token)):
        if raises:
            raise BadCSRFToken('check_csrf_token(): Invalid token')
        return False
    return True
Beispiel #5
0
def login_view(request: Request):
    """Login form.

    After successful login redirects to the URL in the query or post
    parameter ``next``. By default redirects to the index page.
    """
    def set_csrf_token(value, max_age=None):
        def set_cookie(request, response):
            response.set_cookie('csrf_token',
                                value=value,
                                path=request.path,
                                secure=request.scheme == 'https',
                                httponly=True,
                                samesite='Strict',
                                max_age=max_age)

        return set_cookie

    next_url = request.params.get('next') or request.route_url('index')
    if request.user:
        return HTTPFound(location=next_url)

    username = ''
    failed = False
    if 'submit' in request.POST:
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')
        csrf_token = request.POST.get('csrf_token', '')
        expected_csrf_token = request.cookies.get('csrf_token', '')
        if not hmac.compare_digest(csrf_token, expected_csrf_token):
            raise BadCSRFToken()

        user = db.user.authenticate(request.db, username, password)
        if user:
            # Important - at the very least generate a new session id at
            # login/logout to prevent session fixation attacks.
            request.session.invalidate()
            request.user = user
            headers = remember(request, user.user_id)
            request.add_response_callback(set_csrf_token('', 0))
            return HTTPFound(location=next_url, headers=headers)

        failed = True

    csrf_token = os.urandom(16).hex()
    url = request.route_url('login')
    request.add_response_callback(set_csrf_token(csrf_token))
    return dict(username=username,
                next=next_url,
                failed=failed,
                login_url=url,
                csrf_token=csrf_token)
Beispiel #6
0
def like_post_json(context: PostResource, request):
    """ JSON `like` action.

    This action is CSRF-safe thanks to:

    - Accepting only POST requests
    - Browsers cannot submit JSON from forms
      (only ``application/x-www-form-urlencoded``)
    - CORS/SOP prevent scripts on `external pages` from posting
      unless a CORS policy allows it.
    - Requires a CSRF-token

    In addition the user must have permission to perform the action.

    Abbreviations:

    - CORS = Cross-Origin Resource Sharing
    - SOP = Same Origin Policy
    """

    request_body: dict = request.json_body

    # In case there is a CORS policy in place we also require the CSRF token
    # to be included in the JSON request.
    #
    # We can get the same effect by setting require_csrf=True which will
    # require the request to include an X-CSRF-Token header.
    #
    # Calling pyramid.csrf.check_csrf_token would also achieve the same thing
    # by checking the X-CSRF-Token header.
    csrf_token = request_body.get('csrf_token', '')
    expected_csrf_token = get_csrf_token(request)
    if not hmac.compare_digest(csrf_token, expected_csrf_token):
        raise BadCSRFToken()

    post = db.post.like_post(request.db, context.post.post_id)
    return post
Beispiel #7
0
 def validate_csrf_token(self, field):
     if field.data != field.current_token:
         raise BadCSRFToken()
    def test_response_equivalence(self):
        from pyramid.exceptions import BadCSRFToken
        from pyramid.httpexceptions import HTTPBadRequest

        self.assertTrue(isinstance(BadCSRFToken(), HTTPBadRequest))