提升Django项目安全性:生产环境加固指南

简介

开发Django应用可以是一种方便的体验,因为它被设计得灵活且可扩展。这个前提也适用于Django的安全设置,这些设置可以帮助您为项目做好生产准备。但是,还有几种方法可以进一步保护您的项目。

拆分您的设置可以根据不同环境设置不同的配置。利用.env文件来设置环境变量或隐藏机密设置将确保您不会发布可能危及项目的细节。同时,改变默认的URL和其他设置可以帮助您避免常见的安全漏洞。

尽管一开始实施这些策略可能会耗费时间,但制定一个实用的工作流程将使您能够在不影响安全性或效率的情况下部署项目的发布版本。

在本教程中,您将通过实施和配置基于环境的设置、.env文件和Django内置的安全设置,为您的Django项目使用一个注重安全性的工作流。这些功能互相补充,将使您的Django项目准备好应对您可能采取的不同部署方式。

先决条件

在开始阅读本指南之前,您需要以下物品:

注意:如果您使用的是一个现有的Django项目,您可能有不同的要求。本教程提供了一个特定的项目结构建议;然而,您也可以根据需要单独使用本教程的每个部分。

第一步——重构Django的设置

在搞清楚如何保护您的Django项目之前,您需要进入项目的目录并激活虚拟环境。

  1. cd django-apps
  2. . env/bin/activate

在这一步中,您将首先将settings.py文件重新排列为特定环境的配置。当您需要在不同的环境之间移动项目时,比如开发环境和生产环境,这是一个很好的做法。这种安排意味着在不同的环境中需要重新配置的内容更少;相反,您将使用环境变量来在不同的配置之间进行切换,这将在本教程中进行讨论。

在您的项目子目录中创建一个名为settings的新目录。

  1. mkdir testsite/testsite/settings

根据先决条件,本教程使用testsite,但您可以在这里替换为您的项目名称。

这个目录将会替换您当前的settings.py配置文件;所有基于环境的设置将会放在该文件夹中的单独文件中。

在您的新设置文件夹中,创建三个Python文件。

  1. cd testsite/testsite/settings
  2. touch base.py development.py production.py

development.py文件将包含通常在开发过程中使用的设置。而production.py文件将包含用于生产服务器的设置。您应该将这两者分开保存,因为生产配置将使用无法在开发环境下正常工作的设置,例如强制使用HTTPS、添加头部信息以及使用生产数据库。

base.py的设置文件将包含development.pyproduction.py将继承的设置。这是为了减少冗余和帮助保持代码的整洁。这些Python文件将替换settings.py,因此您现在需要删除settings.py以避免让Django感到困惑。

当您还在设置目录中时,使用以下命令将settings.py重命名为base.py

  1. mv ../settings.py base.py

您刚刚完成了新的基于环境的设置目录的大纲。您的项目还不能理解您的新配置,所以下一步,您将解决这个问题。

步骤2 – 使用django-environ

目前,Django无法识别您的新设置目录或其内部文件。因此,在继续使用基于环境的设置之前,您需要使用django-environ使Django正常工作。这是一个依赖项,它可以从.env文件中加载环境变量。这意味着Django将在您项目的根目录中查找一个.env文件,以确定要使用哪个设置配置。

进入您项目的根目录,然后使用ls命令列出目录的内容。

  1. cd ../../
  2. ls

您项目根目录中的文件应该如下所示:

输出
db.sqlite3 manage.py testsite

安装django-environ

  1. pip install django-environ

现在需要配置Django来使用.env文件。您需要编辑两个文件:manage.py用于开发环境,wsgi.py用于生产环境。

首先,使用Nano或您首选的文本编辑器打开manage.py文件进行编辑。

  1. nano manage.py

添加下面突出显示的代码:

testsite/manage.py

这是文章《如何增强您的生产Django项目的安全性》的第2部分(共7部分)。

内容片段:

import os
import sys
import environ

environ.Env.read_env()

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings')

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

按下CTRL+X,按下Y键保存,并按下回车键,保存并关闭manage.py文件。

接下来打开wsgi.py以进行编辑。

  1. nano testsite/wsgi.py

 

请添加下面高亮的行。

测试站点/测试站点/wsgi.py

import os
import environ

environ.Env.read_env()

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings')

application = get_wsgi_application()

通过按下CTRL+X快捷键,再按下Y键保存,最后按下回车键来保存并关闭文件。

你在这两个文件中添加的代码有两个功能。首先,每当Django运行时——用于开发的manage.py,用于生产的wsgi.py——你告诉它去寻找你的.env文件。如果文件存在,你就指示Django使用.env推荐的设置文件;否则,默认使用开发配置。

最后,您将在当前目录中创建一个.env文件。

  1. nano .env

 

现在加入以下一行代码来将环境设置为开发模式:

测试网站/.env
DJANGO_SETTINGS_MODULE="testsite.settings.development"

通过按下CTRL+X,按下Y键保存,然后按下回车键以保存并关闭文件。

注意:将.env文件添加到.gitignore文件中,以便它永远不会被提交;您将使用此文件来包含诸如密码和API密钥等在公开环境中不可见的数据。您的项目运行的每个环境都将有自己的.env文件,其中包含该特定环境的配置设置。

建议在您的项目中创建一个.env.example文件,以便您可以轻松地在需要时创建一个新的.env文件。

这是文章《如何增强您的生产Django项目的安全性》的第3部分(共7部分)。

默认情况下,Django会使用testsite.settings.development。但是,如果您将DJANGO_SETTINGS_MODULE环境变量更改为testsite.settings.production,它将开始使用您的生产配置。接下来,您需要填充development.pyproduction.py的设置配置。

第三步——创建开发和生产环境设置

接下来,您将打开base.py文件,并在独立的development.pyproduction.py文件中添加您要修改的配置信息。production.py文件将需要使用您的生产数据库凭据,请确保您拥有这些凭据。

注意:根据环境,您需要确定需要配置哪些设置。本教程仅涵盖一个常见示例,用于生产和开发环境的设置(即安全设置和分离的数据库配置)。在本教程中,您将使用先决条件教程中的Django项目作为示例项目。您将把设置从base.py移动到development.py。首先打开development.py

  1. nano testsite/settings/development.py

然后添加以下代码:

testsite/testsite/settings/development.py

import os
from .base import *

DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

通过按下CTRL+X,然后按下Y键保存,最后按下ENTER键来保存并关闭文件。

首先,您将从base.py文件中导入设置,development.py将继承base.py的设置。然后,您将转移要修改的开发环境设置。在这种情况下,与开发相关的设置如下:DEBUG,在开发环境中需要为True,但在生产环境中不需要;以及DATABASES,使用本地数据库而不是生产数据库。您在这里使用的是SQLite数据库作为开发数据库。

注意:出于安全考虑,Django的调试输出永远不会显示包含APIKEYPASSSECRETSIGNATURETOKEN等字符串的任何设置。这样做是为了确保如果您意外地在DEBUG仍然启用的情况下将项目部署到生产环境中,秘密信息不会被泄露。言归正传,永远不要在DEBUG启用的情况下公开部署项目。它只会使您的项目安全受到威胁。

接下来,您将在production.py中添加内容。使用以下命令打开文件:

  1. nano testsite/settings/production.py

然后添加以下代码。生成的内容将类似于development.py,但具有不同的数据库配置和DEBUG设置为False

testsite/testsite/settings/production.py

第四步 — 配置Django安全设置

Django内置了多项安全设置,旨在增强生产环境项目的安全性。本步骤将指导您如何将这些关键安全设置添加到您的项目中,并将其限定在production.py配置文件中。这些设置不建议在开发环境中使用。

这些安全设置主要强制使用HTTPS进行各种网络功能,例如会话cookie、CSRF cookie、HTTP升级到HTTPS等。因此,如果您的服务器尚未配置域名并启用HTTPS,请先暂停本部分操作。如需了解服务器部署准备工作,请参阅文末推荐的文章。

编辑production.py文件

首先,打开您的production.py文件:

nano testsite/settings/production.py

添加生产环境配置

在打开的文件中,根据以下代码示例和解释,添加适用于您项目的突出显示设置。请注意,testsite是您的项目名称,因此您的设置文件路径应为testsite/testsite/settings/production.py

import os
from .base import *
import environ

env = environ.Env()
environ.Env.read_env()

DEBUG = False

ALLOWED_HOSTS = []

DATABASES = {
    'default': {
        'ENGINE': env('SQL_ENGINE', default='django.db.backends.sqlite3'),
        'NAME': env('SQL_DATABASE', default=os.path.join(BASE_DIR, 'db.sqlite3')),
        'USER': env('SQL_USER', default='user'),
        'PASSWORD': env('SQL_PASSWORD', default='password'),
        'HOST': env('SQL_HOST', default='localhost'),
        'PORT': env('SQL_PORT', default=''),
    }
}

对于上述数据库配置示例,您可以使用.env文件来配置每个凭据,并设置默认值。请注意,如果您已经为生产环境的Django项目设置了数据库,请务必使用您自己的数据库配置,而非示例中提供的配置。

