codecamp

Django web application security

先决条件: 阅读服务器端程序"网站安全"主题。 完成Django教程主题(至少包括)至少 Django教程第9部分:使用表单
目的: 理解你需要做(或不做)的主要事情来保护你的Django Web应用程序。

概述

网站安全主题概述了网站安全对于服务器端设计的意义,以及一些 的你可能需要防范的更常见的威胁。 该文章中的一个关键消息是,当Web应用程序信任来自浏览器的数据时,几乎所有攻击都成功。

重要信息:您可以了解有关网站安全性的最重要的一个教训是不要信任来自浏览器的数据 这包括网址参数, POST 数据,HTTP标头和Cookie,用户上传的文件等中的 GET 请求数据。始终检查和清理所有传入的数据。 总是假设最坏的。

Django用户的好消息是,许多更常见的威胁都由框架处理! Django中的安全性(Django docs)文章介绍了Django的安全功能以及如何 保护Django驱动的网站。

常见的威胁/保护

在本文中,我们将仅演示在我们的Django LocalLibrary 教程中的一些安全功能,而不是复制Django文档。

跨站脚本(XSS)

XSS是用于描述允许攻击者通过网站将客户端脚本注入到其他用户的浏览器中的一类攻击的术语。 这通常通过在数据库中存储恶意脚本来实现,其中可以将恶意脚本检索并显示给其他用户,或者通过让用户单击将导致攻击者的JavaScript由用户的浏览器执行的链接。

