Two Scoops of Django 3x - Compress 8
Two Scoops of Django 3x - Compress 8
For Sphinx, you’ll need to follow the Myst instructions to generate markdown docs: https:
//myst-parser.readthedocs.io/en/latest/using/intro.html.
Here we provide a table that describes what we consider the absolute minimum documen-
tation:
Chapter 25: Documentation: Be Obsessed
Please consider creating documents within these other methods with the same names as the
ones we suggested in the table on the previous page.
interrogate.readthedocs.io/
25.9 Summary
In this chapter we went over the following:
Next, we’ll take a look at common bottlenecks in Django projects and ways to deal with
them.
Chapter 25: Documentation: Be Obsessed
26 | Finding and Reducing
Bottlenecks
This chapter covers a few basic strategies for identifying bottlenecks and speeding up your
Django projects.
On the other hand, if your site’s user base is growing steadily or you’re about to land a
strategic partnership with a popular brand, then read on.
We also urge you to read up on database access optimization in the official Django docs:
docs.djangoproject.com/en/3.2/topics/db/optimization/
You probably have a rough idea of some of the URLs to start with. For example, which
pages don’t feel snappy when they load?
Install django-debug-toolbar locally if you don’t have it yet. Look at your project in a web
browser, and expand the SQL panel. It’ll show you how many queries the current page
contains.
ä Make sure your indexes are helping speed up your most common slow queries. Look
at the raw SQL generated by those queries, and index on the fields that you filter/sort
on most frequently. Look at the generated WHERE and ORDER_BY clauses.
ä Understand what your indexes are actually doing in production. Development ma-
chines will never perfectly replicate what happens in production, so learn how to
analyze and understand what’s really happening with your database.
ä Look at the query plans generated by common queries.
ä Turn on your database’s slow query logging feature and see if any slow queries occur
frequently.
ä Use django-debug-toolbar in development to identify potentially-slow queries defen-
sively, before they hit production.
Once you have good indexes, and once you’ve done enough analysis to know which queries
to rewrite, here are some starting tips on how to go about rewriting them:
The MySQL equivalent is the EXPLAIN command, which isn’t as detailed but is still
helpful. For more information, see:
ä dev.mysql.com/doc/refman/5.7/en/explain.html
A nice feature of django-debug-toolbar is that the SQL pane has an EXPLAIN fea-
ture.
Logs. Don’t add logs to the database. Logs may seem OK on the surface, especially in devel-
opment. Yet adding this many writes to a production database will slow their performance.
When the ability to easily perform complex queries against your logs is necessary, we rec-
ommend third-party services such as Splunk or Loggly, or use of document-based NoSQL
databases.
Ephemeral data. Don’t store ephemeral data in the database. What this means is data that
requires constant rewrites is not ideal for use in relational databases. This includes examples
such as django.contrib.sessions, django.contrib.messages, and metrics. Instead, move this
data to things like Memcached, Redis, and other non-relational stores.
ä wiki.postgresql.org/wiki/Detailed_installation_guides
ä wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
ä revsys.com/writings/postgresql-performance.html
ä craigkerstiens.com/2012/10/01/understanding-postgres-performance
ä craigkerstiens.com/2013/01/10/more-on-postgres-performance
You can easily set up the per-site cache, or you can cache the output of individual views or
template fragments. You can also use Django’s low-level cache API to cache Python objects.
Reference material:
ä docs.djangoproject.com/en/3.2/topics/cache/
ä github.com/niwinz/django-redis
Let’s go over the tools that will help you with these scenarios.
ä Caching of QuerySets.
ä Cache invalidation settings/mechanisms.
ä Different caching backends.
ä Alternative or experimental approaches to caching.
ä django-cacheops
ä django-cachalot
Cache invalidation is hard, and in our experience, magical cache libraries are better
for projects with more static content. By-hand caching is a lot more work, but leads
to better performance in the long run and doesn’t risk those terrifying moments.
The problem with making Django and Python do the work is that compression and minifica-
tion take up system resources, which can create bottlenecks of their own. A better approach
is to use Nginx or Apache web servers configured to compress the outgoing content. If you
are maintaining your own web servers, this is absolutely the way to go.
For CSS and JavaScript, most people use JavaScript-powered tools for minification. Tools
like django-webpack-loader manage the JavaScript libraries within the Django context.
The advantage of this approach is the greater mindshare of tools and solved problems in
this domain space.
Content Delivery Networks (CDNs) like Fastly, Akamai, and Amazon Cloudfront serve
static media such as images, video, CSS, and JavaScript files. They usually have servers all
over the world, which serve out your static content from the nearest location. Using a CDN
rather than serving static content from your application servers can speed up your projects.
ä “The Temple of Django Database Performance” is a book that dives deep into opti-
mizing Django projects for speed and scalability. It’s a delightful book full of fan-
tasy and RPG references and worth every penny. spellbookpress.com/books/
temple-of-django-database-performance/
ä Written with a focus on scaling Django, the book “High Performance Django” espouses
many good practices. Full tricks and tips, as well as questions in each section that
force you to think about what you are doing. Dated in places, but still full of useful
information. highperformancedjango.com
ä Watch videos of presentations from past DjangoCons and Py-
Cons about different developers’ experiences. Scaling prac-
tices vary from year to year and from company to company:
https://www.youtube.com/results?search_query=django+scaling
Figure 26.1: With your site running smoothly, you’ll be feeling as cool as a cone.
26.10: Summary
26.10 Summary
In this chapter, we explored a number of bottleneck reduction strategies including:
ä Whether you should even care about bottlenecks in the first place.
ä Profiling your pages and queries.
ä Optimizing queries.
ä Using your database wisely.
ä Caching queries.
ä Identifying what needs to be cached.
ä Compression of HTML, CSS, and JavaScript.
ä Exploring other resources.
In the next chapter, we’ll cover various practices involving asynchronous task queues, which
may resolve our bottleneck problems.
Chapter 26: Finding and Reducing Bottlenecks
27 | Asynchronous Task Queues
An asynchronous task queue is one where tasks are executed at a different time from when
they are created, and possibly not in the same order they were created. Here is an example
of a human-powered asynchronous task queue:
1 In their spare time, Audrey and Daniel make ice cream cakes, taking orders from
friends and family. They use an issue tracker to track their tasks for scooping, spread-
ing, and decorating each cake.
2 Every so often, when they have spare time, they review the list of tasks and pick
one to do. Audrey prefers scooping and decorating, always doing those tasks first.
Daniel prefers scooping and spreading, finishing those before decorating. The result
is asynchronous completion of cake-making tasks.
3 As a cake-making task is completed and delivered, they mark the issue as closed.
Broker The storage for the tasks themselves. This can be implemented using any sort of
Chapter 27: Asynchronous Task Queues
persistence tool, although in Django the most common ones in use are RabbitMQ
and Redis. In the human-powered example, the storage is an online issue tracker.
Producer The code that adds tasks to the queue to be executed later. This is application
code, the stuff that makes up a Django project. In the human-powered example, this
would be Audrey and Daniel, plus anyone they can get to pitch in to help.
Worker The code that takes tasks from the broker and performs them. Usually there is more
than one worker. Most commonly each worker runs as a daemon under supervision.
In the human-powered example, this is Audrey and Daniel.
Serverless Usually provided by services such as AWS Lambda, this is, to paraphrase Mar-
tin Fowler, “where some amount of server-side logic is written by us but unlike tra-
ditional architectures is run in stateless compute containers that are event-triggered,
ephemeral (only last for one invocation), and fully managed by a 3rd party.” Server-
less takes over the role of Broker and Worker. In the human-powered example, it’s
as if Daniel and Audrey use a third-party service to take the orders and then follow
their precise instructions on doing the work.
Here is a useful rule of thumb for determining if a task queue should be used:
Please keep in mind there are site-traffic driven exceptions to all of these use cases:
27.1: Do We Need a Task Queue?
ä Sites with small-to-medium amounts of traffic may never need a task queue for any
of these actions.
ä Sites with larger amounts of traffic may discover that nearly every user action requires
use of a task queue.
Determining whether or not a site or action needs a task queue is a bit of an art. There is no
easy answer we can provide. However, knowing how to use them is a really powerful tool in
any developer’s toolkit.
Chapter 27: Asynchronous Task Queues
28 | Security Best Practices
When it comes to security, Django has a pretty good record. This is due to security tools
provided by Django, solid documentation on the subject of security, and a thoughtful team
of core developers who are extremely responsive to security issues. However, it’s up to indi-
vidual Django developers such as ourselves to understand how to properly secure Django-
powered applications.
This chapter contains a list of things helpful for securing your Django application. This list
is by no means complete. Consider it a starting point.
Most of Django’s security features “just work” out of the box without additional configura-
tion, but there are certain things that you’ll need to configure. We’ve highlighted some of
these details in this chapter, but please make sure that you read the official Django documen-
tation on security as well: docs.djangoproject.com/en/3.2/topics/security/
Keep in mind that when you turn off DEBUG mode, you will need to set AL-
LOWED_HOSTS or risk raising a SuspiciousOperation error, which generates a 400
BAD REQUEST error that can be hard to debug. For more information on setting/debug-
ging ALLOWED_HOSTS see:
We cover the mechanics of how to keep your SECRET_KEY out of version control
28.6: HTTPS Everywhere
in Chapter 5: Settings and Requirements Files, Section 5.3: Separate Configuration From
Code, and Section 5.4: When You Can’t Use Environment Variables.
There is also no guarantee that any of your users are seeing what you expect them to see: an
attacker could manipulate anything in the request or the response. So HTTPS makes sense
even if all your information is public, but you do care about the integrity of the information.
Your entire site must be behind HTTPS. Your site’s static resources must also be served via
HTTPS, otherwise visitors will get warnings about “insecure resources” which will rightly
scare them away from your site. For reference, these warnings exist because they are a po-
tential man-in-the-middle vector.
If visitors try to access your site via HTTP, they must be redirected to HTTPS. This
can be done either through configuration of your web server or with Django middleware.
Performance-wise, it’s better to do this at the web server level, but if you don’t have control
over your web server settings for this, then redirecting via Django middleware is fine.
In the modern era obtaining SSL certificates is cheap and easy. Depending on your plat-
form, it’s even provided for free. To set it up, follow the instructions for your particular
web server or platform-as-a-service. Our preferred services are the very reputable (and free)
letsencrypt.org and cloudflare.com. These services makes it trivial to secure sites
with proper SSL certicates.
Large cloud providers such as AWS, Google, and Microsoft also provide options.
access rights for users to the bare minimum permissions they need to perform their
work.
While it is unlikely that a penetration will occur on these systems, either from an
outside or employee, it is not outside the realm of possibility.
For many systems the time benefit of using someone else’s SSL is worth it. However,
sites containing critical privacy information (HIPAA comes to mind) should set up
their own SSL.
settings.MIDDLEWARE definition.
2 Set settings.SECURE_SSL_REDIRECT to True .
WARNING: django.middleware.security.SecurityMiddleware
Does Not Include static/media
Even if all Django requests are served over HTTPS, omitting HTTPS for resources
like javascript would still allow attackers to compromise your site.
As JavaScript, CSS, images, and other static assets are typically served directly by
the web server (nginx, Apache), make certain that serving of such content is done
via HTTPS. Providers of static assets such as Amazon S3 now do this by default.
28.6: HTTPS Everywhere
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
If you have set up your own web servers, Wikipedia has sample HSTS configuration snippets
that you can use: en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
When you enable HSTS, your site’s web pages include a HTTP header that tells HSTS-
compliant browsers to only connect to the site via secure connections:
To give you a better idea of how this works, here’s an example of what a HTTP Strict
Transport Security response header might look like:
1 Set max-age to a small value like 300 (5 minutes) during initial deployment of a
secured site to make sure you haven’t screwed something up or forgotten to make
some portion of the site available via HTTPS. We suggest this small value because
once you set max-age, we can’t unset it for users; their browsers control expiration,
not us.
Chapter 28: Security Best Practices
2 Once it’s been confirmed the site is properly secured, set max-age to incrementally
larger values. Go to 1 hour (3600), 1 week (604800), 1 month (2592000), and finally
1 year (31536000). This allows us to correct issues that may not turn up in initial
checks.
3 Once the project’s max-age is set to 1 year or greater (31536000), submit the project
for HSTS preloading, which can be done at hstspreload.org/. This aids in block-
ing active attackers from intercepting the first request from a user to a site.
Note that HSTS should be enabled in addition to redirecting all pages to HTTPS as de-
scribed earlier.
Example: Imagine we create a new Django website called example.com. The site
is HTTPS with HSTS enabled. We test the HSTS settings, which work fine,
and then increase the duration to a year. Alas, after a month, someone realises
legacy.example.com is still a production service and does not support HTTPS. We
remove includeSubdomains from the header, but by now it’s already too late: all
clients inside the company have the old HSTS header remembered.
Once you have a server set up (preferably a test server), use the Qualys SSL Labs server test
at ssllabs.com/ssltest/index.html to see how well you did. A fun security game is
trying to score an A+. Especially as the official Two Scoops of Django reward for getting
that good of a grade is a trip to the local favorite ice cream saloon.
We recommend that you avoid setting wildcard values here. For more information, read the
Django documentation on ALLOWED_HOSTS and the get_host() method:
ä docs.djangoproject.com/en/3.2/ref/settings/#allowed-hosts
ä docs.djangoproject.com/en/3.2/ref/request-response/#django.
http.HttpRequest.get_host
fragments. All args and kwargs are escaped before being passed to str.format() which
then combines the elements.
Reference: docs.djangoproject.com/en/3.2/ref/utils/#django.utils.html.
format_html
Reference: docs.djangoproject.com/en/3.2/ref/templates/builtins/
#json-script
ä en.wikipedia.org/wiki/Content_Security_Policy
28.10: Defend Against Python Code Injection Attacks
ä github.com/mozilla/django-csp
ä docs.djangoproject.com/en/3.2/ref/templates/builtins/#escape
ä en.wikipedia.org/wiki/Cross-site_scripting
Needless to say, upon discovery the critical security flaw was quickly removed. This just
goes to show that no matter how secure Python and Django might be, we always need to
be aware that certain practices are incredibly dangerous.
– docs.python.org/3/library/pickle.html
You should not use the Python standard library’s pickle module to deserialize anything
which could have been modified by the user. As a general rule, avoid accepting pickled values
from user for any reason. Specific warnings about pickle and security are listed below:
ä lincolnloop.com/blog/playing-pickle-security/
ä blog.nelhage.com/2011/03/exploiting-pickle/
Chapter 28: Security Best Practices
When using PyYAML, only use safe_load() . While the use of YAML in the Python
and Django communities is rare outside of continuous integration, it’s not uncommon to
receive this format from other services. Therefore, if you are accepting YAML documents,
only load them with the yaml.safe_load() method.
For reference, the yaml.load() method will let you create Python objects,
which is really bad. As Ned Batchelder says, yaml.load() should be renamed to
yaml.dangerous_load() : nedbatchelder.com/blog/201302/war_is_peace.
html
Typically most Django sites use either database- or cache-based sessions. These function
by storing a hashed random value in a cookie which is used as a key to the real session
value, which is stored in the database or cache. The advantage of this is that only the key
to the session data is sent to the client, making it very challenging for malignant coders to
penetrate Django’s session mechanism.
However, Django sites can also be built using cookie-based sessions, which place the session
data entirely on the client’s machine. While this means slightly less storage needs for the
server, it comes with security issues that justify caution. Specifically:
Another thing to consider is that cookie-based sessions are a potential client-side perfor-
mance bottleneck. Transmitting the session data server-to-client is generally not an issue,
but client-to-server transmissions are much, much slower. This is literally the difference
between download and upload speeds all internet users encounter.
Additional reading:
ä docs.djangoproject.com/en/3.2/topics/http/sessions/
#using-cookie-based-sessions
ä http://bit.ly/2plfHqU Threatpost.com article on cookies
ä yuiblog.com/blog/2007/03/01/performance-research-part-3/
class SpecialForm(forms.Form):
my_secret = forms.CharField(
widget=forms.TextInput(attrs={'autocomplete': 'off'}))
For any site that might be used in a public area (an airport for example), consider changing
the form field itself to PasswordInput:
class SecretInPublicForm(forms.Form):
my_secret = forms.CharField(widget=forms.PasswordInput())
If you must allow upload and download of arbitrary file types, make sure that the server
uses the “Content-Disposition: attachment” header so that browsers won’t display
the content inline.
images with image content type headers, and that uploads are restricted to a whitelisted
subset of file extensions.
Take extra care with your web server’s configuration here, because a malicious user can try to
attack your site by uploading an executable file like a CGI or PHP script and then accessing
the URL. This won’t solve every problem, but it’s better than the defaults.
Consult your web server’s documentation for instructions on how to configure this, or con-
sult the documentation for your platform-as-a-service for details about how static assets and
user-uploaded files should be stored.
If you are only accepting uploads of certain file types, do whatever you can do to ensure that
the user is only uploading files of those types. For example, you can:
If the contents of an uploaded file are malicious, any validation happening after
to_python() is executed may be too late.
Further research:
ä docs.djangoproject.com/en/3.2/ref/models/fields/#filefield
Chapter 28: Security Best Practices
One common reason we want to avoid the Meta.exclude attribute is that its behavior
implicitly allows all model fields to be changed except for those that we specify. When using
the excludes attribute, if the model changes after the form is written, we have to remember
to change the form. If we forget to change the form to match the model changes, we risk
catastrophe.
Let’s use an example to show how this mistake could be made. We’ll start with a simple ice
cream store model:
# stores/models.py
from django.conf import settings
from django.db import models
class Store(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
# Assume 10 more fields that cover address and contact info.
Here is the wrong way to define the ModelForm fields for this model:
# DON'T DO THIS!
from django import forms
class StoreForm(forms.ModelForm):
class Meta:
28.14: Don’t Use ModelForms.Meta.exclude
model = Store
# DON'T DO THIS: Implicit definition of fields.
# Too easy to make mistakes!
excludes = ("pk", "slug", "modified", "created", "owner")
In contrast, this is the right way to define the same ModelForm’s fields:
class StoreForm(forms.ModelForm):
class Meta:
model = Store
# Explicitly specifying the fields we want
fields = (
"title", "address_1", "address_2", "email",
"usstate", "postal_code", "city",
)
The first code example, as it involves less typing, appears to be the better choice. It’s not, as
when you add a new model field you now you need to track the field in multiple locations
(one model and one or more forms).
Let’s demonstrate this in action. Perhaps after launch we decide we need to have a way of
tracking store co-owners, who have all the same rights as the owner. They can access account
information, change passwords, place orders, and specify banking information. The store
model receives a new field as shown on the next page:
# stores/models.py
from django.conf import settings
from django.db import models
class Store(models.Model):
title = models.CharField(max_length=255)
Chapter 28: Security Best Practices
slug = models.SlugField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
co_owners = models.ManyToManyField(settings.AUTH_USER_MODEL)
# Assume 10 more fields that cover address and contact info.
The first form code example which we warned against using relies on us remembering to
alter it to include the new co_owners field. If we forget, then anyone accessing that store’s
HTML form can add or remove co-owners. While we might remember a single form,
what if we have more than one ModelForm for a model? In complex applications this is not
uncommon.
On the other hand, in the second example, where we used Meta.fields we know exactly
what fields each form is designed to handle. Changing the model doesn’t alter what the form
exposes, and we can sleep soundly knowing that our ice cream store data is more secure.
These occur when the patterns such as Active Record, designed to empower developers,
create security risks for web applications. The solution is the approach we advocate in this
section, which is explicit definition of fields that can be modified.
Django allows you to bypass its ORM and access the database more directly through raw
SQL. When using this feature, be especially careful to escape your SQL code properly. This
is of concern in these specific components of Django:
Reference:
ä docs.djangoproject.com/en/3.2/topics/security/
#sql-injection-protection
Instead, we recommend using third-party services like Stripe, Braintree, Adyen, PayPal,
and others that handle storing this information for you, and allow you to reference the data
via special tokens. Most of these services have great tutorials, are very Python and Django
friendly, and are well worth the time and effort to incorporate into your project.
unless required by law. Even if required by law, if it is possible to store the data in one-way
hashes, then by all means do so.
If PII is discovered within a project that doesn’t have a legal obligation to store it, raise it
as an issue immediately with the project owners. Having this data stolen through either
technical or social methods can be disastrous legally and financially for an organization. In
the case of Payment cards can be easily cancelled, the same cannot be said for PII.
In the case of PHI, which is medical data, our warnings are more dire. Specifically, ev-
eryone involved is at risk for civil and criminal prosecution under Title II of HIPAA. See
en.wikipedia.org/wiki/HIPAA#Security_Rule. While that rule is for US-based
projects, many countries have similar regulations. For example, the authors know someone
called to trial in Kenya over a PHI leak.
‘I’ve set up (these kinds of services) for distinct actions: it mails me once a week
for each project with any outdated dependencies, and if it finds an insecure ver-
sion it automatically creates a pull request in GitHub, so tests run automatically
and I can deploy quickly.’
– Sasha Romijn, Django core dev and security reviewer for Two Scoops of
Django 1.8
ä docs.djangoproject.com/en/3.2/ref/clickjacking/
Unfortunately, Python, like many other programming languages, doesn’t account for this or
other venues of attack via XML. Furthermore, third-party Python libraries such as lxml are
vulnerable to at least 4 well-known XML-based attacks. For a list of Python and Python
library vulnerabilities see https://feld.to/2KKwuMq.
Fortunately for us, Christian Heimes created defusedxml, a Python library designed to
patch Python’s core XML libraries and some of the third-party libraries (including lxml).
ä https://pypi.org/project/defusedxml
For modern web applications, what that usually means is you enter your password as well
as a value sent to your mobile device. Another option for One-Time Passwords (OTP) is
to register them in a password manager.
The advantage of 2FA is that it adds another component, one that changes frequently, to
the authentication process, great for any site involving personal identity, finance, or medical
requirements.
The downside is that the user needs to have a charged mobile device with access to a network
in order to log in to your site, making it not so ideal for users who may not have a charged
mobile device or easy access to a network.
ä en.wikipedia.org/wiki/Two_factor_authentication
ä https://pypi.org/project/django-two-factor-auth
Chapter 28: Security Best Practices
Our opinion is that at this point in time, length is more important than complexity. An 8
character length password mixing cases, numbers, and special characters is easier by several
orders of magnitude to break than a 50-character sentence of just lower cased letters. What’s
even better is if you have a 30-50 character sentence that includes numbers, mixed cases,
and special characters.
Reference: xkcd.com/936/
28.25: Don’t Prevent Copy/Pasting of Password
Mozilla also provides a similar, but non-Django specific service called Observatory
(observatory.mozilla.org).
Here are some patterns for looking up records without revealing sequential identifiers:
28.28.2 UUIDs
Django comes with very useful models.UUIDField. While a use case for using them as
primary keys for large distributed systems exists, they also serve nicely for public lookups.
Here is a sample model:
import uuid
from django.db import models
class IceCreamPayment(models.Model):
uuid = models.UUIDField(
unique=True,
default=uuid.uuid4,
editable=False)
def __str__(self):
return str(self.pk)
UUID('0b0fb68e-5b06-44af-845a-01b6df5e0967')
>>> IceCreamPayment.objects.get(uuid=payment.uuid)
<IceCreamPayment: 1>
cryptanalysis-of-hashids).
To summarize: If you want to hide your sequential identifiers, don’t rely on obfus-
cation.
– James Bennett, Django Core Developer and security reviewer of Two Scoops
of Django.
PBKDF2 is Django’s default because it’s supported in the Python standard library and thus
requires no third-party packages. Argon2 is the best option that is natively supported by
Django. Installation instructions and references:
ä https://docs.djangoproject.com/en/3.2/topics/auth/passwords/
#using-argon2-with-django - Instructions on installing and using Argon2
with Django
ä https://docs.djangoproject.com/en/3.2/topics/auth/passwords/
#argon2 - Instructions on customizing Argon2 arguments (advanced and not
necessary for most projects)
ä latacora.micro.blog/2018/04/03/cryptographic-right-answers.
html - Easy-to-understand list of answers to questions non-security focused
developers get asked about cryptographic questions.
,→ href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script
,→ src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script
,→ src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></
<script
,→ src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></scr
This is why we recommend the use of subresource integrity hashes when including CSS,
JavaScript, or web fonts from external projects. This is provided as a feature by CDNJS, the
JQuery project, the Bootstrap project, and a shockingly small number of other projects.
From the official Bootstrap site, this is what using SRI looks like:
,→ href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
,→ integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T
crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
,→ integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo
crossorigin="anonymous"></script>
<script
,→ src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
,→ integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1
crossorigin="anonymous"></script>
<script
,→ src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
,→ integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM
crossorigin="anonymous"></script>
If a project doesn’t include SRI hashes with their installation instructions, we suggest you
Chapter 28: Security Best Practices
load their assets using CDNJS, which includes it as a feature in their “copy” dropdown. Just
choose the “copy link tag” option.
ä cdnjs.com - CDN that provides SRI hashes for all libraries they host
ä developer.mozilla.org/en-US/docs/Web/Security/Subresource_
Integrity - Mozilla’s documetation of SRI
ä w3.org/TR/SRI/ - Official specification
In order to add clarity, we’ve created Appendix G: Security Settings Reference. This is where
we put important and useful information on how to better configure the security settings of
a Django project.
First, keep in mind that security practices are constantly evolving, both in the
Django community and beyond. Subscribe to groups.google.com/forum/#!forum/
django-announce and check Twitter, Hacker News, and various security blogs regularly.
Second, remember that security best practices extend well beyond those practices specific to
Django. You should research the security issues of every part of your web application stack,
and you should follow the corresponding sources to stay up to date.
amzn.to/1dZ7xEY
28.34 Summary
Please use this chapter as a starting point for Django security, not the ultimate reference
guide. See the Django documentation’s list for additional security topics:
docs.djangoproject.com/en/3.2/topics/security/
#additional-security-topics
Django comes with a good security record due to the diligence of its community and at-
tention to detail. Security is one of those areas where it’s a particularly good idea to ask for
help. If you find yourself confused about anything, ask questions and turn to others in the
Django community for help.
Chapter 28: Security Best Practices
29 | Logging: What’s It For, Anyway?
Logging is like rocky road ice cream. Either you can’t live without it, or you forget about it
and wonder once in awhile why it exists.
Anyone who’s ever worked on a large production project with intense demands understands
the importance of using the different log levels appropriately, creating module-specific log-
gers, meticulously logging information about important events, and including extra detail
about the application’s state when those events are logged.
While logging might not seem glamorous, remember that it is one of the secrets to building
extremely stable, robust web applications that scale and handle unusual loads gracefully.
Logging can be used not only to debug application errors, but also to track interesting
performance metrics.
Logging unusual activity and checking logs regularly is also important for ensuring the
security of your server. In the previous chapter, we covered the importance of checking your
server access and error logs regularly. Keep in mind that application logs can be used in
similar ways, whether to track failed login attempts or unusual application-level activity.
In addition to your application logs, you should be aware that there are other types of logs,
and that using and checking all of your server logs is necessary. Your server logs, database
logs, network logs, etc. all provide vital insight into your production system, so consider
them all equally important.
The different log levels available to you are DEBUG, INFO, WARNING, ERROR, and CRITICAL.
Let’s now explore when it’s appropriate to use each logging level.
In your production environment, we recommend using every log level except for DEBUG.
Since the same CRITICAL, ERROR, WARNING, and INFO logs are captured whether in pro-
duction or development, introspection of buggy code requires less modification of code. This
is important to remember, as debug code added by developers working to fix one problem
can create new ones.
The rest of this section covers how each log level is used.
For example, if your code relies on an internal web service being available, and if that web
service is part of your site’s core functionality, then you might log at the CRITICAL level
anytime that the web service is inaccessible.