完成配置后,按下CTRL+X,然后按Y保存,最后按回车键确认并关闭文件。

现在,您的项目已配置为根据.env文件中的DJANGO_SETTINGS_MODULE变量使用不同的设置。根据您使用的示例设置,当项目切换到生产环境时,DEBUG将变为FalseALLOWED_HOSTS将被定义,并且项目将开始使用您已在服务器上配置好的生产数据库。

这是文章《如何增强您的生产Django项目的安全性》的第5部分(共7部分)。

import os
from .base import *
import environ

env = environ.Env()
environ.Env.read_env()

DEBUG = False

ALLOWED_HOSTS = ['your_domain', 'www.your_domain']

DATABASES = {
    'default': {
        'ENGINE': env('SQL_ENGINE', default='django.db.backends.sqlite3'),
        'NAME': env('SQL_DATABASE', default=os.path.join(BASE_DIR, 'db.sqlite3')),
        'USER': env('SQL_USER', default='user'),
        'PASSWORD': env('SQL_PASSWORD', default='password'),
        'HOST': env('SQL_HOST', default='localhost'),
        'PORT': env('SQL_PORT', default=''),
    }
}

SECURE_SSL_REDIRECT = True

SESSION_COOKIE_SECURE = True

CSRF_COOKIE_SECURE = True

SECURE_BROWSER_XSS_FILTER = True
  • ALLOWED_HOSTS 是一个字符串列表,表示您的项目可以提供服务的主机/域名。这是一项安全措施,旨在防止攻击者进行缓存投毒和DNS欺骗。您可以在Django文档中找到有关 ALLOWED_HOSTS 的更多详细信息。
  • SECURE_SSL_REDIRECT 将所有HTTP请求重定向到HTTPS(除非被豁免)。这意味着您的项目将始终尝试使用加密连接。为此,您需要在服务器上配置SSL。请注意,如果您的Nginx或Apache已经配置了此功能,则此设置将是多余的。
  • SESSION_COOKIE_SECURE 告诉浏览器,Cookie只能通过HTTPS处理。这意味着您的项目为登录等活动生成的Cookie只能通过加密连接工作。
  • CSRF_COOKIE_SECURESESSION_COOKIE_SECURE 相同,但适用于您的CSRF令牌。CSRF令牌可防止跨站请求伪造。Django的CSRF保护通过确保提交给您项目的任何表单(用于登录、注册等)都是由您的项目而不是第三方创建的来实现这一点。
  • SECURE_BROWSER_XSS_FILTER 会在所有尚未设置 X-XSS-Protection: 1; mode=block 头的响应中添加此头。这确保了第三方无法向您的项目注入脚本。例如,如果用户使用公共字段将脚本存储在您的数据库中,当该脚本被检索并显示给其他用户时,它将不会运行。

按下 CTRL+X,按下 Y 保存,然后按下 ENTER 键来保存并关闭文件。

如果您想了解更多关于Django中各种安全设置的信息,请查阅他们的文档。

警告

警告:Django 文档指出,您不应完全依赖 SECURE_BROWSER_XSS_FILTER。请务必记住验证和清理输入数据。

其他设置

以下设置旨在支持 HTTP 严格传输安全(HSTS)——这意味着您的整个网站必须始终使用 SSL。

  • SECURE_HSTS_SECONDS:HSTS 的设置时长(秒)。如果您将其设置为一小时(以秒为单位),每次访问您网站上的网页时,它都会告诉您的浏览器,在接下来的一个小时内,HTTPS 是访问该网站的唯一方式。如果在此期间您访问了网站的不安全部分,浏览器将显示错误,并且不安全的页面将无法访问。
  • SECURE_HSTS_PRELOAD:仅当 SECURE_HSTS_SECONDS 设置后才生效。此头部指令指示浏览器预加载您的网站。这意味着您的网站将被添加到流行浏览器(如 Firefox 和 Chrome)中硬编码的列表中。这要求您的网站始终加密。使用此头部时务必小心。如果您在任何时候决定不为项目使用加密,可能需要数周时间才能手动从 HSTS 预加载列表中移除。
  • SECURE_HSTS_INCLUDE_SUBDOMAINS:将 HSTS 头部应用于所有子域名。启用此头部意味着您的域名(your_domain)和不安全的子域名(unsecure.your_domain)都将需要加密,即使 unsecure.your_domain 与此 Django 项目无关。

警告:错误配置这些额外设置可能导致您的网站长时间停止运行。

在实施这些设置之前,请阅读 Django 文档中有关 HSTS 的说明。需要考虑这些设置如何与您自己的 Django 项目配合使用;总的来说,这里讨论的配置是大多数 Django 项目的良好基础。接下来,您将进一步审查一些 .env 的使用方法。

