如何加强生产 Django 项目的安全性

作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。

介绍

开发Django应用程序可以是一种快速而干净的体验,因为它的方法灵活且可扩展。Django 还提供了各种面向安全的设置,可以帮助您无缝地为生产准备项目。但是,在生产部署方面,有多种方法可以进一步保护您的项目。通过分解设置来重构项目将使您能够根据环境轻松设置不同的配置。利用dotenv隐藏环境变量或机密设置将确保您不会发布任何可能危及项目的详细信息。

虽然实施这些不同的策略和功能一开始可能看起来很耗时,但开发实用的工作流程将使您能够在不影响安全性或生产力的情况下部署项目的版本。

在本教程中,您将通过实施和配置基于环境的设置dotenv、以及 Django 的内置安全设置,为 Django 开发利用面向安全的工作流这些功能都相互补充,并将生成一个 Django 项目版本,该版本已准备好用于您可能采用的不同部署方法。

先决条件

在开始本指南之前,您需要具备以下条件:

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

步骤 1 — 重构 Django 的设置

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

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

  • mkdir testsite/testsite/settings

(根据我们使用的先决条件testsite,但您可以在此处替换您的项目名称。)

该目录将替换您当前的settings.py配置文件;所有基于环境的设置都将位于此文件夹中包含的单独文件中。

在新settings文件夹中,创建三个 Python 文件:

  • cd testsite/testsite/settings
  • touch base.py development.py production.py

development.py文件将包含您通常在开发过程中使用的设置。并且production.py将包含在生产服务器上使用的设置。您应该将它们分开,因为生产配置将使用在开发环境中不起作用的设置;例如,强制使用 HTTPS、添加标头和使用生产数据库。

base.py设置文件将包含设置,development.pyproduction.py从继承。这是为了减少冗余并帮助保持您的代码更简洁。这些 Python 文件将替换settings.py,因此您现在将删除settings.py以避免混淆 Django。

仍在您的settings目录中时,使用以下命令重命名settings.pybase.py

  • mv ../settings.py base.py

您刚刚完成了新的基于环境的设置目录的大纲。您的项目尚无法理解您的新配置,因此接下来,您将解决此问题。

第 2 步 – 使用 python-dotenv

目前 Django 不会识别您的新设置目录或其内部文件。因此,在继续使用基于环境的设置之前,您需要让 Django 使用python-dotenv. 这是从.env文件加载环境变量的依赖项这意味着 Django 将查看.env项目根目录中文件,以确定它将使用哪些设置配置。

转到项目的根目录:

  • cd ../../

安装python-dotenv

  • pip install python-dotenv

现在您需要配置 Django 以使用dotenv. 为此manage.py您将编辑两个文件:, 用于开发,以及wsgi.py,用于生产。

让我们从编辑开始manage.py

  • nano manage.py

添加以下代码:

测试站点/管理.py
import os
import sys
import dotenv

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

    if os.getenv('DJANGO_SETTINGS_MODULE'):
    os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')

    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()

dotenv.load_dotenv(
    os.path.join(os.path.dirname(__file__), '.env')
)

保存并关闭manage.py,然后打开wsgi.py进行编辑:

  • nano testsite/wsgi.py

添加以下突出显示的行:

testsite/testsite/wsgi.py

import os
import dotenv

from django.core.wsgi import get_wsgi_application

dotenv.load_dotenv(
    os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
)

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

if os.getenv('DJANGO_SETTINGS_MODULE'):
 os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')

application = get_wsgi_application()

您添加到这两个文件中的代码做了两件事。首先,每当 Djangomanage.py运行时——用于运行开发,wsgi.py用于生产——你告诉它寻找你的.env文件。如果该文件存在,则指示Django使用.env推荐的设置文件,否则默认使用开发配置。

保存并关闭文件。

最后,让我们.env在项目的根目录中创建一个

  • nano .env

现在添加以下行以将环境设置为开发:

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

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

建议创建一个.env.example以包含在您的项目中,以便您可以.env在需要的任何地方轻松创建一个新的

因此,在默认情况下Django会使用testsite.settings.development,但如果你改变DJANGO_SETTINGS_MODULEtestsite.settings.production例如,它会开始使用你的产品配置。接下来,您将填充您的development.pyproduction.py设置配置。

第 3 步 – 创建开发和生产设置

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

注意:您可以根据环境确定需要配置哪些设置。本教程将仅涵盖生产和开发设置(即安全设置和单独的数据库配置)的常见示例。

在本教程中,我们使用先决条件教程中的 Django 项目作为示例项目。我们将设置从 移动base.pydevelopment.py首先打开development.py

  • nano testsite/settings/development.py

首先,您将从中导入base.py– 此文件从base.py. 然后,您将传输要为开发环境修改的设置:

testsite/testsite/settings/development.py
from .base import *

DEBUG = True

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

在这种情况下,特定于开发的设置是:DEBUG,您True在开发中需要它,但在生产中不需要并且DATABASES,本地数据库而不是生产数据库。您在这里使用 SQLite 数据库进行开发。

注意:出于安全考虑,Django 的 DEBUG 输出永远不会显示任何可能包含以下字符串的设置:API, KEY, PASS, SECRET, SIGNATURE, or TOKEN

这是为了确保机密不会泄露,如果您不小心将项目部署到DEBUG仍然启用的生产环境

话虽如此,永远不要在DEBUG启用的情况下公开部署项目它只会使您的项目的安全性处于危险之中。

接下来,让我们添加到production.py

  • nano testsite/settings/production.py

生产将类似于development.py,但具有不同的数据库配置并DEBUG设置为False

testsite/testsite/settings/production.py
from .base import *