Django的模板系统通过保护您免受大多数XSS攻击 转义在HTML中为"危险"的特定字符。 我们可以通过尝试使用我们在 Django教程第9部分:使用表单中设置的创建作者表单将一些JavaScript注入我们的LocalLibrary网站来演示。

  1. Start the website using the development server (python3 manage.py runserver).
  2. Open the site in your local browser and login to your superuser account.
  3. Navigate to the author-creation page (which should be at URL: http://127.0.0.1:8000/catalog/author/create/).
  4. Enter names and date details for a new user, and then append the following text to the Last Name field:
    <script>alert('Test alert');</script>.

    注意:这是一个无害的脚本,如果执行,会在浏览器中显示一个警告框。 如果在提交记录时显示警报,则该站点容易受到XSS威胁。

  5. Press Submit to save the record.
  6. When you save the author it will be displayed as shown below. Because of the XSS protections the alert() should not be run. Instead the script is displayed as plain text.

如果你查看页面的HTML源代码,你可以看到脚本标签的危险字符已经变成了它们无害的转义码等价物(例如> 现在& gt; / code>)

<h1>Author: Boon&lt;script&gt;alert(&#39;Test alert&#39;);&lt;/script&gt;, David (Boonie) </h1>

使用Django模板保护您免受大多数XSS攻击。 但是,可以关闭此保护,并且保护不会自动应用于通常不会由用户输入填充的所有标记(例如,表单字段中的 help_text 通常 不是用户提供的,所以Django不会转义那些值)。

XSS攻击也可能来自其他不受信任的数据源,例如Cookie,Web服务或上传的文件(只要数据在包含在页面中之前未被充分清理)。 如果您要显示来自这些来源的数据,那么您可能需要添加自己的验证码。

跨站点请求伪造(CSRF)保护

CSRF攻击允许恶意用户在没有该用户的知识或同意的情况下使用另一用户的凭证来执行动作。 例如,假设我们有一个黑客想为我们的LocalLibrary创建其他作者。

注意:显然,我们的黑客不是为了这笔钱! 一个更有野心的黑客可以在其他网站上使用相同的方法来执行更加有害的任务(例如将钱转到自己的帐户等)

为了做到这一点,他们可能创建一个HTML文件,如下所示,它包含一个作者创建表单(就像我们在上一节中使用的),一旦文件加载提交。 然后他们将文件发送给所有的图书馆员,并建议他们打开文件(它包含一些无害的信息,诚实!)。 如果文件由任何已登录的库管理器打开,则将使用其凭据提交表单,并创建新作者。

<html>
<body onload='document.EvilForm.submit()'>

<form action="http://127.0.0.1:8000/catalog/author/create/" method="post" name='EvilForm'>
  <table>
    <tr><th><label for="id_first_name">First name:</label></th><td><input id="id_first_name" maxlength="100" name="first_name" type="text" value="Mad" required /></td></tr>
    <tr><th><label for="id_last_name">Last name:</label></th><td><input id="id_last_name" maxlength="100" name="last_name" type="text" value="Man" required /></td></tr>
    <tr><th><label for="id_date_of_birth">Date of birth:</label></th><td><input id="id_date_of_birth" name="date_of_birth" type="text" /></td></tr>
    <tr><th><label for="id_date_of_death">Died:</label></th><td><input id="id_date_of_death" name="date_of_death" type="text" value="12/10/2016" /></td></tr>
  </table>
  <input type="submit" value="Submit" />
</form>

</body>
</html>

运行开发Web服务器,并使用超级用户帐户登录。 将上面的文本复制到一个文件,然后在浏览器中打开它。 你应该得到一个CSRF错误,因为Django有这种事情的保护!

启用保护的方式是在表单定义中包含 {%csrf_token%} 模板标记。 然后,此标记将显示在您的HTML中,如下所示,其中的值特定于当前浏览器上的用户。

<input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' />

Django生成用户/浏览器特定的键,并将拒绝不包含该字段或包含用户/浏览器的不正确字段值的表单。

为了使用这种类型的攻击,黑客现在必须发现并包括用于特定目标用户的CSRF密钥。 他们也不能使用"scattergun"方法向所有库管理员发送恶意文件,并希望其中一个将打开它,因为CSRF密钥是特定于浏览器的。

默认情况下,Django的CSRF保护功能处于打开状态。 您应该始终使用表单中的 {%csrf_token%} 模板标记,并对可能更改或向数据库添加数据的请求使用 POST

其他保护

Django还提供其他形式的保护(大多数是困难的或不特别有用的演示):

SQL injection protection
SQL injection vulnerabilities enable malicious users to execute arbitrary SQL code on a database, allowing data to be accessed, modified, or deleted irrespective of the user's permissions. In almost every case you'll be accessing the database using Django’s querysets/models, so the resulting SQL will be properly escaped by the underlying database driver. If you do need to write raw queries or custom SQL then you'll need to explicitly think about preventing SQL injection.
Clickjacking protection
In this attack a malicious user hijacks clicks meant for a visible top level site and routes them to a hidden page beneath. This technique might be used, for example, to display a legitimate bank site but capture the login credentials in an invisible <iframe> controlled by the attacker. Django contains clickjacking protection in the form of the X-Frame-Options middleware which, in a supporting browser, can prevent a site from being rendered inside a frame.
Enforcing SSL/HTTPS
SSL/HTTPS can be enabled on the web server in order to encrypt all traffic between the site and browser, including authentication credentials that would otherwise be sent in plain text (enabling HTTPS is highly recommended). If HTTPS is enabled then Django provides a number of other protections you can use:
Host header validation
Use ALLOWED_HOSTS to only accept requests from trusted hosts.

还有许多其他保护,并注意到使用上述机制。 虽然我们希望这给了你Django提供的概述,你应该仍然阅读Django安全文档。

    概要

    Django有针对一些常见威胁的有效保护,包括XSS和CSRF攻击。 在本文中,我们已经在我们的 LocalLibrary 网站中演示了Django如何处理这些特定威胁。 我们还提供了一些其他保护的简要概述。

    这是一个非常简短的web安全。 我们强烈建议您阅读 Django中的安全性,以获得更深入的了解。

    本单元中关于Django的下一步和最后一步是完成评估任务

    也可以看看

    Django Tutorial Part 10: Testing a Django web application
    Django Tutorial Part 3: Using models
    温馨提示
    下载编程狮App,免费阅读超1000+编程语言教程
    取消
    确定
    目录
    CSS

    关闭

    MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }