Django logout function Denial-of-service
- Security releases issued: 1.8.4, 1.7.10, 1.4.22
- CVE: 2015-5963
- Fix: Update/1.8.4/1.7.10/1.4.22/Add @login_required()
Django 官方在八月十八号发布多个版本更新,修复几个安全问题,其中便包括一个由编码不当导致的 DoS 漏洞,测试一些网站均存在此问题。
Detail
django.contrib.auth.views.logout 视图用于开发者实现用户注销退出功能,正常情况下对于 logout 视图应使用官方提供的 django.contrib.auth.decorators.login_required 修饰器方法来判断用户是否已经登录。由于不少开发人员忽略使用修饰器进行判断,导致攻击者可以匿名访问视图,不断创建会话阻塞导致拒绝服务攻击。
django/contrib/sessions/middleware.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
try: accessed = request.session.accessed modified = request.session.modified except AttributeError: pass else: if accessed: patch_vary_headers(response, ('Cookie',)) if modified or settings.SESSION_SAVE_EVERY_REQUEST: if request.session.get_expire_at_browser_close(): max_age = None expires = None else: max_age = request.session.get_expiry_age() expires_time = time.time() + max_age expires = cookie_date(expires_time) # Save the session data and refresh the client cookie. # Skip session save for 500 responses, refs #3881. if response.status_code != 500: request.session.save() response.set_cookie(..... |
在对 settings.SESSION_SAVE_EVERY_REQUEST 的判断条件中,middleware 中间件未对 session 的状态进行判断,导致可能绕过进入判断体创建空会话。
这意味着我们能够发送大量的请求对目标网站进行会话阻塞从而达到拒绝服务攻击:
Fix
django/contrib/sessions/middleware.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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, domain=settings.SESSION_COOKIE_DOMAIN) else: if accessed: patch_vary_headers(response, ('Cookie',)) if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: |
sessions/backends/base.py & sessions/backends/cached_db.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def is_empty(self): "Returns True when there is no session_key and the session is empty" try: return not bool(self._session_key) and not self._session_cache except AttributeError: return True def flush(self): """ Removes the current session data from the database and regenerates the key. """ self.clear() self.delete() self._session_key = None |
虽然严格意义上来讲它是个 DoS 漏洞,但另一方面它完全是由开发者不严谨导致的问题(官方背锅),修复版本将 flush() 的 create() 方法修改以避免创建新的空会话,增加 is_empty() 以用来进行会话判断。
如果你实在不想升级版本,那么记得在必要的视图层增加 @login_required('/') 修饰器。