通译第二十章:保险
来源:互联网 发布:淘宝女童装店铺最火 编辑:程序博客网 时间:2024/05/01 16:07
The Django Book:第20章 保险
internet是令人惶恐的地方
在从前的几年里,internet恐惧故事差一点继续出现在新闻中,我们看到病毒以惊人的速度传播,大量危及保险的电脑被应
做武器,从不完了的武备与垃圾邮件作战,以及好多从危及保险的网站甄别偷窃的报导
作为好的web开发人员,打击这些黑暗权势是我们的责任,每个web开发人员急需把保险作为根本的web编程方面,不幸的是,
保险问题看上去很缠手--攻击者只需要找出一个独自的弱项,但是防御者务须保护每个独自的方面
Django尝试减免这个难题,它设计来源于动为你防范好多常见的生手(甚至有心得的)web开发人员简单犯的保险差错,懂得这些
问题是什么依然很主要,Django怎的保护你,以及--更主要的--你让你的代码更保险的方法
但是,第一,一个主要的不应诺:我们决不是这个领域的专家,之所以我们不会尝试全面的解释每个弱项,相反,我们将付出适
合Django的保险问题的一个简短的纲要
web保险的正题
如其你只从本章学习到一件事儿,则让它为这个:
从未有过--在任何情况下--信赖浏览器的数据
你从没知道在HTTP联接的另一面是谁,它可能是一个你的用户,但是它也或者很简略为一个找寻破绽的攻击者或金莲本
来自于浏览器的任何门类的数据急需被当做是偏执狂的一幅健康良药,它保护"in band"的数据--即从web表单交付的数据--
和"out of band"--即HTTP脑袋,cookies,以及其余请求信息,蒙骗一般浏览器自动平添的请求元数据是很不足挂齿的
本章议论的每个弱项都直接来自于信赖来自于线上的数据其后在运用它事前驱除数据失败,你应当让不停的问"数据来自于何
处?"变成正常实践
SQL打针
SQL打针是一个常见的开辟,攻击者改变Web页面参数(比如GET/POST数据或URLs)来安插纯真的Web程序直接在它的数据库实施
的随便的SQL片断,这可能是在猖獗全世界里最风险的--不幸的是它是最常见的--弱项
这个弱项最简略出现在当用户手动输入构造化SQL时,比如,设想写一个步骤来从一个联系搜寻页面征集联系信息列表,为了
防止在你的系统里读取每个独自的邮件时碰到垃圾邮件,我们将逼迫用户在我们提供他们的email地址前输入某人的用户名:
Java代码
一.def user_contacts(request):
2. user = request.GET['username']
3. sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
4. # execute the SQL here...
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
# execute the SQL here...
注意,这个例证中,以及下部全部相仿的"不用做这个"的例证中,我们有意保存了多数用以让步骤真个工作的代码,我们
不会让这些代码在某人偶尔取走它们时工作
虽然起始这看上去不风险,但它真的是这么
第一,保护我们的整个邮件列表的尝试将以一个聪明的构造化查询失败,思忖如若一个攻击者输入"' OR 'a'='a"到查询框里
这种情况下,字符串插补将构建的查询将为:
Java代码
一.SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
因为我们在该字符串里容许不安全的SQL,攻击者平添的OR子句准保每个独自的行都回来
尽管这样,这是最小的引起惊慌的进攻,设想一下子如若攻击者交付"'; DELETE FROM user_contacts WHERE 'a' = 'a"将产生
什么,我们将失去这个完整的查询:
Java代码
一.SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';
SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';
呀!我们的联系列表哪里去了?
解决方案
诚然这个问题很阴毒并且有时候很难发现,解决方案却很简单:从未信任命户交付的数据,而且当传送给SQL时一直escape它
Django数据库API为你做这个,它依据你应用的数据库服务器(比如PostgreSQL,MySQL)的引号常例自动escape全部非一般的SQL
参数,比如,在这个API调用中:
Java代码
一.foo.get_list(bar__exact="' OR 一=一")
foo.get_list(bar__exact="' OR 一=一")
Django将呼应的escape输入,结果是像这么的话语:
Java代码
一.SELECT * FROM foos WHERE bar = '/' OR 一=一'
SELECT * FROM foos WHERE bar = '/' OR 一=一'
这是完全无害的
这合适全部的Django数据库API,含有一些额外状况:
一,extra()步骤的where参数(参照附录XXX),该参数设计时接受原始SQL
二,运用初级数据库API来手动开展查询
至于此中每种景况,很简单让你自个儿受保护,每种情况下,避免字符串插补有益于传接"绑定参数",即,这一部分我们开始的
事例应当被写成:
Java代码
一.from django.db import connection
2.
三.def user_contacts(request):
4. user = request.GET['username']
5. sql = "SELECT * FROM user_contacts WHERE username = %s;"
6. cursor = connection.cursor()
7. cursor.execute(sql, [user])
8. # ... do something with the results
from django.db import connection
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = %s;"
cursor = connection.cursor()
cursor.execute(sql, [user])
# ... do something with the results
初级execute步骤应用SQL字符串和%s placeholders,而且自动escape和安插作为第二个参数传送的列表参数,你应当一直以
这种形式构建自定义的SQL
不幸的是,你不能在SQL的每个地方应用绑定参数,它们不允许作为标识符(即表名也许列名),这么,如其你急需,如从一个
POST变量动态构建表的列表,你将亟需在你的代码里escape该名字,Django提供了一个步骤django.db.backend.quote_name
它将依据现阶段数据库的引号scheme来escape标识符
跨站点脚本(XSS)
很可能最常见的web弱项,跨站点脚本,可能XSS,在点染到HTML事先失败于准确的escape用户交付的内容的web程序里发现
这容许攻击者恶意的安插随便的通常是script标签格式的HTML
攻击者正常施用XSS进攻来窃取cookie和session信息,或许骗取用户提供私有信息给差错的人(也叫phishing)
这类别型的攻打可以采取一些不同的方式,并且有差一点无限的改变方式,之所以我们将只见见一个典型的事例,让我们见见1
个十分简略的"hello world"视图:
Java代码
一.def say_hello(request):
2. name = request.GET.get('name', 'world')
3. return render_to_response("hello.html", {"name" : name})
def say_hello(request):
name = request.GET.get('name', 'world')
return render_to_response("hello.html", {"name" : name})
这个视图简略的从GET参数读取一个名字并传接名字给hello.html模板,我们或许像这么为该视图写一个模板:
Java代码
1.Hello, {{ name }}!
Hello, {{ name }}!
之所以如若我们访问http://example.com/hello/name=Jacob,点染的页面将包孕:
Java代码
1.Hello, Jacob!
Hello, Jacob!
但是之类--如若我们访问
Java代码
一.http://example.com/hello/name=Jacob
http://example.com/hello/name=Jacob
会发作什么?
则我们会失去:
Java代码
1.Hello, Jacob!
Hello, Jacob!
当然,攻击者不会应用像i标签的货色,他可以包孕整个HTML集来用随意内容截取你的页面,这品种型的进攻被用以蒙骗用户
输入数据到看上去像它们的银行网站,但是实际上是把你的帐号信息发送给攻击者的XSS-截取表单
如其你把储存该数据在数据库中而且后头在你的站点上展示则会更糟
比如,在某点上MySpace被发现关于这品种型的XSS攻打有弱项,用户安插javascript到他确当你访问他的profile页面时自动
增添他为你的友人的profile里,几天以内他有了几百万的友人
现时,这或许听起来很好,但是记住该攻击者让他的代码--而不是MySpace的--运作在你的计算机里,这违背了至于信赖MySpa
ce上边的全部代码都是实质上由MySpace所写的假想
MySpace十分幸运这些恶意的代码没自动剔除访问者的帐号,改动他们的密码,用垃圾邮件浸没站点,或许其余任何该弱项
开释的噩梦般的情景
的情景
解决方案
解决方案十分简略:一直escape任何也许来自于用户的内容,如其我们像这么简略的重写我们的模板:
Java代码
1.Hello, {{ name|escape }}!
Hello, {{ name|escape }}!
则我们不再易受攻击了,你应当当在你的站点上展示用户交付的内容时一直应用escape标签(也许一个相像物)
为啥Django不为你做这些?
批改Django来源于动escape全部展示在模板中的变量是一个频繁出现在Django开发人员邮件列表中的议论正题
目前为止,Django的模板避免了这种举动,由于它锐敏而不足见的更动了应当很直接的举动(展示变量),这是个狡猾的问题
和一个很难评估的平衡,增添掩藏的举动与Django的核心思念相左(以及Python的,至于这种问题),但是保险等同主要
然则,也存在公道的机遇使得Django在将来平添某种方式的自动escape(或许差点儿自动escape)举动,它将一直比此书更新
(特别是终极树版本)
就算Django平添了这个特点,你应当依然一直保有思考"该数据从哪儿来?"的习气,没一直100%保护你的站点免受XSS强攻
的自动解决方案
跨站点请求仿冒(CSRF)
CSRF应恶意网站蒙骗用户未知的从一个他们已经认证的站点载入一个URL时产生--这么,就可以运用他们的认证状态
Django由内劲攻打来防范这品种型的攻打,进攻自身和那些工具在第15章进行了胪陈
Session仿冒/截取
这是一个非一般的攻打,而不是对用户的session数据的正常部类的强攻,它可以有一些不同的模式:
一,中间人强攻,内中攻击者当它在有线(或许无线)网络上流走时偷听session数据
二,Session仿冒,内中攻击者应用仿冒的session ID(或者经过中间人强攻取得)来假装为除此以外一个用户
这前两种的事例是在咖啡馆的攻击者运用无线网络来获取一个session cookie,其后他可以应用这个cookie来仿照原始用户
三,cookie仿冒攻打,内中攻击者覆盖储存在cookie中的假想只读的数据,第12章详细解释了cookies怎么工作,此中一个突
出点是对浏览器和恶意用户在你不知情的情况下改动cookies是微乎其微的
网站储存类似于IsLoggedIn=一可能甚或LoggedInAsUser=jacob的cookie有很长的历史,开辟这类别型的攻击者太简单了
但是关于在更微妙的级别,信赖任何储存在cookie中的货色从未有过是个好主意,你从未有过知道谁在翻找它们
四,Session定置,内中攻击者蒙骗用户设立或许重设他们的session ID
比如,PHP容许session标识符在URL中传接(即http://example.com/?PHPSESSID=fa90197ca25f六ab40bb1374c510d七a32),蒙骗
用户点击一个硬编码了session ID的链接的攻击者将招致用户采取该session
这被用在phishing攻打中来蒙骗用户输入个人信息到攻击者全部的帐号,它可以稍后登录该帐号并失去那些数据
五,Session放毒,内中攻击者打针潜在风险的数据到用户的session中--一般经过一个用户交付来设立session数据的web表单
一个轨范的例证是站点在cookie中储存简略的用户爱好(比如页面背景色彩),攻击者可以蒙骗用户点击一个联接来交付一个
实质上包孕XSS强攻的"色彩",如若这个色彩没escape(参照上边的)用户可能再度打针狠毒的代码到用户环境
解决方案
有一些可以防止蒙受这些强攻的正常准则:
一,从未允许session信息包孕在URL中
Django的session构架(参照第12章)容易的不允许session包孕在URL中
二,不用在cookies中直接储存数据,相反,储存投射到储存在后端的session数据的session ID
如若你施用Django内建的session构架(即request.session),它可以自动为你处置,session构架施用的独一的cookie是一个
独自的session ID,全部的session数据储存在数据库中
三,如若你在模板中展示session数据记得escape它,参照上头的XSS一部分,而且记得它适宜任何用户创造的内容,你应当把
session信息当做用户创设的
四,防备任何也许的攻击者窃取session IDs
固然几乎不可能检测到某人在窃取session ID,Django确实有内建的强力的session进攻的防范,Session IDs储存为哈希(而
不是延续的数目字),这防止了强力攻打,而且如若用户尝试一个不存在的sessino ID时用户将一直失去一个新的session ID,
这防止了session定置
注意这些准则和工具中没一个防止了中间人强攻,这品种型的强攻差一点没法检测,如其你的站点容许登录用户看到一些种
型的敏感数据,你应当一纵贯过HTTPS来服务站点,并且,如若你有一个容许SSL的站点,你应当设立SESSION_COOKIE_SECURE
设立为True,这将使Django只透过HTTPS发送session cookie
E-mail脑袋打针
SQL打针的很少有人晓得的姊妹e-mail脑袋打针窃取email发送web表单并运用它们来发送垃圾邮件,任何从web表奇数据构建
email脑袋的模式都是这品种型的强攻
让我们见见轨范的好多站点的联系人表单,正常它email一个硬编码的email地址,之所以第一眼由此看来没垃圾邮件乱用的强攻
尽管这样,绝大多数的这种表单也容许用户输入他自各儿的email正题(再有一个发送地址,有时一些其余域),这个正题域被
用于构建email信息的正题脑袋
如若当构建email信息时脑袋没escape,攻击者可以应用类似于"hello/ncc:spamvictim@example.com"(这边/n是换行字符)
这将使得构建的email脑袋成为:
Java代码
一.To: hardcoded@example.com
二.Subject: hello
三.cc: spamvictim@example.com
To: hardcoded@example.com
Subject: hello
cc: spamvictim@example.com
和SQL打针同样,如其我们信赖用户给定的正题行,我们将容许他后见一些恶意的脑袋,则它们可以应用我们的联系表单来
发送垃圾邮件
解决方案
我们可以用我们防备SQL打针一样的形式来防止这种攻打:一直escape或许证验用户交付的内容
Django内建的mail步骤(座落django.core.mail)容易的不允许用来构建脑袋(发送和接受地址以及正题)的任何域中有换行
如若你尝试施用django.core.mail.send_mail和一个包孕换行的正题,Django将触发BadHeaderError异常
如若你决议运用发送email的其余步骤,你将亟需确认脑袋的换行罗致疏失可能被驱除,你或者想检察django.core.mail中的
SafeMIMEText种来见见Django怎的作这件事
索引穿梭
索引穿梭使另一个打针作派的进攻,此中恶意的用户蒙骗文件系统代码来读和/或写web服务器应当不允许访问的资料
一个例证或许为一个从硬盘读资料而不扫除文件名的视图:
Java代码
一.def dump_file(request):
2. filename = request.GET["filename"]
3. filename = os.path.join(BASE_PATH, filename)
4. content = open(filename).read()
5.
6. # ...
def dump_file(request):
filename = request.GET["filename"]
filename = os.path.join(BASE_PATH, filename)
content = open(filename).read()
# ...
诚然它看上去限制了资料访问为访问BASE_PATH(经过施用os.path.join)下头的资料,如其攻击者传送一个包孕..(这是两个
句点,UNIX对"父索引"的近道)的filename,他可以访问BASE_PATH"以上"的资料,他发现准确数量的小数点来顺利访问只是
时间问题,例如../../../../../etc/passwd
读取资料而不正确的escape的货色至于此问题是易受攻击的,写资料的视图只是易受攻击,但结果成倍可怕
另一个该问题的改变坐落基于URL也许其余请求信息动态载入模块的代码中,一个鼓吹很好的事例来自于Ruby on Rails全世界
在2006中叶先期,Rails应用类似于http://example.com/person/poke/一的URLs来直接载入模块和调用步骤,结果是心细机关
的URL也许自动载入任何的代码,包括一个数据库重置脚本!
解决方案
如其你的代码急需基于用户输入读写资料,你亟需十分留神的扫除请求途径来准保攻击者不能从你限制访问的根本索引逃出
注意,不需要说,你应当从未写可以读取硬盘任何位置的代码
怎么干这个escape的美例证座落Django内建的静态内容服务视图(座落django.views.static),这里是相干的代码:
Java代码
一.import os
二.import posixpath
3.
4.# ...
5.
六.path = posixpath.normpath(urllib.unquote(path))
七.newpath = ''
八.for part in path.split('/'):
9. if not part:
10. # strip empty path components
11. continue
12.
13. drive, part = os.path.splitdrive(part)
14. head, part = os.path.split(part)
15. if part in (os.curdir, os.pardir):
16. # strip '.' amd '..' in path
17. continue
18.
19. newpath = os.path.join(newpath, part).replace('//', '/')
import os
import posixpath
# ...
path = posixpath.normpath(urllib.unquote(path))
newpath = ''
for part in path.split('/'):
if not part:
# strip empty path components
continue
drive, part = os.path.splitdrive(part)
head, part = os.path.split(part)
if part in (os.curdir, os.pardir):
# strip '.' amd '..' in path
continue
newpath = os.path.join(newpath, part).replace('//', '/')
Django自身不读资料(除非你施用static.serve步骤,但是它被上边展示的代码保护),之所以这个弱项不会影响核心代码很多
除此而外,运用URL配备抽象意味Django将从未载入你没展示告诉它载入的代码,没创办一个URL来以致Django载入没在
URL配备里谈到的货色的模式
暴露疏失信息
在开发阶段,可以在你的浏览器里看到堆栈和失误信息是是非非素有用的,Django有不一般让调试简略的十分"漂亮"和丰富的调试
信息
尽管这样,万一站点上线的话如若这些错处还展示,它们有时能无心的直露相助攻击者的你的代码也许配备的一些方面
并且,差错和堆栈信息对终极用户基本没有用场,如其你点代码触发了不可处置的异常,站点访问者应当不能看到完整的
堆栈信息--可能任何代码片断也许Python(面临程序员的)疏失信息,相反,访问者应当看到友善的"该页面不可得到"信息
当然,天然开发者急需看到堆栈信息来在他们的代码中调试问题,之所以构架应当从大众掩藏全部的疏失信息,但是它应当
展示他们给受信赖的站点开发人员
解决方案
Django有一个容易的标记来统制这些错处新的展示,如若DEBUG设立被设为True,错误信息将展示在浏览器中,要不然Django
将点染回到一个HTTP500("内部服务器差错")信息并点染一个你提供的差错模板,这个差错模板被称作500.html,而且应当
坐落一个你的模板索引的源目录
既是开发人员依然亟需看到上线站点生成的错误信息,至于任何这种模式处置的差错将把完整的堆栈信息发送email给在
ADMINS设立中给定的任何方址
在Apache和mod_python下面署的用户应当也确认他们在他们的Apache配置文件里设立了PythonDebug Off,这将保准任何在
Django无机会载入先期产生的差错都将不会展示给大众
最后一句话
希望所有这些对于保险问题的研讨不会太有胁从感,是这么,web可以是一个猖獗和横蛮的全世界,但是经过一丁点的高见,你
可以有一个难以置信的安全网站
记住web保险是一个不停改变的领域,如若你在阅览此书的终极树版本,准保稽查更多更新的关于已发现的新弱项的保险资源
实际上,每个月也许每星期费用一些时间来研究和护持现阶段状态的web程序保险一直是个好主意,这是小小的的投资,但是你得
到对你的站点和用户的保护是无价的
我们缺失了什么东西?有一些你以为我们应该在本章讲到的其余保险弱项?我们有一些错处?在本段留下诠释来让我们晓得!
本文来源:
我的异常网
Java Exception
- 通译第二十章:保险
- 通译之第十九章:国际化
- 通译之第十八章:自定义Django的admin界面
- 通译www.djangobook.com之第二十一章:调度Django
- 第二十章
- 保险
- 保险
- 保险
- 保险
- 保险
- 梦里人第二十章
- 第二十章 XML
- 第二十章、WWW 伺服器
- 第二章第二十题
- 第二章第二十题
- 第二章第二十题
- 第三章 第二十题
- 第四章第二十题
- 通译之第十八章:自定义Django的admin界面
- sql 设置外键
- 通译之第十九章:国际化
- html两个元素中间的空白解决办法
- div中的float与clear
- 通译第二十章:保险
- 通译www.djangobook.com之第二十一章:调度Django
- 无
- 《蜗居》,高房价招安了这一代年轻人
- 个人收藏 different between CRM_ORDER_READ and CRM_ORDER_READ_OW
- Javascript跨域访问解决方案【转帖】
- java jdk 中文乱码
- netbeans字符集问题
- JSP标记符号