Web开发者不会注意到由 “AJAX(Asynchronous JavaScript And XML)”所带来的激情。不费力气就能创建像Google Suggest那样的智能网站或者像Gmail那样基于Web的应用程序,这在很大程度上要归功于这种技术。然而,伴随着AJAX应用程序的发展,我们发现了它的一些不足之处,我们发现它的安全漏洞也在逐渐变大,就像慢慢地把基于AJAX的站点放入了一颗定时炸弹中。
AJAX的好处
在当年“Web应用程序”的美好时代,事情非常简单。你填写了一个表单,点击“提交”按钮,然后当前屏幕就消失了,等待一小会儿后你就转入到了下一个页面。今天的状况已经不是这样的了,用户需要的是一种就像任何桌面应用程序那样流畅、快捷和人性化的Web体验。
AJAX经常是和DHTML(Dynamic HTML)一起协作的,它的顺利执行需要允许网页中的JavaScript代码和web服务器在后台无缝通讯。比方说,当你开始在Google Suggest的搜索框中输入东西时,web页面就和服务器在后台开始交换数据,然后会给出一些你可能需要的词条等。所有的这一切都不需要页面刷新或者按下任何按钮。同样这也就是像Gmail那样的应用程序怎么能对实时拼写检查做的那么好的原因。
AJAX怎样工作
AJAX复杂的原理已经超出了今天所要阐述的范围,这里只简单描述一下。你的页面上的JavaScript代码能够在不依赖于用户的情况下和你的Web服务器取得联系。这里面起核心作用的就是JavaScript的XMLHttpRequest对象,这个对象能够被就像用户敲击键盘或者时钟事件在后台或者异步触发(也就是术语异步JavaScript和XML)。
如果你在Google Suggest中输入“ajax”后,就会得到像我输入后得到的服务器请求一样:
1. www.google.com/complete/search?hl=en&js=true&qu=aj
2. www.google.com/complete/search?hl=en&js=true&qu=aja
3. www.google.com/complete/search?hl=en&js=true&qu=ajax
在这个术语中的XML部分有一点会引起人们的误解,其实这一部分是没有任何意义的。它是从JavaScript对象得来的名字,同时许多 AJAX风格的应用程序使用了XML,这个对象能够就任何事务向服务器发出一个请求。甚至JavaScript代码本身也能够被取回和评估。继续完成我的输入“ajax example”,将会从Google的服务器产生下面的回应:
sendRPCDone(frameElement, “ajax example”, new Array(”ajax example”, “ajax examples”), new Array(”153,000 results”, “177,000 results”), new Array(”"));
这将会给你一些关于强大的AJAX的暗示吧,它具有在运行中(on the fly)把新的JavaScript代码加入到浏览器中的能力。然而,最优化的方法看起来好像束缚了XML协定。举个例子说明一下,比如Google产生了下面的这个东西:
ajax example
153,000
ajax examples
177,000
显然,你可以在一个合适的表单中解释这些XML数据,但我们要感谢JavaScript,它确实能够在一些非常典型的限制条件下和大量讨厌的IE bug环境里非常好的处理XML对象。
为了帮助你理解一些Ajax的问题,我在这里给你介绍一个假想的旅行公司-“时代尖端旅行公司”。由于受到AJAX bug的推动,他们的主要web开发者Max Uptime为了创建一个这样的应用程序,他决定混合使用AJAX,这样,他走在时代尖端了。
AJAX的问题
半数以上的AJAX安全风险来自隐含在服务器中的漏洞。显然,使用安全编码技术的好的设计,对于更安全的AJAX大有帮助,我们需要感谢Max 熟悉开放万维网应用安全计划(the Open Web Application Security Project – OWASP)排名前十的最严重web应用程序安全漏洞列表(www.owasp.org)。不幸的是,当Max实现AJAX的时候,他仍然需要面对许多额外的因素:
1.新的技术:如果Max想把他的站点连接到一个SQL数据库,他在Google查到了数百万的例子。AJAX技术,不管这种技术有多年轻,它仍然是出现在采购循环中相对较早的,尽管它只有很少一部分好的例子出现在网络上。为了解决一些难处理的和不必要的复杂问题,这就要求像Max那样的开发者去自主开发。Max也就不得不编写服务器端和客户端的代码,创建他自己不太确定的协议(特别是对服务器响应来讲)。不管这些协议有多好,都将会及时表现在页面上。
2.非传统方式的设计:AJAX有一点点不同于传统设计方式,因为这样的应用程序是半客户端半服务端的。在Max的例子里,他是唯一的开发者,所以他为服务端和客户端都能够进行编码。在同一时间使用两种不同的语言(特别是在早期阶段)进行开发将会产生一些初级的编码错误,因为他要在两端来回跳跃,对一端来讲非常好,但可能在另一端不能够胜任。即使Max有一个大的开发团队,安全编码责任也可能在服务端和客户端开发小组之间代码移交的时候发生问题。
3.太多的脚本语言:Max凭借他自己的聪明才智决定建立世界上最优秀的旅行登记工具。你从输入你现在的位置(通过邮政编码、电话区号或者 GPS等等)开始登记,这时候一个AJAX请求就会被立即发送来确定你确切的位置。从那时候开始,屏幕上就会填入你所有可以利用的旅行方式,这一切甚至都是在你决定你想要去什么地方、你打算什么时候动身和你打算和谁一同去之前就完成的。
这个屏幕上的单元格和控件都充满了AJAX驱动,服务器端和客户端的脚本可能需要超过20个不同的服务器调用。你可以想像一个很小的个体服务器程序,比如findairportsbylocation.aspx 或者 determinemaxbaggageallowancebyairline.php.
显而易见,如果没有Max的仔细计划(比如创建多功能的“重载”JavaScript函数和服务器脚本),每一次设计他都需要创建超过40个独立的部分。更多的编程意味着会产生更多的错误和bug,意味着需要更多的时间去编写、管理、测试和更新代码。不仅如此,因为在客户端的 JavaScript代码中应用了大量的这种脚本,他们在正式的程序测试中也容易变得很健忘。
4.确定小部分的AJAX不会引起危害:这个站点是一个计划出行的站点,但是Max考虑的是它将立刻为你提供一个显示精确位置的卫星视图,并且把你所要到达目的地的天气情况也提供给你。AJAX最大的诱惑之一看起来好像是直到最后一刻它还在进行其它的操作,就像一个讲解员在那里解说一样,为了 AJAX使用了AJAX。当Max开始尝试他的新想法时,他会逐渐尝试增加更多新的功能,完全忽视测试的需要。
5.不安全的通讯:每一个AJAX调用可能只回传很少数量的数据给客户端,但那些数据是私有的、保密的。Max可以编写一个便利的工具来对你的信用卡号码进行数字校验,但是如果使用纯文本代替over SSL进行发送数据会怎样呢?这是一个显而易见的问题,但是当有许多例行程序需要考虑,特别是屏幕上其它99%的数据不是真正的机密数据时,很容易就会忽视掉SSL的。
6.服务器端访问控制:使用JavaScript程序来触发AJAX经常会掩饰一些显而易见的编码错误,服务器端访问控制就是一个例子。假设Max想参考你上次游览的一个详细目的地来为你提供你中意的旅馆,他可能会是像下面这样:
showprevioushotels.aspx?userid=12345&destination=UK
这当然是非常好的,但是如果一个恶意用户把URL改成了如下所示该怎么办呢:
showprevioushotels.aspx?userid=12346&destination=%
他们会得到其他人最喜爱的旅馆吗?(注意:%在SQL语句中是通配符)。无疑,这是一个没有什么危害的例子,但是Max应该使用 session、cookie或者其它符号形式来确保数据能并且只能发送到正确的用户那里。它们可能仅仅是数据的一小部分,但它们可能就是最重要的一小部分。
7.服务器端验证:实际上这里有两个问题。第一,AJAX控制经常被用来在用户最后提交到服务器之前的输入验证。这麻痹了Max,使Max有一种虚假的安全感,原因是他建立了称作alloweddestinations.php的函数,根据用户的ID来决定他们能够到达的正确目的地。
因为这是一个服务器端的检查,当这个页面最后被提交的时候他不必再次为在服务器上做检查而烦恼,这里我们假定不会有恶意的用户暗中破坏从alloweddestinations.php的响应或者破坏对服务器最后的请求。
AJAX控制可以比用户自己更仔细验证用户的输入,但是他们还是经常在服务器上最后做一次验证。
AJAX验证的第二个问题就是控制本身会受到验证漏洞的影响。这里再次强调一下,URL通常是隐藏着的,所以也会经常忘掉它。举例说明一下,也许我可以使用SQL Injection来对刚才的脚本进行攻击,如下所示:
showprevioushostels.aspx?userid=’; update users set type=’admin’ where userid=12345;–
就会让我登录后具有系统管理员的
django工程的布局
<repository_root>/
<django_project_root>/
<configuration_root>/
Top level:Repository Root
最上层是工程根目录的绝对路径
这里还经常有的文件是:
README
doc/
design/
.gitignore
requirement.txt
Second level:Django project root
这里通常是由django-admin.py startproject 命令产生的
这里还包括:
configuration_root
media/
static/
templates/
各种apps 的文件夹
Third level:configuration root
也是由 django-admin.py startproject 产生的
包括:
urls.py
settings.py等
简单的布局类似是这样:
icratings_project/
.gitignore
Makefile
docs/
requirements.txt
icratings/
manage.py
media/
products/
profiles/
ratings/
static/
templates/
icratings/
__init__.py
settings/
urls.py
wsgi.py
不需要把 virtualenv的内容放到版本控制中去,只要把需要的信息放到requirement.txt中就行了
chapter 4 django基本的app设计
app设计的黄金法则:
Write programs that do one thing and do it well.
功能专一并且松耦合
一个工程中的实用app案例:
我们建立一个商店,使用twoscoops_project这个工程目录
这个Django project has:
•A flavors app to track all of our ice cream %avors and list them on our website.•A blog app for the o fficial Two Scoops blog.
•An events app to display listings of our shop's events on our website:events such as Strawberry Sundae Sundays and Fudgy First Fridays.
在将来可能增加的功能:
*A shop app to allow us to sell pints by mail order.
*A tickets app, which would handle ticket sales for premium all-you-can-eat icecream fests.
由于tickets这个功能很多应用可能不会用到,而且会有比较复杂的逻辑,所以我们单独分出一个app
怎么给app命名呢?
*当个单词,容易理解和维护 animals , blog, polls等
*有时还要考虑url的因素,适当调节名称
保持app比较小巧
chapter 5 配置和所需的文件
好的实践经验:
*所有的配置文件需要版本控制
*DRY原则,相同配置继承即可
开发环境的配置不要在版本控制中,本地需要配置的debug等和生产环境区别很大。
使用多配置请参看 http://www.slideshare.net/jacobian/the-best-and-worst-of-django
建立settings模块来处理不同配置的使用
settings/
__init__.py
base.py
local.py
staging.py
test.py
production.py
使用时候可以这样使用:
$ django-admin.py runserver --settings=twoscoops.settings.local
一个开发环境的配置案例:
DEBUG = True
TEMPLATE_DEBUG = DEBUG
EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'twoscoops',
'USER': '',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '',
}
}
INSTALLED_APPS += ('debug_toolbar', )
INTERNAL_IPS = ('127.0.0.1',)
MIDDLEWARE_CLASSES += \
('debug_toolbar.middleware.DebugToolbarMiddleware', )
使用方法:$ django-admin.py runserver --settings=twoscoops.settings.local
使用环境变量来保存密码
requirement也是用类似的模块处理不同环境下不同开发人员的不同配置
Modem,其实是Modulator(调制器)与Demodulator(解调器)的简称,中文称为调制解调器(港台称 之为数据机)。根据Modem的谐音,亲昵地称之为“猫”。 所谓调制,就是把数字信号转换成电话线上传输的模拟信号;解调,即把模拟信号转换成数字信号。 合称调制解调器。 调制解调器的英文是MODEM,它的作用是模拟信号和数字信号的“翻译员”。电子信号分两种,一种 是"模拟信号",一种是"数字信号"。我们使用的电话线路传输的是模拟信号,而PC机之间传输的是数字 信号。所以当你想通过电话线把自己的电脑连入Internet时,就必须使用调制解调器来"翻译"两种不同的 信号。连入Internet后,当PC机向Internet发送信息时,由于电话线传输的是模拟信号,所以必须要用调 制解调器来把数字信号"翻译"成模拟信号,才能传送到Internet上,这个过程叫做"调制"。当PC机从 Internet获取信息时,由于通过电话线从Internet传来的信息都是模拟信号,所以PC机想要看懂它们,还 必须借助调制解调器这个“翻译”,这个过程叫作“解调”。总的来说就称为“调制解调”。 1. modem是上网拨号器,要上网必须通过它。 2. 路由器安装在modem之后,代替人工拨号上网的过程,并且通过路由器可以组建局 域网,比如你家里有一根宽带线但有两台电脑要上网就需要路由器了。 3. 路由器和无线路由器都是可以插网线的,无线路由器主要是可以和无线网卡通讯, 实现无线上网,前提是你的电脑要有无线网卡。 4. modem和路由器的连接关系是:宽带接入线接入modem,路由器用网线连接到 modem,电脑通过网线或无线来接到路由器。 5.150M或者300M中的M指的是信号强度,当然是越大越好,也越贵。 6. 综上所述,如果家用超过一个电脑有上网需求的两个都需要,而modem是必须的。 modem一般宽带供应商给的就够了,路由器现在市面上的TPLink比较实惠,家用足够了