1

I have a pyramid application using pyramid_beaker. Here is my configuration:

# Options For Sessions and Caching:
session.type = file
session.data_dir = %(here)s/../../data/sessions/data
session.lock_dir = %(here)s/../../data/sessions/lock
# Session Options:
session.key = session_id
session.secure = false
session.timeout = 3600
session.cookie_expires = true
session.cookie_domain = .mydomain.local
session.httponly = true
# Encryption Options:
session.encrypt_key = c]?wvL",ni3J.)d8(e~z8b-9Le=Anh'.QMytBj^Kukfi<79C$Cg22)cX;__xs6?S
session.validate_key = \2R('?pL]\Z_8?(o`.?.?^.RF6t*5pCh6PH`~aon%H`PX$;E}"((mu-@(?G<=!:+
# pyramid_beaker specific option
session.cookie_on_exception = true

And here is the login form view:

def login(self):

    message_html = _('view.login.welcome-message', default='Please log in.')
    login_url = self.request.route_url('login')
    login = ''
    password = ''
    referrer = self.request.url
    if referrer == login_url:
        referrer = self.request.route_url('home')
    came_from = self.request.POST.get('came_from', referrer)
    csrf_token = self.request.session.get_csrf_token()

    if 'form.submitted' in self.request.POST:
        login = self.request.POST.get('login')
        password = self.request.POST.get('password')
        if csrf_token == self.request.POST.get('csrf_token'):
            if login in USERS:
                manager = BCRYPTPasswordManager()
                if manager.check(USERS[login], password):
                    headers = remember(self.request, login)
                    return HTTPFound(location=came_from, headers=headers)

        message_html = _('view.login.failed-login-message', default='Login failed!')

    return {
        'message_html': message_html,
        'url': login_url,
        'login': login,
        'password': password,
        'came_from': came_from,
        'csrf_token': csrf_token,
    }

Now, when a user wants to log in, the view renders a form and a cookie session_id is generated. When the user submit a valid form then the cookie’s value is accepted to authenticate the user.

Nothing prevent a user to change the cookie’s value before submitting the form. This behavior is apparently a security flaw according to this question.

So, how to use pyramid_beaker in order for the server to generate a new session_id value when login succeed instead of taking the one from the cookie?

Community
  • 1
  • 1

1 Answers1

1

Pyramid's session API provides a way to invalidate sessions when escalating privileges. You can do this via the request.session.invalidate() in your login view. pyramid_beaker itself supports request.session.regenerate_id() to maintain the session data, but this is not part of the Pyramid session API and will only work if you are using pyramid_beaker. If you are worried about a user guessing someone else's session id, you are protected here because the id that is stored within the cookie is signed if you specify beaker's session.secret option.

Michael Merickel
  • 23,153
  • 3
  • 54
  • 70
  • How do you check if the signed cookie has been changed by the client? – Raj May 01 '13 at 00:25
  • If the client changes the cookie, it will not be a valid session and just won't show up. – Michael Merickel May 01 '13 at 05:08
  • Adding `self.request.session.invalidate()` just before `headers = remember(self.request, login)` solved my problem. However, the cookie does not seem to be signed because I could easily forge a cookie with value 123. My first logging attempt is rejected because the CSRF test fails but the 2nd attempt is successful and "123" becomes a valid value. –  May 01 '13 at 15:20
  • 1
    If the cookie value isn't signed then you aren't setting `session.secret` in your ini settings. – Michael Merickel May 01 '13 at 19:58
  • Ok, this is true. And this [These options should then be used instead of the secret option listed above.](http://beaker.readthedocs.org/en/latest/configuration.html#encryption-options) is why I did not use secret. Just to be sure I made a test by taking my original login view (without the `self.request.session.invalidate()`) and by setting `secret` and...I am not able anymore to forge a cookie with 123 as the value and so my problem is solved without adding `self.request.session.invalidate()`. I am going to find out about the beaker documentation weirdness and come back. –  May 02 '13 at 06:44