Django 1.6.3 版本发行说明

2014 年 4 月 21 日

Django 1.6.3 修复了 1.6.2 版本中的一些错误,包括三个安全问题,并进行了一个不兼容的变更:

使用 reverse() 时出现意外的代码执行

Django 的 URL 处理基于正则表达式模式(表示 URL)到可调用视图的映射,而 Django 自身的处理包括将请求的 URL 与这些模式进行匹配,以确定要调用的适当视图。

Django 还提供了一个方便的函数 — reverse() — 用于执行与之相反的过程。reverse() 函数接收有关视图的信息,并返回调用该视图的 URL。鼓励应用程序开发人员使用 reverse(),因为``reverse()`` 的输出总是基于当前的 URL 模式,这意味着开发人员在更改 URL 时不需要修改其他代码。

reverse() 的一个参数签名是传递所需视图的点分 Python 路径。在这种情况下,Django 将导入由该点分路径指示的模块,作为生成结果 URL 的一部分。如果这样的模块在导入时具有副作用,那么这些副作用将会发生。

因此,在满足以下条件时,攻击者可能导致意外的代码执行:

  1. 存在一个或多个视图,它们根据用户输入构建 URL(通常是查询字符串中的“next”参数,指示在操作成功完成后重定向到哪里)。
  2. 攻击者知道服务器的 Python 导入路径上存在一个或多个模块,在导入时执行具有副作用的代码。

为了解决这个问题,reverse() 现在将只接受并导入基于项目的 URL 模式配置 中列出的包含视图的模块的点分路径,以确保只有开发人员打算以这种方式导入的模块才能被导入。

缓存匿名页面可能会泄露 CSRF 令牌

Django 包括了一个 缓存框架 和一个用于 防止跨站点请求伪造 (CSRF) 攻击 的系统。CSRF 保护系统基于一个随机的 nonce,它会在 cookie 中发送给客户端,在将来的请求中客户端必须发送该 nonce,同时在表单中还有一个隐藏值,必须与表单一起提交。

缓存框架包括一个选项,用于缓存对匿名(即未经认证的)客户端的响应。

当第一个对指定页面的匿名请求来自没有 CSRF cookie 的客户端时,缓存框架还会缓存 CSRF cookie,并为没有 CSRF cookie 的其他匿名客户端提供相同的随机数。这可能使攻击者获得有效的 CSRF cookie 值,并执行绕过 cookie 检查的攻击。

为了解决这个问题,缓存框架将不再缓存此类响应。启发式方法将是:

  1. 如果传入的请求没有提交任何 cookie,且
  2. 如果响应发送了一个或多个 cookie,且
  3. 如果在响应中设置了 Vary: Cookie 头,那么该响应将不会被缓存。

MySQL 类型转换

众所周知,MySQL 数据库会对某些查询进行“类型转换”;例如,在查询包含字符串值的表时,但使用基于整数值进行过滤的查询,MySQL 首先会默默地将字符串强制转换为整数,然后返回基于该整数的结果。

如果在未先将值转换为适当类型的情况下执行查询,这可能产生意外的结果,类似于查询本身被操作时会发生的情况。

Django 的模型字段类知道它们自己的类型,大多数此类字段在查询之前会将查询参数明确转换为正确的数据库级别类型。然而,有三个模型字段类没有正确地转换它们的参数:

在查询之前,这三个字段已经更新为将参数转换为正确的类型。

此外,自定义模型字段的开发人员现在通过文档受到警告,需要确保他们的自定义字段类能够执行适当的类型转换,以及使用 raw()extra() 查询方法的用户将被建议确保在执行查询之前进行适当的手动类型转换,因为这些方法允许开发人员提供原始 SQL 或 SQL 片段。

select_for_update() 需要在事务中使用

在历史上,使用 select_for_update() 的查询可以在自动提交模式下,在事务之外执行。在 Django 1.6 之前,Django 的自动事务模式允许使用这种方式锁定记录,直到下一次写操作。Django 1.6 引入了数据库级别的自动提交;自那时起,在这种上下文中执行将取消 select_for_update() 的效果。因此,现在假定这是一个错误并引发异常。

这个更改是因为这样的错误可能是因为在不需要全局事务(例如,ATOMIC_REQUESTS 设置为 True)或 Django 的旧的自动提交行为的项目中包含一个期望全局事务的应用程序引起的;而且,这样的错误可能会表现为数据损坏的错误。

如果您在测试类中使用 select_for_update() 并且该测试类是 TransactionTestCase 的子类而不是 TestCase,则此更改可能导致测试失败。

其他错误修复和更改

  • 从 GeoIP 库检索的内容现在已经正确解码为其默认的 iso-8859-1 编码(#21996)。
  • 在使用 bulk_create()ForeignObject 时,修复了 AttributeError 错误(#21566)。
  • 修复了在查询编译多次时,使用 F() + timedelta()QuerySet 引发崩溃的问题(#21643)。
  • 防止了 IntegerField 子类的自定义 widget 类属性被其 __init__ 方法中的代码覆盖的问题(#22245)。
  • 提高了 strip_tags() 的准确性(但如文档中所述,它仍然不能保证产生 HTML 安全的结果)。
  • 修复了 django.contrib.gis 中 SQL 编译器的回归问题,针对非具体字段(#22250)。
  • 在带有 URL 前缀的站点中运行时修复了 ModelAdmin.preserve_filters#21795)。
  • 修复了在没有设置 PATH 环境变量时管理实用程序中的 find_command 引发的崩溃(#22256)。
  • 在 Windows 上修复了 changepassword#22364)。
  • 避免在 MySQL 上遮蔽死锁异常(#22291)。
  • _set_autocommit 中包装数据库异常(#22321)。
  • 修复了在关闭数据库连接或数据库服务器断开连接时的原子性问题(#21239#21202)。
  • 修复了 prefetch_related 中的回归问题,导致相关对象查询包括不必要的连接(#21760)。

此外,Django 内部的 six 版本,即 django.utils.six,已升级到最新版本(1.6.1)。