python几大web开发框架
Flask——微型框架
官网原文
练习:[HCTF 2018]admin
-
跨站脚本攻击( XSS )
跨站脚本攻击是指在一个网站的环境中注入恶任意的 HTML (包括附带的 JavaScript )。要防防御这种攻击,开发者需要正确地转义文本,使其不能包含恶 意的 HTML 标记。更多的相关信息请参维基百科上在文章: 跨站脚本 。
在 Flask 中,除非显式指明不转义, Jinja2 会自动转义所有值。这样可以排除所有模板导致的 XSS 问题,但是其它地方仍需小心:
>不使用 Jinja2 生成 HTML 。 >在用户提交的数据上调用了 Markup 。 >发送上传的 HTML ,永远不要这么做,使用` Content-Disposition: attachment 头部`来避免这个问题。 >发送上传的文本文件。一些浏览器基于文件开头几个字节来猜测文件的 `content-type` ,用户可以利用这个漏洞来欺骗浏览器,通过伪装文本文件来执 行 HTML 。
另一件非常重要的漏洞是不用引号包裹的属性值。虽然 Jinja2 可以通过转义 HTML 来保护你免受 XSS 问题,但是仍无法避免一种情况:属性注入的 XSS 。为了免受这 种攻击,必须确保在属性中使用 Jinja 表达式时,始终用单引号或双引号包裹:
<input value="{{ value }}">
。因为如果不这么做,攻击者可以轻易地注入自制的 JavaScript 处理器。例如一个攻击者可以注入以下 HTML+JavaScript 代码:onmouseover=alert(document.cookie)
当用户鼠标停放在这个输入框上时,会在警告窗口里显示 cookie 信息。一个精明的 攻击者可能还会执行其它的 JavaScript 代码,而不是把 cookie 显示给用户。结合 CSS 注入,攻击者甚至可以把元素填满整个页面,这样用户把鼠标停放在页面上的任 何地方都会触发攻击。
有一类 XSS 问题 Jinja 的转义无法阻止。 a 标记的 href 属性可以包含 一个 javascript: URI 。如果没有正确保护,那么当点击它时浏览器将执行其代码。
<a href="{{ value }}">click here</a>
<a href="javascript:alert('unsafe');">click here</a>
为了防止发生这种问题,需要设置 Content Security Policy (CSP) 响应头部。 -
跨站请求伪造( CSRF )
如果你的验证信息存储在 cookie 中,那么你就使用了隐式的状态管理。“已登入” 这个状态由一个 cookie 控制,并且这个 cookie 在页面的每个请求中都会发送。不幸的是,在第三方站点发送的请求中也会发送这个 cookie 。如果你不注意这点,一 些人可能会通过社交引擎来欺骗应用的用户在不知情的状态下做一些蠢事。
假设你有一个特定的 URL ,当你发送 POST 请求时会删除一个用户的资料(例 如 http://example.com/user/delete ) 。如果一个攻击者现在创造一个页面 并通过页面中的 JavaScript 发送这个 post 请求,只要诱骗用户加载该页面,那么 用户的资料就会被删除。
设象在有数百万的并发用户的 Facebook 上,某人放出一些小猫图片的链接。当用户 访问那个页面欣赏毛茸茸的小猫图片时,他们的资料就被删除了。
那么如何预防这个问题呢?基本思路是:对于每个要求修改服务器内容的请求,应该 使用一次性令牌,并存储在 cookie 里, 并且 在发送表单数据的同时附上它。 在服务器再次接收数据之后,需要比较两个令牌,并确保它们相等。
Flask 没有做这件事。因为这应该是表单验证框架做的事,而 Flask 不包括表单验证。
-
JSON 安全
Flask 0.10 版和更低版本中, jsonify() 没序列化顶层数组为 JSON 。这是因为 ECMAScript 4 存在安全漏洞。
ECMAScript 5 关闭了这个漏洞,所以只有非常老的浏览器仍然脆弱,而且还有其他更严重的漏洞 。 因此,这个行为被改变了,并且 jsonify() 现在支持了序列化数据。
-
安全头部
为了控件安全性,浏览器识别多种头部。我们推荐检查应用所使用的以下每种头部。 Flask-Talisman 扩展可用于管理 HTTPS 和安全头部。
-
HTTP Strict Transport Security (HSTS)
告诉浏览器把所有 HTTP 请求转化为 HTTPS ,以防止 man-in-the-middle (MITM) 攻击。
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
-
Content Security Policy (CSP)
告诉浏览器哪里可以加载各种资源。这个头部应当尽可能使用,但是需要为网站定义 正确的政策。一个非常严格的政策是:
response.headers['Content-Security-Policy'] = "default-src 'self'"
https://csp.withgoogle.com/docs/index.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
-
X-Content-Type-Options
强制浏览器遵守内容类型而不是尝试检测它,这可以会被滥用,以生成一个跨站脚本 ( XSS )攻击。
response.headers['X-Content-Type-Options'] = 'nosniff'
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
-
X-Frame-Options
防止外部网站把你的站点嵌入到 iframe 中。这样可以防止外部框架点击转化针 对你的页面元素的隐藏点击,也称为“点击支持”。
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
-
X-XSS-Protection
如果请求包含类似于 JavaScript 的东西且响应的内容包含相同的数据时,浏览器将 尝试通过不加载页面来防止反射的 XSS 攻击。:
response.headers['X-XSS-Protection'] = '1; mode=block'
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
-
Set-Cookie 选项
这些选项可以被添加到一个 Set-Cookie 头部以增强其安全性。 Flask 具有将 其配置于会话 cookie 上的配置选项。它们也可以配置在其他 cookie 上。
>Secure 限制 cookies 仅用于 HTTPS 流量。 > HttpOnly 保护 cookies 内容不被 JavaScript 读取。 > SameSite 限制如何从外部网站通过请求发送 cookie 。可以设置为 'Lax' (推荐)或者 'Strict' 。 Lax 防止从外部网站通过有 CSRF 倾向请求(比如一个表单)发送 cookie 。 Strict 防止通过所有外部请求发 送 cookie ,包括常规连接。
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax')
指定 Expires 或者 Max-Age 选项后,将会分别在给定时间后或者当前时间 加上所定义存活期后删除 cookie 。如果两个参数都没有指定,则会在关闭浏览器时删除。
# cookie expires after 10 minutes
response.set_cookie('snakes', '3', max_age=600)
对于会话 cookie 来说,如果 session.permanent 被设置了,那么
PERMANENT_SESSION_LIFETIME
会被用于设置有效期。 Flask 的缺省 cookie 实现会验证加密签名不会超过这个值。降低这个值有助于 缓解重播攻击,可以在稍后发送被拦截的 cookie 。app.config.update(
PERMANENT_SESSION_LIFETIME=600
)
@app.route('/login', methods=['POST'])
def login():
...
session.clear()
session['user_id'] = user.id
session.permanent = True
...
使用 itsdangerous.TimedSerializer 来签名和验证其他 cookie 值( 或者其他任何需要安全签名的值)。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
-
HTTP Public Key Pinning (HPKP)
告诉浏览器只使用指定的证书密钥进行服务器验证,以防止 MITM 攻击。
启用后小心,如果密钥设置或者升级不正确则难以撤消。https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning
-
-
复制/粘贴到终端
隐藏字符,例如退格字符( \b 、 ^H )可以 导致文本的 HTML 渲染结果与 粘贴到终端 的结果不 同。
例如,import y\bose\bm\bi\bt\be\b
在 HTML 中渲染为import yosemite
,但是当粘贴到终端时,因为退格字符的作用,会变成import os
如果您预计用户会从您的站点复制和粘贴不受信任的代码,例如从技术博客上的 用户评论中复制代码,那么请考虑增加额外的过滤,例如替换所有 \b 字符:
body = body.replace("\b", "")
大多数现代终端会在粘贴时警告并删除隐藏字符,所以这不是绝对必需的。同时 也会存在无法过滤的其他方式的危险命令。根据您网站的用途不同,一般最好显示关于代码复制的警告。
Tornado——异步框架
官网原文
练习:[护网杯 2018]easy_tornado
-
Cookies 和 secure cookies
使用 set_cookie 方法在用户的浏览器中设置 cookies:
class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_cookie("mycookie"): self.set_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!")
Cookies 是不安全的而且很容易被客户端修改. 如果你通过设置 cookies 来 识别当前登陆的用户, 你需要利用签名来防止 cookies 被伪造. Tornado 利用 set_secure_cookie 和 get_secure_cookie 方法来对 cookies签名. 为了使用这些方法, 你需要在创建应用程序时指定一个叫做 cookie_secret 的密匙. 你可以在应用程序的设置中通过传递参数来注册密匙:
application = tornado.web.Application([ (r"/", MainHandler), ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
对 cookies 签名后就有确定的编码后的值, 还有时间戳和一个
HMAC
. 如果 cookes 过期或者签名不匹配,get_secure_cookie
将返回 None 就如同这个 cookie 没有被设置一样. 这是一个安全版本的例子:class MainHandler(tornado.web.RequestHandler): def get(self): if not self.get_secure_cookie("mycookie"): self.set_secure_cookie("mycookie", "myvalue") self.write("Your cookie was not set yet!") else: self.write("Your cookie was set!")
Tornado 的 secure cookies 保证完整性但不保证保密性. 就是说, cookie 将不会被修改, 但是它会让用户看到. cookie_secret 是一个对称密钥, 所以它必须被保护起来 – 任何一个人得到密钥的值就将会制造一个签名的 cookie.
默认情况下, Tornado 的 secure cookies 将会在 30 天后过期. 如果要修改这个值, 使用
expires_days
关键词参数传递给set_secure_cookie
和max_age_days
参数传递给get_secure_cookie.
这两个值的传递是相互独立的, 你可能会在大多数情况下会使用一个 30 天内合法的密匙, 但是对某些敏感操作 (例如修改账单信息) 你可以使用一个较小的max_age_days
.Tornado 支持多个签名的密匙, 这样可以使用密匙轮换. 这样
cookie_secret
必须是一个具有整数作为密匙版本的字典. 当前正在使用的签名密匙版本必须在应用程序中被设置为key_version
如果一个正确的密匙版本在 cookie 中被设置, 密匙字典中的其它密匙也可以被用来作为 cookie 的签名认证, 为了实现 cookie 的更新, 可以在get_secure_cookie_key_version
中查询当前的密匙版本. -
用户认证
当前通过认证的用户在请求处理器的
self.current_user
当中, 而且还存在于模版中的 current_user. 默认情况下, current_user 的值为 None.为了在你的应用程序中实现用户认证, 你需要覆盖请求控制器中的
get_current_user()
方法 来确认怎样获取当前登陆的用户, 例如, 从 cookie 的值中获取该信息. 下面这个例子展示了通过用户的昵称来确定用户身份, 值被保存在 cookies 中:class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): return self.get_secure_cookie("user") class MainHandler(BaseHandler): def get(self): if not self.current_user: self.redirect("/login") return name = tornado.escape.xhtml_escape(self.current_user) self.write("Hello, " + name) class LoginHandler(BaseHandler): def get(self): self.write('<html><body><form action="/login" method="post">' 'Name: <input type="text" name="name">' '<input type="submit" value="Sign in">' '</form></body></html>') def post(self): self.set_secure_cookie("user", self.get_argument("name")) self.redirect("/") application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
你可以使用
Python 装饰器 (decorator) tornado.web.authenticated
来获取登陆的用户. 如果你的方法被这个装饰器所修饰, 若是当前的用户没有登陆, 则用户会被重定向到 login_url (在应用程序设置中). 上面的例子也可以这样写:class MainHandler(BaseHandler): @tornado.web.authenticated def get(self): name = tornado.escape.xhtml_escape(self.current_user) self.write("Hello, " + name) settings = { "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", "login_url": "/login", } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
如果 post() 方法被 authenticated 修饰, 而且用户还没有登陆, 这时服务器会产生一个 403 错误.
1@authenticated
描述符仅仅是精简版的if not self.current_user: self.redirect()
, 而且可能对于非浏览器的登陆者是不适用的.
点击Tornado Blog example application
来查看一个完整的用户认证程序 (将用户的数据保存在 MySQL 数据库中). -
第三方认证
tornado.auth
模块既实现了认证, 而且还支持许多知名网站的认证协议, 这其中包括Google/Gmail
,Facebook
,Twitter
, 和FriendFeed
. 模块内包含了通过这些网站登陆用户的方法, 并在允许的情况下访问该网站的服务. 例如, 下载用户的地址薄或者在允许的情况下发布一条 Twitter 信息.这里有一个 Google 身份认证的例子, 在 cookie 中保存 Google 的认证信息用来进行后续的操作:
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler, tornado.auth.GoogleOAuth2Mixin): @tornado.gen.coroutine def get(self): if self.get_argument('code', False): user = yield self.get_authenticated_user( redirect_uri='http://your.site.com/auth/google', code=self.get_argument('code')) # Save the user with e.g. set_secure_cookie else: yield self.authorize_redirect( redirect_uri='http://your.site.com/auth/google', client_id=self.settings['google_oauth']['key'], scope=['profile', 'email'], response_type='code', extra_params={'approval_prompt': 'auto'})
-
跨站请求伪造防护
跨站请求伪造(Cross-site request forgery), XSRF, 是一个 web 应用程序要面临的常规问题 .
一个普遍被接受的防护 XSRF 做法是让每一个用户 cookie 都保存不可预测的值, 然后把那个值通过 form 额外的提交到你的站点. 如果 cookie 和 form 中提交的值不匹配, 那么请求很有可能是伪造的.
Tornado 内置有 XSRF 保护. 你需要在应用程序中设置
xsrf_cookies
:settings = { "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", "login_url": "/login", "xsrf_cookies": True, } application = tornado.web.Application([ (r"/", MainHandler), (r"/login", LoginHandler), ], **settings)
如果设置了 xsrf_cookies , Tornado web 应用程序将会为每一个用户设置一个 _xsrf cookie 来拒绝所有与 _xsrf 的值不匹配的
POST
,PUT
, 和DELETE
请求. 如果你将此设置打开, 你必须给每个通过 POST 提交表单中添加这个字段. 你可以通过特殊的UIModule xsrf_form_html()
来实现这些, 在模版中是可用的:<form action="/new_message" method="post"> {% module xsrf_form_html() %} <input type="text" name="message"/> <input type="submit" value="Post"/> </form>
如果你提交一个
AJAX POST
请求, 你的每次请求需要在你的 JavaScript 中添加一个_xsrf
的值. 这是一个我们在 FriendFeed 中用到的一个通过AJAX POST
方法来自动添加 _xsrf 值的 jQuery 函数:function getCookie(name) { var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); return r ? r[1] : undefined; } jQuery.postJSON = function(url, args, callback) { args._xsrf = getCookie("_xsrf"); $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST", success: function(response) { callback(eval("(" + response + ")")); }}); };
对于 PUT 和 DELETE 请求 (除了不像 POST 请求用到 form 编码参数), XSRF token 会通过 HTTP 首部中的
X-XSRFToken
字段来传输.XSRF cookie
在xsrf_form_html
被使用时设置, 但是在一个非通常形式的 纯 JavaScript 应用程序中, 你可能需要手动设置self.xsrf_token
(仅通过读取这个属性就足以有效设置 cookie 了).如果你需要对每一个基本的控制器自定义 XSRF 行为, 你一个覆盖
RequestHandler.check_xsrf_cookie().
例如, 如果你有一个不是通过 cookie 来认证的 API, 你可能需要让check_xsrf_cookie()
不做任何事来禁用 XSRF 的保护功能. 然而, 如果你既支持 cookie 认证又支持 非基于 cookie 的认证, 这样当前请求通过 cookie 认证的 XSRF 保护就会十分的重要.
Django——全栈框架
-
跨站脚本(XSS)保护
XSS 攻击允许用户将客户端脚本注入其他用户的浏览器。这通常是通过将恶意脚本存储在数据库中来实现的,在那里它会被检索并显示给其他用户,或者让用户点击一个链接,这将导致攻击者的 JavaScript 被用户的浏览器执行。然而,XSS 攻击可能源自任何不受信任的数据源,例如 cookie 或 Web 服务,只要数据在包含在页面中之前没有得到充分的净化。
使用 Django 模板可以保护免受大多数 XSS 攻击。但是,了解它提供的保护及其局限性很重要。
Django 模板会转义对 HTML 特别危险的特定字符。虽然这可以保护用户免受大多数恶意输入的侵害,但并非完全万无一失。例如,它不会保护以下内容:
<style class={{ var }}>...</style>
如果 var 设置为“
class1 onmouseover=javascript:func()
”,这可能会导致未经授权的 JavaScript 执行,具体取决于浏览器呈现不完美 HTML 的方式。 (引用属性值可以解决这种情况。)在将 is_safe 与自定义模板标签、安全模板标签、mark_safe 以及 autoescape 关闭时使用时要特别小心也很重要。
此外,如果使用模板系统输出 HTML 以外的内容,则可能会有完全独立的字符和单词需要转义。
在数据库中存储 HTML 时也应该非常小心,尤其是在检索和显示该 HTML 时。
-
跨站请求伪造(CSRF)保护
CSRF 攻击允许恶意用户在其他用户不知情或不同意的情况下使用该用户的凭据执行操作。
Django 具有针对大多数类型的 CSRF 攻击的内置保护,前提是您已在适当的情况下启用和使用它。但是,与任何缓解技术一样,也存在局限性。例如,可以全局或针对特定视图禁用 CSRF 模块。只有当您知道自己在做什么时,才应该这样做。如果您的站点具有不受您控制的子域,则还有其他限制。
CSRF 保护通过检查每个 POST 请求中的秘密来工作。这可确保恶意用户无法“重播”表单 POST 到您的网站,并让其他登录用户在不知不觉中提交该表单。恶意用户必须知道用户特定的秘密(使用 cookie)。
使用 HTTPS 部署时,
CsrfViewMiddleware
将检查 HTTP 引用标头是否设置为同一来源的 URL(包括子域和端口)。由于 HTTPS 提供了额外的安全性,因此必须通过转发不安全的连接请求并为受支持的浏览器使用 HSTS 来确保连接使用 HTTPS 可用。除非绝对必要,否则在使用 csrf_exempt 装饰器标记视图时要非常小心。
-
SQL注入保护
SQL 注入是一种攻击,恶意用户能够在数据库上执行任意 SQL 代码。这可能导致记录被删除或数据泄露。
Django 的查询集不受 SQL 注入的影响,因为它们的查询是使用查询参数化构造的。查询的 SQL 代码与查询的参数分开定义。由于参数可能是用户提供的,因此不安全,它们被底层数据库驱动程序转义。
Django 还赋予开发人员编写原始查询或执行自定义 sql 的能力。这些功能应该谨慎使用,并且您应该始终小心正确地转义用户可以控制的任何参数。此外,在使用 extra() 和 RawSQL 时应该小心。
-
点击劫持保护
点击劫持是一种攻击,其中恶意站点将另一个站点包装在框架中。这种攻击可能会导致毫无戒心的用户被诱骗在目标站点上执行意外操作。
Django 包含
X-Frame-Options 中间件
形式的点击劫持保护,它在支持的浏览器中可以防止站点在框架内呈现。可以在每个视图的基础上禁用保护或配置发送的确切标头值。强烈建议将中间件用于不需要第三方站点将其页面包装在框架中的任何站点,或者只需要允许站点的一小部分。
-
SSL/HTTPS
将站点部署在 HTTPS 之后总是更好的安全性。如果没有这一点,恶意网络用户就有可能嗅探身份验证凭据或客户端和服务器之间传输的任何其他信息,并且在某些情况下(活跃的网络攻击者)可能会更改以任一方向发送的数据。
如果您想要 HTTPS 提供的保护,并在您的服务器上启用它,您可能需要一些额外的步骤:
>如有必要,设置 SECURE_PROXY_SSL_HEADER,确保已彻底理解那里的警告。不这样做可能会导致 CSRF 漏洞,并且不正确执行也可能是危险的! >将 SECURE_SSL_REDIRECT 设置为 True,以便将 HTTP 上的请求重定向到 HTTPS。 >注意 SECURE_PROXY_SSL_HEADER 下的警告。对于反向代理,将主 Web 服务器配置为重定向到 HTTPS 可能更容易或更安全。 >使用“安全”cookie。
如果浏览器最初通过 HTTP(大多数浏览器的默认设置)进行连接,则现有 cookie 可能会泄露。因此,应该将 SESSION_COOKIE_SECURE 和 CSRF_COOKIE_SECURE 设置设为 True。这会指示浏览器仅通过 HTTPS 连接发送这些 cookie。请注意,这意味着会话将无法通过 HTTP 工作,并且 CSRF 保护将阻止通过 HTTP 接受任何 POST 数据(如果您将所有 HTTP 流量重定向到 HTTPS,这会很好)。
使用 HTTP 严格传输安全 (HSTS):HSTS 是一个 HTTP 标头,它通知浏览器以后与特定站点的所有连接都应始终使用 HTTPS。与通过 HTTP 将请求重定向到 HTTPS 相结合,这将确保连接始终享有 SSL 的附加安全性,前提是发生了一个成功的连接。 HSTS 可以使用
SECURE_HSTS_SECONDS、SECURE_HSTS_INCLUDE_SUBDOMAINS 和 SECURE_HSTS_PRELOAD
进行配置,也可以在Web 服务器
上进行配置。 -
主机头验证
在某些情况下,Django 使用客户端提供的 Host 标头来构造 URL。虽然对这些值进行了清理以防止跨站点脚本攻击,但伪造的主机值可用于跨站点请求伪造、缓存中毒攻击和电子邮件中的链接中毒。
因为即使看似安全的 Web 服务器配置也容易受到假主机头的影响,Django 会根据
django.http.HttpRequest.get_host()
方法中的ALLOWED_HOSTS
设置验证主机头。此验证仅适用于get_host()
;如果您的代码直接从request.META
访问 Host 标头,将绕过此安全保护。警告:本文档的先前版本建议配置 Web 服务器以确保它验证传入的 HTTP 主机标头。虽然这仍然是推荐的,但在许多常见的 Web 服务器中,似乎验证 Host 标头的配置实际上可能不会这样做。例如,即使 Apache 配置为从设置了
ServerName
的非默认虚拟主机为您的 Django 站点提供服务,HTTP 请求仍有可能匹配该虚拟主机并提供假主机标头。因此,Django 现在要求您显式设置ALLOWED_HOSTS
而不是依赖 Web 服务器配置。如果配置需要,Django 要求您显式启用对X-Forwarded-Host
标头的支持(通过USE_X_FORWARDED_HOST
设置)。 -
推荐人政策
浏览器使用 Referer 标头作为向站点发送有关用户如何到达那里的信息的一种方式。
通过设置 Referrer 策略,您可以帮助保护用户的隐私,限制在何种情况下设置 Referer 标头。有关详细信息,请参阅安全中间件参考的引用策略部分。
-
会话安全
类似于 CSRF 限制要求部署站点,以便不受信任的用户无法访问任何子域,django.contrib.sessions 也有限制。有关详细信息,请参阅有关安全性的会话主题指南部分。 -
用户上传的内容
-
如果站点接受文件上传,建议在 Web 服务器配置中将上传限制为合理的大小,以防止拒绝服务 (DOS) 攻击。在 Apache 中,这可以使用 LimitRequestBody 指令轻松设置。
-
如果正在提供自己的静态文件,请确保禁用像 Apache 的 mod_php 这样的处理程序,它会将静态文件作为代码执行。
-
当以不遵循安全最佳实践的方式提供媒体时,Django 的媒体上传处理会带来一些漏洞。具体来说,如果 HTML 文件包含有效的 PNG 标头和恶意 HTML,则该文件可以作为图像上传。该文件将通过 Django 用于 ImageField 图像处理 (Pillow) 的库的验证。当此文件随后显示给用户时,它可能会显示为 HTML,具体取决于 Web 服务器的类型和配置。
-
框架级别不存在防弹技术解决方案来安全验证所有用户上传的文件内容,但是,您可以采取一些其他步骤来缓解这些攻击:
- 通过始终为来自不同顶级或二级域的用户上传内容提供服务,可以防止一类攻击。这可以防止任何被同源策略保护(例如跨站点脚本)阻止的漏洞利用。例如,如果您的站点在 example.com 上运行,您可能希望从 usercontent-example.com 之类的内容提供上传的内容(MEDIA_URL 设置)。仅提供来自 usercontent.example.com 等子域的内容是不够的。
- 应用程序可以选择为用户上传的文件定义一个允许的文件扩展名列表,并将 Web 服务器配置为仅提供此类文件。
-
-
其他安全主题
虽然 Django 提供了良好的开箱即用的安全保护,但正确部署应用程序并利用 Web 服务器、操作系统和其他组件的安全保护仍然很重要。
- 确保您的 Python 代码在 Web 服务器的根目录之外。这将确保您的 Python 代码不会意外作为纯文本(或意外执行)。
- 小心处理任何用户上传的文件。
- Django 不会限制对用户进行身份验证的请求。为了防止对身份验证系统的暴力攻击,可以考虑部署 Django 插件或 Web 服务器模块来限制这些请求。
- 将 SECRET_KEY 保密。
- 使用防火墙限制缓存系统和数据库的可访问性是一个好主意。