第五步 — 使用 django-environ 加密密钥

本教程的最后一部分将帮助您运用 django-environ。这将使您能够隐藏某些信息,例如您的项目的 SECRET_KEY 或管理员的登录 URL。如果您计划在 GitHub 或 GitLab 等平台上发布代码,这是一个很好的主意,因为这些秘密不会被公开。相反,当您在本地环境或服务器上开始设置项目时,您可以创建一个新的 .env 文件并定义这些秘密变量。

在这个部分,您必须隐藏您的 SECRET_KEY,这样您才能开始工作。

在项目的根目录下打开您的 .env 文件。

  1. nano .env

并添加以下行,确保用您自己的机密字符串替代 “your_secret_key”。

测试网站/.env

DJANGO_SETTINGS_MODULE="testsite.settings.development"
SECRET_KEY="your_secret_key"

然后按下 CTRL+X 保存并关闭文件,按 Y 保存,然后按 ENTER

接下来,打开 base.py 文件。

  1. nano testsite/settings/base.py

像这样更新 SECRET_KEY 变量:

测试网站 / 测试网站 / 设置 / 基本.py

. . .
<^>import environ

env = environ.Env()
environ.Env.read_env()<^>

SECRET_KEY = env('SECRET_KEY')
. . .

注意:SECRET_KEY 不应该被实际的密钥替换。SECRET_KEY 变量应保持不变,并将实际的密钥添加到 .env 文件中。然后按下 CTRL+X,按 Y 保存并关闭文件,然后按 ENTER 键。您的项目现在将使用位于 .env 文件中的 SECRET_KEY

最后,您将通过给管理员 URL 添加一长串随机字符来隐藏它。因此,您将无法访问 your_domain/admin,而是需要访问 your_domain/very_secret_url/admin。这会确保机器人和陌生人都很难找到您的管理员 URL,因此他们会更难尝试强行破解您的管理员登录。

再次打开 .env 文件:

  1. nano .env

并添加一个 SECRET_ADMIN_URL 变量。

测试站点/.env

DJANGO_SETTINGS_MODULE="testsite.settings.development"
SECRET_KEY="your_secret_key"
SECRET_ADMIN_URL="very_secret_url"

通过按下 CTRL+X 键,按下 Y 键保存,然后按下 ENTER 键,保存并关闭文件。

现在您要告诉 Django 使用 SECRET_ADMIN_URL 来隐藏您的管理后台 URL。

  1. nano testsite/urls.py

注意:不要忘记用自己的秘密 URL 替换掉 very_secret_url

如果您想要为这个变量使用随机字符串,Python 提供了一个很棒的 secrets.py 库来生成这样的字符串。他们给出的例子是创建小型 Python 程序生成安全随机字符串的很好的方式。

隐藏Django管理员URL

为了增强安全性,您可以修改管理员URL,使其不易被猜测。请按照以下步骤操作:

测试站点/测试站点/urls.py文件中进行如下编辑:

from django.contrib import admin
from django.urls import path

import environ

env = environ.Env()
environ.Env.read_env()

urlpatterns = [
    path(env('SECRET_ADMIN_URL') + '/admin/', admin.site.urls),
]

保存并关闭文件(通常通过按下CTRL+X,然后按下Y键,最后按下回车键)。

现在,您的管理员登录页面将位于/very_secret_url/admin/,而不是默认的/admin/路径,这有助于防止暴力破解攻击。

管理员登录页面

结论

在本教程中,您已经配置了当前的Django项目,使其能够轻松适应不同的环境。您的项目现在利用django-environ来处理敏感信息和设置。您的生产环境设置也已启用Django内置的安全功能。

如果您已启用所有建议的安全组件并按照指示重新实施了设置,那么您的项目将具备以下关键功能:

  • 所有通信(例如,子域名、Cookie、CSRF)均采用SSL/HTTPS加密。
  • 有效预防XSS(跨站脚本)攻击。
  • 有效预防CSRF(跨站请求伪造)攻击。
  • 项目密钥已隐藏。
  • 管理员登录URL已隐藏,有效防止暴力破解攻击。
  • 开发和生产环境拥有独立的设置。

如果您对学习更多关于Django感兴趣,可以查看我们关于Django开发的教程系列。

此外,如果您尚未将项目投入实际生产环境,请参考以下教程:在Ubuntu 20.04上使用Postgres、Nginx和Gunicorn配置Django。您也可以访问我们的Django主题页面以获取更多教程。

当然,请详细阅读Django的设置文档以获取更多信息。

bannerAds