创建安全的Web应用程序

HTML,ASP,JSP,PHP    2009-05-03 07:47  

  把一个问题说清楚,是要有前提的,也要大家有一些通过的词汇,就像大家谈OO,就知道谈的面向对象,以及面向对象的一些特征;懂设计模式的,一听到"观察者",就立马知道什么意思。

  一.下面看看安全的一些概念:

  首先,我们来看看什么是安全性?

  我们常常提起“实现安全性”“创建安全的系统”。所以安全性一般是定义很多,如:安全性就是保证系统可以完全按照我们想要的方式运行;安全性就是防止以我们不希望的方式运行系统...定义很多,但是不知道大家有没有发现,我们理解的安全的定义很狭窄的。怎么说?我们一般认为安全就是这样的:采取一定的措施(主要是编程代码的措施)处理程序运行时的意外,或者防止意外发生。但是安全的问题不仅仅只是代码的问题,所以在安全编程方面,我们要考虑的更多。

  什么是安全

  我们不能保证一个系统绝对的安全,不可能做到100%的安全。安全的定义的受到很多的限制,首先我们来看看一个例子。

  我们都用过银行的ATM机,如果有人捡到了我们的卡想要盗取我们的钱,那么他只有猜我们的密码。如果密码的长度只有一位数,那么捡卡人第一次猜对的概率就是1/10,他只要十次就可以盗取我们的钱(假设可以不限次数的猜),那么系统的安全行很差;如果密码是2位,那么密码就有100中可能,那么捡卡人第一次才对的几率就是1/100,第二次就是1/99,第三次1/98...系统的安全行也很差.

  如果把密码的尝试限制在3次,如果密码为2位数,那么密码被猜出的几率就大大的减小了:

  第一次猜中的几率就是1/100

  第二次猜中(第一次没有中)的几率就是(99/100) * (1/99)=0.01

  第三次猜中(前两次不中)的几率就是(99/100)*(98/99)*(1/98)=0.01

  三次之内猜中的几率就是:0.01+1.01+0.01=0.03

  所以,当我们限制了尝试的次数的时候,系统就比之前安全了一些,但是系统还并不是很安全,只能说比之前稍微的好了点,但是风险依然很大。

  为了使得系统更加的安全,就要减小系统的密码被猜出的可能行,我们可以从两个方面着手:

  1.使得密码尝试的次数减小,如用户只能输入一次密码;

  2.加大密码的位数,如6位。这样密码被猜出的几率就更加的小了,风险也小了很多,系统的安全行就又提高了。所以,我们常说安全不安全的,都只是一个相对的概念。说了这么多,就是要说一点:不要把安全看死了,安全不安全要看我们的环境。

  在谈后面的话题之前,让我们来共享一些术语:

  脆弱性是系统的一个特征,它可能会使得应用系统不安全按照我们预想的方式运行。一般表示系统不好的特征。

  威胁就是指利用错人性破环系统安全的可能行。

  利用就是利用脆弱行的方法。

  总结一句话就是:脆弱行导致了威胁,利用则实现了威胁,简言之,攻击。

  二、Web应用中的安全问题

  首先我们就看看对Web的攻击。攻击有很多种的,其中一部分可以使用ASP.NET代码进行防范的,但是其他的攻击方式还是可以产生破坏的,如直接攻击服务器。下面就来看典型的例子。

  我们之前说过,攻击就是利用系统的脆弱性以实现一定的威胁。攻击的结果也很多,如:

  未经授权的访问--用户获取了更多的权限,从而可以将应用程序用于其他的途径,如果获取了网站的管理员的密码,散布政治言论。

  代码执行--在目标系统上运行恶意代码,而且还会导致其他的威胁,如木马。

  拒绝服务--合法用户被禁止访问应用程序

  信息失窃--机密的信息被盗取

  破坏信息--信息遭到修改。如,站点被涂改,发布攻击性的消息和政治言论。

  下面我们就来看看常见的一些脆弱性,以及对它们的利用,以及引起的威胁。

  缓冲区溢出

  这个问题由来已久,而且到现在为止,也是Web应用种最常被利用的脆弱性。

  当应用程序的外部输入没有经过检查就被插入内存的时候,就会存在缓冲区溢出的脆弱性。如果插入的长度超过了内存中为此分配的空间的长度,输入就溢出,可能将占据内存中的其他的地方,甚至运行恶意的代码。

  对缓冲区溢出的主要利用就是把附加的数据写到内存中缓冲区的其他地方,这样就常常导致程序的崩溃,因为内存破坏了,这也是拒绝服务器的攻击方式,如果附加的数据设计的很巧妙的话,附加的数据还可以重写函数的返回地址,那么程序就按照攻击者的意愿执行,病毒,木马就是这样。

  当然,在c++中,这个问题很常见,因为C++可以直接操作内存地址,进行很底层的操作。但是在.NET中是否也有这个问题?

  因为.net是基于托管代码的,也就是说.net的代码不是直接操作内存,而是在中间隔了一层CLR。托管代码的执行要靠CLR来为止作为边界检查,所以CLR中的任何脆弱性都将转变为应用程序的脆弱性。如果有高手知道了CLR的问题,那么,托管代码也出问题。

  脚本注入和跨站脚本攻击

  任何时候我们都要有这个心理:用户都是恶意的。所以我们不能信任用户的任何输入,在用户输入的时候一定要检验。假如没有正确的处理好用户的输入,就可能在程序中引入脚本注入的脆弱性。该脆弱性允许用户将自己额脚本注入到数据中,如在用户留言中,用户插入" ",那么我们的留言的页面就中是弹出提示。

  跨站脚本的攻击一般表现为一个在URL参数中带有客户端的脚本。这些脚本用来盗取用户的cookie信息等,

  我们这里只是简单的说下,后面的一些文章还会具体的谈,以及解决方案。

  SQL注入

  相信这个问题,大家或多或少都知道一些,主要是恶意的用户在我们的程序的数据库中执行精心设计的SQL语句。而且威胁很大,设置可以获取服务器的管理员的权限。

  分布式拒绝服务

  也称为DDOS(Disrtibute Denial Of Service)。DDOS攻击主要就是用大量的计算机攻击一个系统。很多的计算机联合起来就可以发送很多的虚假的请求,以至于被攻击的系统超负荷,而不能向其他的用户提供服务。

  蓄意工具者为了发动DDOS,就必须获取足够多的机器。恶意的用户设计在别人的电脑上注入木马和病毒,获取机器的控制权,"借"别人的电脑发送攻击。被控制的电脑就是所谓的"僵尸"。

  DDOS攻击一般来攻击服务器,而且攻击的方式也是防不胜防,很多的防护软件和防火墙不能区分正确的请求和虚假的请求。

  人的问题

  有时候,被利用的脆弱性不是技术上的脆弱性,而是人的脆弱性。如果用户没有安全的意识,就容易受骗,而为攻击者打开系统的。方式很多,如用Email欺骗用户,诱使用户执行一些程序,还有就是蠕虫...

  蛮力攻击

  如果不采取一定的措施防止用户无休止的尝试连接应用程序,那么我们就容易受到不计其数的猜测密码口令的攻击,即蛮力攻击。

  攻击的方式主要就是设计一个程序,用它向目标应用发送很多的请求以测试不同的密码口令。

  有一点要注意:考虑安全问题的时候,我们常常把程序比作为一个城堡,在城堡的周围建造城墙并且严格盘查各个通道。保护Web程序与此类似,但是,如果这样,那么我们对于已经进入城堡的用户就无计可施了。

  三、安全由谁来负责

  很多时候安全问题不仅仅只是程序员的责任,而是在系统设计的时候就开始考虑的问题,而且系统的安全也是由很多的人来负责的,如:

  程序设计者要保证程序结构是安全的

  网络管理员要保证网络和服务器是安全的

  程序员要保证代码不会引入脆弱性

  数据库管理员确保数据库服务器没有脆弱性

  用户也不要受欺骗

  当然,还有其他的很多任务要做好,才能实现安全。

  我们都是开发人员,下面就来看看我们开发人员不能解决的问题:

  网络

  ASP.NET程序运行要依附于网络,如果网络连接中断,我们没有办法。

  Web服务器和数据库服务器

  如果服务器本身不安全,那么我们的代码写的再好也徒劳。

  客户端

  我们终究能控制客户端,而且很多的恶意用户甚至可以绕过我们的客户端的javascript验证,要记住用户都是恶意的.

  我们可以解决的问题:

  把安全放在第一位

  构建并维护具有一定安全等级的系统,是重要而又困难的事情。以前,安全问题往往被认为是外部的问题,在程序种不考虑。

  客户和管理者永远也不会同意为了某个功能而花费金钱,除非他么能够看到明显的好处。作为程序员,我们有责任教育公司其他的人,使得他们明白安全的重要性,不能像以前那么只是把安全的问题轻描淡写,而是要作为核心的功能引入系统。安全功能永远是系统核心功能。

  实现稳固的安全功能

  为了达到系统安全的目的,作为ASP.NET开发人员,我们要在程序种增加功能。安全的系统都是经过严格的测试和审查的。我们后面的问题会详细的讲述ASP.NET的安全架构。

  避免增加新的脆弱性

  在向程序种增代码时,可能引入新的脆弱性。对于新增加的代码,要严格的审查,确保不会造成安全漏洞。

  我们很多时候都是先把功能实现,然后再来考虑安全的问题,做一些维护。这样做很危险,所以我们在项目开始时候就考虑安全的问题。

  还有就是要记得删除我们程序中的明显的漏洞。我们可能在开发调试的时候用了测试代码,如:

  