DEBUG = False

ALLOWED_HOSTS = []

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

对于给定的示例数据库配置,您可以使用dotenv配置每个给定的凭据,包括默认值。假设您已经为项目的生产版本设置了数据库,请使用您的配置而不是提供的示例。

您现在已将项目配置为使用基于DJANGO_SETTINGS_MODULEin 的不同设置.env鉴于您使用的示例设置,当您将项目设置为使用生产设置时,DEBUG将变为FalseALLOWED_HOSTS已定义,并且您开始使用您(已经)在服务器上配置的不同数据库。

第 4 步 — 使用 Django 的安全设置

Django 包含可供您添加到项目中的安全设置。在此步骤中,您将向您的项目添加安全设置,这些设置对于任何生产项目都是必不可少的。这些设置旨在在您的项目对公众可用时使用。不建议在您的开发环境中使用任何这些设置;因此,在此步骤中,您将这些设置限制在production.py配置中。

在大多数情况下,这些设置将强制对各种 Web 功能使用 HTTPS,例如会话 cookie、CSRF cookie、将 HTTP 升级到 HTTPS 等。因此,如果您还没有使用指向它的域来设置服务器,请暂时不要看这一节。如果您需要设置您的服务器以备部署,请查看关于此的建议文章结论

首先打开production.py

  • nano production.py

在您的文件中,根据代码后面的说明添加适用于您的项目的突出显示设置:

testsite/testsite/settings/production.py
from .base import *

DEBUG = False

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

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

SECURE_SSL_REDIRECT = True

SESSION_COOKIE_SECURE = True

CSRF_COOKIE_SECURE = True

SECURE_BROWSER_XSS_FILTER = True
  • 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_FILTERX-XSS-Protection: 1; mode=block在所有还没有它的响应上设置标头。这可确保第三方无法将脚本注入您的项目。例如,如果用户使用公共字段将脚本存储在您的数据库中,则在检索该脚本并将其显示给其他用户时,该脚本将不会运行。

如果您想了解有关 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 标头应用于所有子域。启用此标头,意味着即使与此 Django 项目无关your_domainunsecure.your_domain也需要加密unsecure.your_domain

警告:不正确地配置这些附加设置可能会在很长一段时间内破坏您的站点。

在实施这些设置之前,请阅读HSTS 上Django 文档

有必要考虑这些设置如何与您自己的 Django 项目配合使用;总的来说,这里讨论的设置是大多数 Django 项目的良好基础。接下来,您将查看dotENV.

第 5 步 –python-dotenv用于秘密

本教程的最后一部分将帮助您利用python-dotenv. 这将允许您隐藏某些信息,例如您的项目SECRET_KEY或管理员的登录 URL。如果您打算在 GitHub 或 GitLab 等平台上发布代码,这是一个好主意,因为这些机密不会被发布。相反,每当您最初在本地环境或服务器上设置项目时,您都可以创建一个新.env文件并定义这些秘密变量。

您必须隐藏您的,SECRET_KEY以便在本节中开始处理。

打开你的.env文件:

  • nano .env

并添加以下行:

测试站点/.env
DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
SECRET_KEY="your_secret_key"

而在你的base.py

  • nano testsite/settings/base.py

让我们SECRET_KEY像这样更新变量:

testsite/testsite/settings/base.py
. . .
SECRET_KEY = os.getenv('SECRET_KEY')
. . .

您的项目现在将使用SECRET_KEY位于.env.

最后,您将通过向其中添加一长串随机字符来隐藏您的管理 URL。这将确保机器人无法暴力破解登录字段,陌生人也无法尝试猜测登录信息。

.env再次打开

  • nano .env

并添加一个SECRET_ADMIN_URL变量:

测试站点/.env
DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
SECRET_KEY="your_secret_key"
SECRET_ADMIN_URL="very_secret_url"

现在让我们告诉 Django 隐藏你的管理 URL SECRET_ADMIN_URL

  • nano /testsite/urls.py

注意:不要忘记用您自己的秘密字符串替换your_secret_keyvery_secret_url也不要忘记替换very_secret_url为您自己的秘密 URL。

如果你想对这些变量使用随机字符串,Python 提供了一个很棒的secrets.py 库来生成这样的字符串。他们提供示例是创建用于生成安全随机字符串的小型 Python 程序的好方法。

像这样编辑管理 URL:

testsite/testsite/urls.py
import os
from django.contrib import admin
from django.urls import path

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

您现在可以在 中找到管理员登录页面,very_secret_url/admin/而不仅仅是/admin/

结论

在本教程中,您已经配置了当前的 Django 项目,以便在不同环境中轻松使用。您的项目现在可python-dotenv用于处理机密和设置。您的生产设置现在启用了 Django 的内置安全功能。

如果您已启用所有推荐的安全组件并按照指示重新实施设置,则您的项目具有以下关键功能:

  • 用于所有通信的 SSL/HTTPS(例如,子域、cookie、CSRF)。
  • XSS(跨站点脚本)攻击预防。
  • CSRF(跨站请求伪造)攻击预防。
  • 隐藏的项目密钥。
  • 隐藏的管理员登录 URL,防止暴力攻击。
  • 开发和生产的单独设置。

如果您有兴趣了解有关 Django 的更多信息,请查看我们关于Django 开发的系列教程

此外,如果您还没有将您的项目投入生产,这里有一个关于如何在 Ubuntu 20.04 上使用 Postgres、Nginx 和 Gunicorn 设置 Django的教程您还可以查看我们的Django 主题页面以获取更多教程。

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

觉得文章有用?

点个广告表达一下你的爱意吧 !😁