如何增强您的生产Django项目的安全性
简介
开发Django应用可以是一种方便的体验,因为它被设计得灵活且可扩展。这个前提也适用于Django的安全设置,这些设置可以帮助您为项目做好生产准备。但是,还有几种方法可以进一步保护您的项目。
拆分你的设置可以根据不同环境设置不同的配置。利用.env来设置环境变量或隐藏机密设置将确保你不会发布可能危及项目的细节。同时改变默认的URL和其他设置可以帮助你避免常见的安全漏洞。
尽管一开始实施这些策略可能会耗费时间,但制定一个实用的工作流程将使您能够在不影响安全性或效率的情况下部署项目的发布版本。
在本教程中,您将通过实施和配置基于环境的设置、.env 文件和 Django 内置的安全设置,为您的 Django 项目使用一个注重安全性的工作流。这些功能互相补充,将使您的 Django 项目准备好应对您可能采取的不同部署方式。
先决条件
在开始阅读这个指南之前,你需要以下物品:
- A pre-existing Django project. If you don’t already have one set up, you can use our How To Install Django and Set Up a Development Environment tutorial for setup. In that tutorial, you’ll use the testsite project from this tutorial as an example.For your Django project, you’ll also need Python 3 installed. You can install it by following Step 1 of our tutorial, How To Install Python 3 and Set Up a Programming Environment on an Ubuntu 20.04 Server.
- A Let’s Encrypt certificate. If you don’t already have one set up, you can use our How To Secure Nginx with Let’s Encrypt on Ubuntu 20.04 tutorial for setup.To use the Let’s Encrypt certificate, you’ll need Nginx installed. You can install it by following our tutorial How To Install Nginx on Ubuntu 20.04.
- This Django Development tutorial series is a great way to get familiar with Django’s file structure and its core settings.
Note
第一步——重新构建Django的设置
在搞清楚如何保护您的Django项目之前,您需要进入项目的目录并激活虚拟环境。
- cd django-apps
- . env/bin/activate
在这一步中,您将首先将settings.py文件重新排列为特定环境的配置。当您需要在不同的环境之间移动项目时,比如开发环境和生产环境,这是一个很好的做法。这种安排意味着在不同的环境中需要重新配置的内容更少;相反,您将使用环境变量来在不同的配置之间进行切换,这将在本教程中进行讨论。
在您的项目子目录中创建一个名为 settings 的新目录。
- mkdir testsite/testsite/settings
根据先决条件,本教程使用testsite,但你可以在这里替换为你的项目名称。
这个目录将会替换你当前的settings.py配置文件;所有基于环境的设置将会放在该文件夹中的单独文件中。
在你的新设置文件夹中,创建三个Python文件。
- cd testsite/testsite/settings
- touch base.py development.py production.py
development.py 文件将包含通常在开发过程中使用的设置。而 production.py 文件将包含用于生产服务器的设置。你应该将这两者分开保存,因为生产配置将使用无法在开发环境下正常工作的设置,例如强制使用 HTTPS、添加头部信息以及使用生产数据库。
base.py的设置文件将包含开发.py和生产.py将继承的设置。这是为了减少冗余和帮助保持代码的整洁。这些Python文件将替换settings.py,因此您现在需要删除settings.py以避免让Django感到困惑。
当你还在设置目录中时,使用以下命令将settings.py重命名为base.py。
- mv ../settings.py base.py
你刚刚完成了新的基于环境的设置目录的大纲。你的项目还不能理解你的新配置,所以下一步,你将解决这个问题。
步骤2 – 使用django-environ
目前,Django无法识别您的新设置目录或其内部文件。因此,在继续使用基于环境的设置之前,您需要使用django-environ使Django正常工作。这是一个依赖项,它可以从.env文件中加载环境变量。这意味着Django将在您项目的根目录中查找一个.env文件,以确定要使用哪个设置配置。
进入你项目的根目录,然后使用ls命令列出目录的内容。
- cd ../../
- ls
你项目根目录中的文件应该如下所示:
db.sqlite3 manage.py testsite
安装django-environ。
- pip install django-environ
现在需要配置Django来使用.env文件。你需要编辑两个文件:manage.py用于开发环境,wsgi.py用于生产环境。
首先,使用Nano或您首选的文本编辑器打开manage.py文件进行编辑。
- nano manage.py
添加下面突出显示的代码:
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以进行编辑。
- nano testsite/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文件。
- nano .env
现在加入以下一行代码来将环境设置为开发模式:
DJANGO_SETTINGS_MODULE="testsite.settings.development"
通过按下CTRL+X,按下Y键保存,然后按下回车键以保存并关闭文件。
Note
建议在您的项目中创建一个.env.example文件,以便您可以轻松地在需要时创建一个新的.env文件。
所以,默认情况下 Django 会使用 testsite.settings.development,但是如果你将 DJANGO_SETTINGS_MODULE 更改为 testsite.settings.production,它将开始使用你的生产配置。接下来,你需要填充你的 development.py 和 production.py 的设置配置。
第三步——创建开发和生产环境设置
接下来,您将打开您的base.py文件,在独立的development.py和production.py文件中添加您要修改的配置信息。production.py文件将需要使用您的生产数据库凭据,请确保您拥有这些凭据。
Note
在本教程中,您将使用先决条件教程中的Django项目作为示例项目。您将把设置从base.py移动到development.py。首先打开development.py。
- nano 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文件中导入设置,该文件继承了base.py的设置。然后,您将转移要修改的开发环境设置。在这种情况下,与开发相关的设置如下:DEBUG,在开发环境中需要为True,但在生产环境中不需要;以及DATABASES,使用本地数据库而不是生产数据库。您在这里使用的是SQLite数据库作为开发数据库。
Note
这样做是为了确保如果您意外地使用DEBUG仍然启用将项目部署到生产环境中,秘密信息不会被泄露。
言归正传,永远不要在DEBUG启用的情况下公开部署项目。它只会使您的项目安全受到威胁。
接下来,您将在production.py中添加。使用以下命令打开文件:
- nano testsite/settings/production.py
然后添加以下代码。生成将类似于development.py,但具有不同的数据库配置和DEBUG设置为False:
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=''),
}
}
按下CTRL+X,按Y保存,然后按回车键,保存并关闭文件。
对于给出的示例数据库配置,你可以使用 .env 文件来配置每个给定的凭据,并包括默认值。假设你已经为你的项目的生产版本设置了一个数据库,请使用你自己的配置而不是提供的示例。
现在,您已经配置了您的项目以根据.env中的DJANGO_SETTINGS_MODULE使用不同的设置。鉴于您使用的示例设置,当您将项目设置为使用生产环境设置时,DEBUG变为False,ALLOWED_HOSTS被定义,并且您开始使用您已在服务器上配置好的不同数据库。
第四步 — 使用Django的安全设置工作
Django 包含了预先设置的安全性设置,供您添加到项目中。在这一步骤中,您将为项目添加被视为任何生产项目所必需的安全性设置。这些设置用于在项目对公众可用时使用。不建议在开发环境中使用这些设置;因此,在这一步骤中,您将这些设置限制为 production.py 配置。
大多数情况下,这些设置将强制使用HTTPS来进行各种网络功能,例如会话cookie、CSRF cookie、HTTP升级为HTTPS等。因此,如果您尚未为您的服务器设置指向其的域名,请先暂停这一部分。如果您需要准备部署服务器,请查看结论中所推荐的文章。
首先打开production.py文件。
- nano testsite/settings/production.py
在您的文件中,根据代码后面的解释,添加适用于您的项目的突出显示的设置。
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 is a list of strings that represent the host/domain names that your project can serve. This is a security measure to prevent an attacker from poisoning caches and DNS. Find more details about ALLOWED_HOSTS in the Django documentation.
- SECURE_SSL_REDIRECT redirects all HTTP requests to HTTPS (unless exempt). This means your project will always try to use an encrypted connection. You will need to have SSL configured on your server for this to work. Note that if you have Nginx or Apache configured to do this already, this setting will be redundant.
- SESSION_COOKIE_SECURE tells the browser that cookies can only be handled over HTTPS. This means cookies your project produces for activities, such as logins, will only work over an encrypted connection.
- CSRF_COOKIE_SECURE is the same as SESSION_COOKIE_SECURE but applies to your CSRF token. CSRF tokens protect against cross-site request forgery. Django CSRF protection does this by ensuring any forms submitted (for logins, signups, and so on) to your project were created by your project and not a third party.
- SECURE_BROWSER_XSS_FILTER sets the X-XSS-Protection: 1; mode=block header on all responses that do not already have it. This ensures third parties cannot inject scripts into your project. For example, if a user stores a script in your database using a public field, when that script is retrieved and displayed to other users it will not run.
按下CTRL+X,按下Y保存,然后按下ENTER键来保存和关闭文件。
如果您想了解更多关于Django内各种安全设置的信息,请查阅他们的文档。
Warning
其他设置
以下设置是为了支持HTTP严格传输安全(HSTS) – 这意味着您的整个网站必须始终使用SSL。
- SECURE_HSTS_SECONDS is the amount of time in seconds HSTS is set for. If you set this for an hour (in seconds), every time you visit a web page on your website, it tells your browser that for the next hour HTTPS is the only way you can visit the site. If during that hour you visit an insecure part of your website, the browser will show an error and the insecure page will be inaccessible.
- SECURE_HSTS_PRELOAD only works if SECURE_HSTS_SECONDS is set. This header instructs the browser to preload your site. This means that your website will be added to a hard-coded list, which is implemented in popular browsers, like Firefox and Chrome. This requires that your website is always encrypted. It is important to be careful with this header. If at anytime you decide not to use encryption for your project, it can take weeks to be manually removed from the HSTS preload list.
- SECURE_HSTS_INCLUDE_SUBDOMAINS applies the HSTS header to all subdomains. Enabling this header means that both your_domain and unsecure.your_domain will require encryption, even if unsecure.your_domain is not related to this Django project.
Warning
在实施这些设置之前,请阅读Django文档上有关HSTS的说明。
需要考虑这些设置如何与您自己的Django项目配合使用;总的来说,这里讨论的配置是大多数Django项目的良好基础。接下来,您将进一步审查一些.env的使用方法。
第五步 — 使用 django-environ 加密秘钥
这个教程的最后一部分将帮助您运用django-environ。这将使您能够隐藏某些信息,例如您的项目的SECRET_KEY或管理员的登录URL。如果您计划在GitHub或GitLab等平台上发布代码,这是一个很好的主意,因为这些秘密不会被公开。相反,当您在本地环境或服务器上开始设置项目时,您可以创建一个新的.env文件并定义这些秘密变量。
在这个部分,你必须隐藏你的SECRET_KEY,这样你才能开始工作。
在项目的根目录下打开你的.env文件。
- nano .env
并添加以下行,确保用你自己的机密字符串替代 “your_secret_key”。
DJANGO_SETTINGS_MODULE="testsite.settings.development"
SECRET_KEY="your_secret_key"
然后按下CTRL+X保存并关闭文件,按Y保存,然后按ENTER。
接下来,打开 base.py 文件。
- nano testsite/settings/base.py
像这样更新SECRET_KEY变量:
. . .
<^>import environ
env = environ.Env()
environ.Env.read_env()<^>
SECRET_KEY = env('SECRET_KEY')
. . .
Note
然后按下CTRL+X,按Y保存并关闭文件,然后按ENTER键。您的项目现在将使用位于.env文件中的SECRET_KEY。
最后,您将通过给管理员URL添加一长串随机字符来隐藏它。因此,您将无法访问your_domain/admin,而是需要访问your_domain/very_secret_url/admin。这会确保机器人和陌生人都很难找到您的管理员URL,因此他们会更难尝试强行破解您的管理员登录。
再次打开 .env 文件:
- nano .env
并添加一个SECRET_ADMIN_URL变量
DJANGO_SETTINGS_MODULE="testsite.settings.development"
SECRET_KEY="your_secret_key"
SECRET_ADMIN_URL="very_secret_url"
通过按下CTRL+X键,按下Y键保存,然后按下回车键,保存并关闭文件。
现在你要告诉Django使用SECRET_ADMIN_URL来隐藏你的管理后台URL。
- nano testsite/urls.py
Note
如果你想要为这个变量使用随机字符串,Python提供了一个很棒的secrets.py库来生成这样的字符串。他们给出的例子是创建小型Python程序生成安全随机字符串的很好的方式。
将管理员URL进行如下编辑:
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/上。
结论 (jié
在本教程中,您已经配置了当前的Django项目,以便在不同的环境中轻松使用。您的项目现在利用django-environ来处理秘密和设置。您的生产环境设置现在启用了Django内置的安全功能。
如果您已经启用了所有建议的安全组件并按照指示重新实施了设置,那么您的项目具备以下关键功能:
- SSL/HTTPS for all communications (for example, subdomains, cookies, CSRF).
- XSS (cross-site scripting) attacks prevention.
- CSRF (cross-site request forgery) attacks prevention.
- Concealed project secret key.
- Concealed admin login URL, preventing brute-force attacks.
- Separate settings for development and production.
如果你对学习更多关于Django感兴趣,可以查看我们关于Django开发的教程系列。
另外,如果您尚未将项目投入实际生产中,请参考以下教程,在Ubuntu 20.04上使用Postgres、Nginx和Gunicorn配置Django。您也可以查看我们的Django主题页面以获取更多教程。
当然,请详细阅读 Django 的设置文档以获取更多信息。