以下为引用的内容:
 public bool ValidateUser(string userName)
 {
  //testCode
  if(userName="app")
   return true;
  //....
 }

  我们可以在代码中加//testCode(见上),然后项目完成后查找所有的"testCode",将其删除。

  还有就是我们也教育用户,是他们对安全问题有一定的了解;我们对于用户的输入都要验证,还是那句话:所有用户都是恶意的。

  四、给出一些建议

  没有百分百的安全

  计算机安全领域中有个说法,"唯一安全的系统是锁在安全柜中而没有通电的系统"。我们必须承认,系统不可能决定安全,如果攻击者有足够的能力和耐心,他们终会找到侵入系统的方法。

  既然不可能绝对的安全,我们怎么办?我们可以尽可能的给未经授权的访问系统加大难度。这样就好比增加了护栏的高度,攻击者想要获取访问权限,就得付出更多的努力。而且很多的攻击者都是有投机的心理的-只找容易攻击的对象。

  藏起来不能保证安全

  我们常常将一些私人的文件藏在Web站点的秘密目录中,然后只告诉我们信任的人。可是,隐藏不能保证真正的安全。例子很多,如,公司在Web页面上使用隐藏的连接提供对"私人"目录的访问。这些连接可能是用一种透明图形隐藏在某个角落,对于Web页面的人不可见,但是对于网络搜索引擎来说,那么隐藏的链接和别的链接毫无分别。

  所以,隐藏不一定安全。而且现在的常常用加密算法,之所以用是因为算法经过了严格的审查和破解测试,在一定时间内算法是安全的,假如有个算法,破解需要几十年甚至更长时间,在现有阶段,我们使用也可说是安全的。

  应用程序的安全性由它最薄弱的环节决定

  大家都知道水桶的模板原理:水桶装水的多少,由最短的那块模板决定的。安全问题也一样的。因为恶意的用户想进入系统,会尝试很多的方式很途径,一旦程序的最薄弱的环节被找到,整个系统可想而知。

  建议在系统开发和完成的时候,构建出系统的结构图,然后标注从客户机一直到终端的数据库服务器所有环节,然后分析之间的连接情况,思考可能出问题的地方,改进。

  安全问题贯穿项目开发

  从项目的设计直到后期的部署,安全都要考虑。不要到最后才添加安全措施。

  过分安全将会不利于项目的开发

  有些时候,过度的强调安全不是好事情。如果系统安全很苛刻,如系统要求用户的密码必须是12位字符,而且还要有2个非数字字符,还要有大写,小写字母。用户很可能就记不住密码,甚至他们把密码直接写在纸上贴在电脑上,那么什么安全都没有了。所以安全的方案要折衷,和用户多商量。

  安全不仅仅只是技术的问题

  前面也讲过,在安全方面我们开发人员可以做的事情,很多的时候,用户是安全薄弱的环节,所以还要加强用户安全教育。

在线留言

我要留言