之前提到过的内容
配置与逻辑代码分离, 脱离版本控制
使用 CSRF token, 保护HTTPS Forms表单的提交安全
不要使用连续的ID作为查询条件
Django中的安全特性
➤ Cross-site scripting (XSS) protection.
➤ Cross-site request forgery (CSRF) protection.
➤ SQL injection protection.
➤ Clickjacking protection.
➤ Support for TLS/HTTPS/HSTS, including secure cookies.
➤ Secure password storage, using the PBKDF2 algorithm with a SHA256 hash by default.
➤ Automatic HTML escaping.
➤ An expat parser hardened against XML bomb attacks.
➤ Hardened JSON, YAML, and XML serialization/deserialization tools.
生产环境中关闭 DEBUG
模式
会暴露许多 stack trace page
信息
SECRET_KEY
存储在合适, 安全的地方
HTTPS
Everywhere
Django co-leader Jacob Kaplan-Moss says, “Your whole site should only be available via
HTTPS, not HTTP at all. This prevents getting “firesheeped” (having a session cookie stolen
when served over HTTP). The cost is usually minimal.”
应该获取一个有保障的 SSL certificate
-
Please Use Let’s Encrypt for SSL Certificates
It used to be that getting an SSL certificate was a painful process. The tools and documentation were and are unpleasant to use, and some of the companies provided them covered their sites with so many shady advertisements that you wondered if they were run by criminals. That all changed in April of 2016 with the launch of Let’s Encrypt (letsencrypt.org). It’s a free service sponsored by large and small organizations who had the same problems we did. They provide easy-to-use open source software that is very well documented and used for millions of projects. Going forward, our opinion is that unless you are using a trusted service that integrates with your hosting platform, you should be using Let’s Encrypt. An example of this would be Amazon’s Certificate Manager for EC2-based projects.
-
Use
django.middleware.security.SecurityMiddleware
,django 1.11+
The tool of choice for projects on Django 1.11+ for enforcing HTTPS/SSL across an entire site through middleware is built right in. To activate this middleware just follow these steps:
1 Add django.middleware.security.SecurityMiddleware to the settings.MIDDLEWARE_CLASSES definition. 2 Set settings.SECURE_SSL_HOST to True .
重定向到 HTTPS
1 web server setting
2 Django middleware
-
WARNING:
django.middleware.security.SecurityMiddleware
Does Not Includestatic/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
使用安全的cookie ,(只有在 HTTPS下使用)
Your site should inform the target browser to never send cookies unless via HTTPS.
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Use HTTP Strict Transport Security (
HSTS
) 使用 HTTP 严格传输安全协议
HSTS 可以在 Web 服务器级上设置,也可以在 Django 中设置(通过 settings.SECURE_HSTS_SECONDS
)
值得注意的是,HSTS是一个单向头,一旦设置成了 N 秒,无法通知浏览器进行重置。因此不要将 HSTS 的 max-age 值设置成超出你的可维护范围
当开启 HSTS 后,你的网页会包含一个 HTTP 头指示那些支持 HSTS 的浏览器只能通过安全连接进行访问:
浏览器会将 HTTP 连接重定向到 HTTPS
如果无法进行安全连接(比如证书是自签名的或已过期),会抛出错误消息并禁止继续访问
一个 HSTS 应答头可能如下:
Strict-Transport-Security: max-age=31536000; includeSubDomains
HSTS 配置的一些建议:
尽可能使用 HSTS 的 includeSubDomains,它能防止通过非安全子域名向你的域名写 cookie 等的相关攻击
初次部署时将 max-age 值设小一些,如 3600 (1 小时)。因为一旦设置后就无法重置。
一旦确信网站已经没有问题了,再将 max-age 值设置大一点,如 31536000 (12 个月) 或 63072000 (24 个人)。
要注意的是,一旦 HSTS 使用了 includeSubDomains,所有的子域名都要使用 HTTPS,而且无法修改回使用 HTTP。
HTTPS Configuration Tools 配置工具
Mozilla 提供了一个 SSL 配置生产工具 SSL Configuration Generator
设置好后,可能使用 Qualys SSL Labs 的测试工具 进行测试,看看我们配置的有多好,最好拿到 A+ 分
使用Allowed Hosts
Validation
In production, you must set ALLOWED_HOSTS
in your settings to a list of allowed host/domain
names in order to avoid raising SuspiciousOperation
exceptions.
修改数据的表单一定要开启 CSRF 保护 Always Use CSRF Protection With HTTP Forms That Modify Data
避免跨站脚本攻击 XSS
Django by default escapes<, >, ’, ”,
and &, which is all that is needed for proper HTML escaping
XSS attacks usually occur when users enter malignant JavaScript that is then rendered into a template directly(输入恶意的JS, 渲染模板到浏览器)
Tips of Django security team:
-
Use
format_html
Overmark_safe
Django gives developers the ability to mark content strings as safe, meaning that Django’s own safeguards are taken away. A better alternative is django.utils.html.format_html , which is like Python’s str.format() method, except designed for building up HTML fragments. All args and kwargs are escaped before being passed to str.format() which then combines the elements.
-
禁止用户修改 HTML 标签上的属性
-
返回给 JavaScript 使用的数据先进行 JSON 编码 Use JSON Encoding for Data Consumed by JavaScript
-
Add Content Security Policy Headers
CSP
开发者明确告诉客户端(制定比较严格的策略和规则),哪些外部资源是可以加载和执行的 ,即使攻击者发现漏洞,但是它是没办法注入脚本的
Also known as CSP, Content Security Policy provides a standard method to declare approved origins of content that browsers should be allowed to load on a website. Covered types are JavaScript, CSS, HTML frames, web workers, fonts, images, embeddable objects such as Java applets, ActiveX, audio and video files, and other HTML5 features. Even with django-csp, implementing this isn’t trivial as it requires standing up a reporting service or using a third-party service
防御对 Python 代码注入攻击
python内置可以执行代码的函数
eval()
,exec()
,execfile()
如果你在项目中允许将任意字符串或文件传入到这些函数,就会有安全漏洞。Eval really is dangerous
不要直接使用
pickle
反序列化Never unpickle data received from an untrusted or unauthenticated source.
当使用
PyYAML
时,只使用yaml.safe_load()
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()
避免使用基于 cookie 的会话
Be Careful With Cookie-Based Session
- 1 正常情况下 Django sites使用
database- or cache-based sessions
storing a hashed random value
in a cookie
which is used as a key
to find the real session value
, which is stored in the database or cache
- 2 Django sites 也支持
built using cookie-based sessions
, which place the session data entirely on the client’s machine
这种会话有几下问题:
It is possible for users to read the contents of cookie-based sessions.
用户有可能会看到会话的内容
If an attacker gains access to a project’s SECRET_KEY and your session serializer is JSONbased,
they gain the ability to falsify session data and therefore, if authentication is used, impersonate any user
如果攻击者获知了项目的 SECRET_KEY,并且你的会话数据是基于 JSON 的,就可能被破解来伪造登录
If an attacker gains access to a project’s SECRET_KEY and your session serializer is picklebased,
they gain the ability to not only falsify session data and also execute arbitrary code. In
other words, not only can they assume new rights and privileges, they can also upload working
Python code. If you are using pickle-based sessions or are considering using them, please read
the tip below
如果攻击者获知了项目的 SECRET_KEY,并且你的会话数据是基于 pickle 的,破解后不仅能伪造登录,而且可以上传任意可执行代码
Another disadvantage of this configuration is that sessions can’t be invalidated in a guaranteed
way (except when they expire): you can try to override the cookie in the browser with a new
value, but you can’t enforce an attacker to use it: if they continue sending requests with the old
cookie, the session backend won’t know the difference
这种会话不能确保使其失效。攻击者可以一直使用旧会话数据。
Use JSON for Cookie-Based Sessions
The default cookie serializer for Django is JSON-based, meaning that even if an attacker
discovers a project’s SECRET_KEY , they can’t execute arbitrary code. If you decide to
write your own cookie serializer, stick to using JSON as the format.
Never, ever use the optional pickle serializer.
所有进入 Django 表单的数据都要验证 Validate All Incoming Data With Django Forms
禁止支付相关表单项上的自动填充功能 Disable the Autocomplete on Payment Fields
包括信息卡、CVV、PIN 等。因为用户可能会在公共电脑上输入。
实现代码如下:
from django import forms
class SpecialForm(forms.Form):
my_secret = forms.CharField(
widget=forms.TextInput(attrs={'autocomplete': 'off'}))
对于会在公共场合要填入的,考虑将输入项修改成 PasswordInput:
from django import forms
class SecretInPublicForm(forms.Form):
my_secret = forms.CharField(widget=forms.PasswordInput())
小心处理用户上传的文件 Handle User-Uploaded Files Carefully
要完全安全地处理只能通过一个完全独立的域名进行处理。或者将上传的文件保存到 CDN 中。
If you must allow upload and download of arbitrary file types 确保服务器头要设置 Content-Disposition: attachment
来避免浏览器会自动解析显示这些内容。
-
当无法用 CDN 时
确保上传的文件保存到一个无法被执行的目录中。并且要将上传文件的后缀限制到一份白名单中。
-
Django 与用户上传的文件
Django 有两个项能允许用户上传文件: FileField
和 ImageField
。
如果只接受特定格式的文件类型,尽可能确保上传的就是这些类型:
使用 python-magic 库检查上传的文件头
使用专门针对该类型文件的 Python 库进行验证。例如 Django 的 ImageField 源码中就是用 PIL 库来验证的
使用 defusedxml 而不是 Python 的内置 XML 库或 lxml
自定义的验证器这里不管用,因为它们是在项内容通过 to_python() 方法转化成 Python 对象后再进行验证的。
不要使用 ModelForms.Meta.exclude
Don’t Use ModelForms.Meta.exclude
When using ModelForms, always use Meta.fields
. Never use Meta.exclude
.
The use of Meta.exclude is considered a grave security risk, specifically a Mass Assignment Vulnerability.
We can’t stress this strongly enough. Don’t do it.
# 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.
# DON'T DO THIS!
from django import forms
from .models import Store
class StoreForm(forms.ModelForm):
class Meta:
model = Store
# DON'T DO THIS: Implicit definition of fields.
# Too easy to make mistakes!
excludes = ("pk", "slug", "modified", "created", "owner")
from django import forms
from .models import Store
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",
)
Don’t Use ModelForms.Meta.fields = ”__all__”
This includes every model field in your model form. It’s a shortcut, and a dangerous one
小心 SQL 注入攻击 Beware of SQL Injection Attacks
在使用原生SQL时要注意
➤ The .raw() ORM method.
➤ The .extra() ORM method.
➤ Directly accessing the database cursor.
不要保存信用卡数据 Never Store Credit Card Data
推荐使用第三方服务如 Stripe、Braintree、Adyen、PayPal
等,然后将它们整合到项目中。
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.
如果要评估一个开源的电子商务解决方式
,先了解下它是否将支付相关信息存放在了数据库中
,如果是,那就换用其它的吧。
Read the Source Code of Open Source E-Commerce Solutions
If you are planning to use any of the existing open source Django e-commerce solutions,
examine how the solution handles payments. If credit card data is being stored in the database,
even encrypted, then please use another solution.
监测你的网站
定期检查网站的访问和错误日志。安装监测工具进行定义检查。
保持依赖包更新 Keep Your Dependencies Up-to-Date
automatically checks requirements files against the latest versions that
PyPI provides.
防止点击劫持 Prevent Clickjacking
使用 defusedxml
来避免 XML Bomb 攻击 Guard Against XML Bombing With defusedxml
尝试使用双因子认证 Explore Two-Factor Authentication
Two-factor authentication (2FA) requires users to authenticate by combining two separate means of identification.
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.
通常是密码 + 手机短信
。它要求用户有手机与手机网络。但是 基于时间的一次性密码算法 TOTP 则没有这个限制,该算法在 Google 等公司的双因子认证中广泛使用。
相关资料:
Multi factor authentication django-two-factor-auth
使用 SecurityMiddleware Embrace SecurityMiddleware
Django 1.8+
内置的 django.middleware.security.SecurityMiddleware
已经实现了 django-secure 包的大部分功能。
强制使用强密码 Force the Use of Strong Password
一个强密码包含不同大小写的字符、数字、标点符号等。
Quality | Password Specification |
---|---|
Bad | 6-10 characters of just the alphabet |
Okay | Minimum 8 characters, mixed case + numeric + special characters |
Better | Minimum 30 characters of just the alphabet |
Best | Minimum 30 characters, mixed case + numeric + special characters |
对网站进行安全检查
这种安全检查不是安全审计,可以通过 ponycheckup.co 进行。
Erik Romijn, on the Django security team, has created Pony Checkup (ponycheckup.com), an
automated security checkup tool for Django websites. There are several security practices that can
easily be probed from the outside, and this is what his site checks for.
使用 UUID 对主键进行混淆 Never Display Sequential Primary Keys
import uuid as uuid_lib
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class IceCreamPayment(models.Model):
uuid = models.UUIDField(
db_index=True,
default=uuid_lib.uuid4,
editable=False)
def __str__(self):
return str(self.pk)
>>> from payments import IceCreamPayment
>>> payment = IceCreamPayment()
>>> IceCreamPayment.objects.get(id=payment.id)
<IceCreamPayment: 1>
>>> payment.uuid
UUID('0b0fb68e-5b06-44af-845a-01b6df5e0967')
>>> IceCreamPayment.objects.get(uuid=payment.uuid)
<IceCreamPayment: 1>
The Dangers of Obfuscating Sequential IDs
Slugs and UUIDs both have their disadvantages. The slug-based approach runs into collisions quickly, causing things like, “vanilla”, “vanilla-2”, “vanilla-3” to occur. UUIDs, to put
it simply, are long and not memorizable by most humans. What can we do?
You can obfuscate the sequential ID. But we don’t recommend it. Why not?
The short answer: Obfuscating is not an effective way to hide sequential IDs.
The long answer: There are any number of methods for obfuscating numbers ranging from
base64 encoding to using the hashids library. These approaches work by converting a
number to a alphanumeric code and back again. They not only hide the number, they
also shorten it. Sounds great, right?
The problem is that every method of obfuscating sequential IDs is fundamentally
insecure. Base64 encoding is trivial to undo. Libraries like hashids can be broken
with brute-forceattacks or by anyone with a good understanding of cryographic
knowledge ( carnage.github.io/2015/08/cryptanalysis-of-hashids).
To summarize: If you want to hide your sequential identifiers, don’t rely on obfuscation