#29203: Cached Session may cause losing a session, when it failed to connect to
cache backend
-------------------------------------+-------------------------------------
     Reporter:  Kenial Sookyum Lee   |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  contrib.sessions     |                  Version:  master
     Severity:  Normal               |               Resolution:
     Keywords:  session cookie,      |             Triage Stage:
  cached session                     |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Kenial Sookyum Lee:

Old description:

> Some cache backends (AFAIK Memcached and Redis) has a feature to ignore
> connection timeout exception, in order to ensure Django application
> working even if cache has failed. This can lead a subtle bug, deleting a
> session cookie. Following steps are reproduce this bug:
>
> - Set cached session for Django session (refer to
> https://docs.djangoproject.com/en/2.0/topics/http/sessions/#using-cached-
> sessions)
> - On Django app, try to log in or set a language in order to make a
> session cookie. At this step, cache nodes should be available.
> - To set cache nodes **unavailable**, by changing to wrong node name or
> turning the nodes off.
> - Reload a page on Django app. Django app responds back with this HTTP
> header, which deletes a session cookie:
> {{{
> Set-Cookie:  sessionid=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-
> Age=0; Path=/
> }}}
>
> The problematic codes are at django/contrib/sessions/middleware.py:22 :
> {{{
>  def process_response(self, request, response):
>         """
>         If request.session was modified, or if the configuration is to
> save the
>         session every time, save the changes and set a session cookie or
> delete
>         the session cookie if the session has been emptied.
>         """
>         try:
>             accessed = request.session.accessed
>             modified = request.session.modified
>             empty = request.session.is_empty()
>         except AttributeError:
>             pass
>         else:
>             # First check if we need to delete this cookie.
>             # The session should be deleted only if the session is
> entirely empty
>             if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
>                 response.delete_cookie(
>                     settings.SESSION_COOKIE_NAME,
>                     path=settings.SESSION_COOKIE_PATH,
>                     domain=settings.SESSION_COOKIE_DOMAIN,
>                 )
> ...
> }}}
>
> I guess the initial intention was to delete a session if there is no
> values in it. However, it happens that code execution reaches at that
> code without exception, even if cache nodes are unavailable. The reason
> is, I already explained above, they just work that way even if the cache
> backend failed.
>
> I first met this bug while I was testing failover of AWS Elasticache
> (Redis). I was in testing of failover scenario, but Django application
> got me logged out repeatedly, even though session data itself is
> remaining in the cache replica. (it should, because it was doing
> failover, not reboot)
>
> IMHO, before checking ''empty'' of session data, there should be a logic
> to check cache backend is actually available. I found out
> ''request.session.cache_key'' can do that function, but it looks less
> explicit. Please show be a better way to do this, if you have one.

New description:

 Some cache backends (AFAIK Memcached and Redis) has a feature to ignore
 connection timeout exception, in order to ensure Django application
 working even if cache has failed. This can lead a subtle bug, deleting a
 session cookie. Following steps are reproduce this bug:

 - Set cached session for Django session (refer to
 https://docs.djangoproject.com/en/2.0/topics/http/sessions/#using-cached-
 sessions)
 - On Django app, try to log in or set a language in order to make a
 session cookie. At this step, cache nodes should be available.
 - To set cache nodes **unavailable**, by changing to wrong node name or
 turning the nodes off.
 - Reload a page on Django app. Django app responds back with this HTTP
 header, which deletes a session cookie:
 {{{
 Set-Cookie:  sessionid=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-
 Age=0; Path=/
 }}}

 The problematic codes are at django/contrib/sessions/middleware.py:37 :
 {{{
  def process_response(self, request, response):
         """
         If request.session was modified, or if the configuration is to
 save the
         session every time, save the changes and set a session cookie or
 delete
         the session cookie if the session has been emptied.
         """
         try:
             accessed = request.session.accessed
             modified = request.session.modified
             empty = request.session.is_empty()
         except AttributeError:
             pass
         else:
             # First check if we need to delete this cookie.
             # The session should be deleted only if the session is
 entirely empty
             if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
                 response.delete_cookie(
                     settings.SESSION_COOKIE_NAME,
                     path=settings.SESSION_COOKIE_PATH,
                     domain=settings.SESSION_COOKIE_DOMAIN,
                 )
 ...
 }}}

 I guess the initial intention was to delete a session if there is no
 values in it. However, it happens that code execution reaches at that code
 without exception, even if cache nodes are unavailable. The reason is, I
 already explained above, they just work that way even if the cache backend
 failed.

 I first met this bug while I was testing failover of AWS Elasticache
 (Redis). I was in testing of failover scenario, but Django application got
 me logged out repeatedly, even though session data itself is remaining in
 the cache replica. (it should, because it was doing failover, not reboot)

 IMHO, before checking ''empty'' of session data, there should be a logic
 to check cache backend is actually available. I found out
 ''request.session.cache_key'' can do that function, but it looks less
 explicit. Please show be a better way to do this, if you have one.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29203#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.58f7682a76aae99cd02e63d812b74993%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to