写着玩
安全问题其实是很多程序员想了解又容易忽略的问题,以下介绍一些常见的安全问题和解决方案,当然有的问题使用阿里云服务器之类的都给解决了,但还是知道的好。
一、输入验证和表示
输入验证和表示问题通常是由特殊字符、编码和数字表示所引起的,这类安全问题的发生是对输入的信任所造成的。这些问题包括:缓冲区溢出、跨站脚本攻击、SQL注入和其他等等。
1.1命令注入
l问题简介
在没有指定完整路径的情况下执行命令可能使得攻击者可以通过改变$PATH或者其他环境变量来执行恶意代码。
l详细描述
命令注入攻击有两种形式:
1)攻击者可以改变应用程序执行的命令
2)攻击者可以改变命令执行的环境。在此我们主要关心第二种情况,即攻击者可以通过改变环境变量或者在搜索路径中插入可执行恶意代码来改变命令的原有目的。
这种类型的命令注入攻击通常发生在:
1)攻击者改变应用程序的环境
2)应用程序执行命令时没有指定一个完整的路径或者校验执行的代码
3)通过执行该命令,应用程序会给予攻击者特殊的权限或者能力,而这些权限或能力是攻击者不应该具有的。
Example 1:下面代码使用系统变量APPHOME来定位程序安装的目录,然后根据此目录的相关路径来执行初始化脚本。
string val =Environment.GetEnvironmentVariable("APPHOME");
string cmd = val + INITCMD;
ProcessStartInfo startInfo = newProcessStartInfo(cmd); Process.Start(startInfo);
示例1中的代码中,攻击者可以通过修改系统变量APPHOME,使其指向一个包含恶意代码的路径,进而利用应用程序的高级权限来执行任意命令。因为程序没有对读入的环境变量值进行验证,如果攻击者能够控制系统的环境变量值APPHOME,那么攻击者就能欺骗应用程序,让其执行恶意代码,从而控制系统。
Example 2:下面的代码来自于一个web管理程序,它允许用户使用批处理文件包裹来开始对Oracle数据库的备份,然后运行cleanup.bat脚本来删除临时文件。脚本rmanDB.bat接收一条命令行参数来决定进行何种备份。因为对数据库的访问是受限制的,所以应用程序会以特殊权限来执行备份。
String btype =request.getParameter("backuptype"); String cmd = newString("cmd.exe /K
\"c:\\util\\rmanDB.bat"+btype+"&&c:\\utl\\cleanup.bat\"")System.Runtime.getRuntime().exec(cmd);
这里的问题是程序没有对从用户处读取的backuptype参数进行任何验证。通常Runtime.exec()函数不会执行多个命令,但是在这里,程序为了在只调用一次Runtime.exec()的情况下运行多个命令,先运行了cmd.exe。一旦该接口被调用,它将会执行由&&分隔开的多个命令。如果攻击者传入一个“&& del c:\\dbms\\*.*”形式的字符串,那么应用程序会将该命令与程序指定的其它命令一起执行。因为应用程序需要有足够的权限来与数据库交互,这就意味着攻击者注入的任何命令都将以同样的权限执行。
Example 3:下面的代码来自一个web应用程序,它允许用户通过访问接口来修改他们的系统密码。在网络环境中,修改密码的步骤之一是在/var/yp目录运行make命令。
System.Runtime.getRuntime().exec("make");
这里的问题是程序没有指定一个完整的路径,并且在执行Runtime.exec()调用前没有清理环境。如果攻击者能够修改环境变量$PATH,让其指向一个叫做make的恶意二进制代码,并使程序在该环境中执行,那么程序原本希望装载的二进制代码将会被恶意代码替代。因为程序需要有足够的权限来执行系统操作,这就意味着攻击者的make将能够以同样的权限运行,这很有可能使攻击者能够完全控制整个系统。
l解决方案
命令注入攻击主要是因为对用户输入或者环境变量值没有进行验证所造成的。为了防范命令注入攻击,我们应该对用户输入进行校验,过滤非法字符(如“&&”等)。同时,在程序中应该尽量不要从环境变量中读入数据,即使不得不依赖环境变量,也应该严格限制这些变量的路径和所起的作用,减少程序受到的影响。此外,还可以去除应用程序不必要的特殊权限。因为很多攻击只有在获得高级特权时才有意义,所以减少程序拥有的特权可以降低被攻击的风险。
1.2跨站脚本
l问题简介
向浏览器发送非法的数据会使浏览器执行恶意代码,通过XML编码进行身份认证也会导致浏览器执行非法代码。
l详细描述
跨站脚本发生在以下两种情况:
1)数据通过不可靠的源进入Web application,大多数情况下是web request。
2)包含在动态内容中的数据在没有经过安全检测就发送给网络用户。
发给web浏览器的恶意内容通常以JavaScript段的形式出现,但也包括HTML, Flash或者其他浏览器能够执行的代码类型,基于XSS的攻击种类几乎是无限的,但是它们通常会包括:传送私有数据(如cookies或者其他session信息)给攻击者,将受害者的浏览器重定向到攻击者所控制的web内容中,或者假借有漏洞的站点在用户机器上进行其他恶意操作。
Example 1:下面的JSP代码段从一个HTTP request中读雇员的eid,并将它显示出来如果eid包含源代码,该源代码就会被浏览器执行。
<% String eid =request.getParameter("eid"); %>
...
Employee ID: <%= eid %>
Example 2:下面的JSP代码段根据一个雇员的ID在数据库中查询对应的名字,并将其打印出来。如果数据库中的数据来源于用户的输入并没有进行相应的安全性检查,将会造成用户的浏览器执行恶意代码。
<%...
Statement stmt =conn.createStatement();
ResultSet rs =stmt.executeQuery("select * from emp where id="+eid);
if (rs != null)
rs.next();
String name =rs.getString("name");
%>
Employee Name: <%= name %>
与示例1一样,当name符合规范的时候,这段代码能够正确运行。但当name有问题时,代码段中并没有任何措施来阻止其运行。同样的,这段代码看起来危险性较小,因为name的值是从数据库中读取的。很显然,数据库的内容是由应用程序所控制的。然而,如果name的值来自于用户所提供的数据,那么这个数据库就会成为恶意内容的渠道。如果对数据库中存储的所有数据没有一个适当的输入验证,那么攻击者就能够在用户的浏览器上执行恶意内容。这种类型的攻击被称为存储型XSS,它显得尤为隐蔽,因为数据存储所带来的间接性使得这种威胁更难被识别,也使得攻击者能够侵袭多的用户。XSS攻击可以采用如下形式:一个web站点对访问者进行来宾登记(譬如经常使用的留言簿程序),攻击者在来宾登记中注入JavaScript代码,之后所有进入来宾登记页面的访问者都会执行该恶意代码。
正如上面的示例所述,造成XSS攻击的原因是HTTP响应中包含了未经验证的数据。XSS攻击可以通过三种媒介侵入受害者:
1)如示例1所示,数据直接从HTTP请求中读取,在HTTP响应中反射回去。反射型XSS通常是由攻击者诱使用户向有漏洞的web应用程序提供危险内容,然后这些危险内容会反射给用户并由浏览器执行。传递恶意内容最常见的方法是将恶意内容作为一个参数包含在URL中,然后将URL公开发布或者通过email发给受害者。以这种形式构造的URL是很多钓鱼(phishing)圈套的核心,通过钓鱼圈套,攻击者获取受害者的信任,诱使受害者访问指向有漏洞站点的URL。当站点将攻击者的内容反射给用户后,恶意内容将被执行,从用户机器上窃取用户的私有信息(如包含session信息的cookies)发送给攻击者或者进行其他恶意活动。
2)如示例2所示,应用程序在数据库或者其它可信数据源中存储了危险数据。这些危险数据随后被应用程序读取并包含在动态内容中。存储型XSS通常是由攻击者将危险内容注入数据存储中,然后该内容被读出并包含在动态内容中。在攻击者看来,注入恶意内容最理想的地方要么是用户访问量大的地方,要么是能引起用户兴趣的地方。这些被攻击者感兴趣的用户通常能够提高该应用程序的权限或者交互一些对攻击者而言有价值的敏感数据。如果这种用户执行恶意内容,攻击者就有可能假冒用户执行一些特殊操作或者访问属于用户的敏感数据。
3)应用程序外部的数据源中存储了危险数据,这些危险数据随后被应用程序作为可信数据读取并包含在动态内容中。
l解决方案
跨站脚本攻击都是由于对用户的输入没有进行严格的过滤造成的,所以我们必须在所有数据进入应用程序之前把可能的危险拦截。针对非法的HTML代码包括单双引号等,可以编写函数对其进行过滤。
1.3跨站脚本:缺乏验证
l问题简介
依赖XML编码来校验用户输入会导致浏览器执行恶意代码。
l详细描述
使用escapeXml="true"属性(默认设置)的标签可以防止一些攻击,但并不能防止所有的跨站脚本攻击。依赖于数据所处的context,像<,>,&,'和"这些字符作为XML的特殊字符,具有特殊的含义。依靠escapeXml="true"(这相当于是一个弱黑名单)来防止跨站脚本,这可能使得攻击者能够注入恶意代码在浏览器中执行。跨站脚本发生在以下两种情况:
1)数据通过不可靠的源进入Web application,大多数情况下是web request。
2)包含在动态内容中的数据在没有经过安全检测就发送给网络用户。
发给web浏览器的恶意内容通常以JavaScript段的形式出现,但也包括HTML, Flash或者其他浏览器能够执行的代码类型。基于XSS的攻击种类几乎是无限的,但是它们通常会包括:传送私有数据(如cookies或者其他session信息)给攻击者,将受害者的浏览器重定向到攻击者所控制的web内容中,或者假借有漏洞的站点在用户机器上进行其他恶意操作。
Example 1:下面的JSP代码段从一个HTTP request中读雇员的eid,并将它显示出来,如果eid包含源代码,该源代码就会被浏览器执行。
<% String eid =request.getParameter("eid"); %>
...
Employee ID: <%= eid %>
Example 2:下面的JSP代码段根据一个雇员的ID在数据库中查询对应的名字,并将其打印出来。如果数据库中的数据来源于用户的输入并没有进行相应的安全性检查,将会造成用户的浏览器执行恶意代码。
<%...
Statement stmt =conn.createStatement();
ResultSet rs =stmt.executeQuery("select * from emp where id="+eid);
if (rs != null)
rs.next();
String name = rs.getString("name");
%>
Employee Name: <%= name %>
与示例1一样,当name符合规范的时候,这段代码能够正确运行。但当name有问题时,代码段中并没有任何措施来阻止其运行。同样的,这段代码看起来危险性较小,因为name的值是从数据库中读取的。很显然,数据库的内容是由应用程序所控制的。然而,如果name的值来自于用户所提供的数据,那么这个数据库就会成为恶意内容的渠道。如果对数据库中存储的所有数据没有一个适当的输入验证,那么攻击者就能够在用户的浏览器上执行恶意内容。这种类型的攻击被称为存储型XSS,它显得尤为隐蔽,因为数据存储所带来的间接性使得这种威胁更难被识别,也使得攻击者能够侵袭更多的用户。XSS攻击可以采用如下形式:一个web站点对访问者进行来宾登记,攻击者在来宾登记中注入JavaScript代码,之后所有进入来宾登记页面的访问者都会执行该恶意代码。
正如上面的示例所述,造成XSS攻击的原因是HTTP响应中包含了未经验证的数据。XSS攻击可以通过三种媒介侵入受害者:
4)如示例1所示,数据直接从HTTP请求中读取,在HTTP响应中反射回去。反射型XSS通常是由攻击者诱使用户向有漏洞的web应用程序提供危险内容,然后这些危险内容会反射给用户并由浏览器执行。传递恶意内容最常见的方法是将恶意内容作为一个参数包含在URL中,然后将URL公开发布或者通过email发给受害者。以这种形式构造的URL是很多钓鱼(phishing)圈套的核心,通过钓鱼圈套,攻击者获取受害者的信任,诱使受害者访问指向有漏洞站点的URL。当站点将攻击者的内容反射给用户后,恶意内容将被执行,从用户机器上窃取用户的私有信息(如包含session信息的cookies)发送给攻击者或者进行其他恶意活动。
5)如示例2所示,应用程序在数据库或者其它可信数据源中存储了危险数据。这些危险数据随后被应用程序读取并包含在动态内容中。存储型XSS通常是由攻击者将危险内容注入数据存储中,然后该内容被读出并包含在动态内容中。在攻击者看来,注入恶意内容最理想的地方要么是用户访问量大的地方,要么是能引起用户兴趣的地方。感兴趣的用户通常会提高该应用程序的权限或者交互一些对攻击者而言有价值的敏感数据。如果这种用户执行了恶意内容,攻击者就有可能假冒用户执行一些特殊操作或者访问属于用户的敏感数据。
6)应用程序外部的数据源中存储了危险数据,这些危险数据随后被应用程序作为可信数据读取并包含在动态内容中。
l解决方案
跨站脚本攻击都是由于对用户的输入没有进行严格的过滤造成的,所以我们必须在所有数据进入应用程序之前把可能的危险拦截。针对非法的HTML代码包括单双引号等,可以编写函数对其进行过滤。
1.4拒绝服务
l问题简介
攻击者能够使程序发生冲突或者使合法用户无法获取资源。
l详细描述
攻击者可以通过向应用程序发送大量请求来使得应用程序无法向合法用户提供服务,但是这种洪水攻击(Flooding Attack)通常能在网络层进行防范。更重要的是有的bug使得攻击者可以通过使用少量的请求来让应用程序过载,这类bug使得攻击者可以指定请求所消耗的系统资源数量或者所占用系统资源的时间。
Example 1:下面的代码允许用户指定线程睡眠的时间。攻击者可以通过指定一个巨大的数字来无限期的占用该线程。通过少量的请求,攻击者就能够耗尽应用程序的线程池。
int usrSleepTime =
Integer.parseInt(usrInput); Thread.sleep(usrSleepTime);
Example 2:下面的代码从一个zip文件中读取一个字符串。因为它使用了ReadLine()方法,所以它将读取一个无限制的输入。攻击者可以利用该代码来导致OutOfMemory Exception,或者消耗大量的内存使得程序要耗费更多的时间来进行垃圾回收,又或者在接下来的操作中使得内存溢出。
InputStream zipInput =zipFile.getInputStream(zipEntry);
Reader zipReader = newInputStreamReader(zipInput);
BufferedReader br = newBufferedReader(zipReader);
String line = br.readLine();
l解决方案
拒绝服务攻击是一种滥用资源性的攻击。从程序代码角度讲,对涉及到系统资源的外部数据应该进行严格校验,防止大数目或者无限制的输入。从系统管理的角度来讲,主机应该:
1)关闭不必要的服务;
2)将数据包的连接数从缺省值128或512修改为2048或更大,以加长每次处理数据包队列的长度,以缓解和消化更多数据包的连接;
3)将连接超时时间设置得较短,以保证正常数据包的连接,屏蔽非法攻击包;
4)及时更新系统、安装补丁。此外,还可以对防火墙、路由器进行设置。禁止对主机非开放服务的访问,限制同时打开的数据包最大连接数,访问控制列表(ACL)过滤,设置数据包流量速率,利用负载均衡技术等等。
1.5HTTP响应截断
l问题简介
在HTTP响应头中包含未经验证的数据将可能导致缓存中毒(cache-poisoning)、跨站脚本(cross-site scripting)、跨用户攻击(cross-user defacement)或者页面劫持(page hijacking)攻击。
l详细描述
HTTP响应截断攻击发生在:
1)数据通过一个非可信源进入web应用程序,最可能的是通过HTTP请求。
2)包含在HTTP响应头中发送给web用户的数据没有对恶意字符进行验证。与很多软件安全攻击一样,HTTP响应截断是一种达到目的的手段,它本身并不是目的。起初,这种攻击很简单:攻击者传递恶意数据给有漏洞的应用程序,应用程序将这些数据包含在HTTP响应头中。要想成功地使用该攻击,应用程序必须允许响应头中包含CR(回车,即%0d或者\r)和LF(换行,即%0a或者\n)字符。这些字符不仅使得攻击者可以控制应用程序发送的部分响应头和响应体,还使得攻击者可以创建能够完全控制的HTTP响应。
Example:面的代码段从HTTP请求中读取一篇博客文章的作者名author,然后将它放入HTTP响应的cookie头。
String author =request.getParameter(AUTHOR_PARAM);
...
Cookie cookie = newCookie("author", author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);
假如在HTTP请求中提交的字符串是由标准的文字和数字字符组成,如“Jane
Smith”,那么HTTP响应中包含的cookie可能是如下形式:
HTTP/1.1 200 OK
...
Set-Cookie: author=Jane Smith
...
然而,因为cookie的值是根据未经验证的用户输入生成的,所以只有当提交的Author.Text的值中不包含任何CR和LF字符时,HTTP响应才会是以上形式。如果攻击者提交了一个恶意字符串,比如“WileyHacker\r\nHTTP/1.1200
OK\r\n...”,那么HTTP响应将被分割为如下两个响应:
HTTP/1.1 200 OK
...
Set-Cookie: author=Wiley Hacker
HTTP/1.1 200 OK
...显然,第二个响应是由攻击者完全控制的,它能够构成任何攻击者想要的响应头和响应体。这种攻击者可以构造任意HTTP响应的能力可以导致各种攻击,包括跨用户攻击、web和浏览器缓存中毒、跨站脚本和页面劫持。跨用户攻击:攻击者可以制作一个专门的请求发给有漏洞的服务端,服务端将会创建两个响应,第二个响应会被误解为是另一个请求的响应,这个请求是由使用同一个TCP连接访问服务端的另一个用户发来的。当攻击者诱使用户自己提交恶意请求时,或者更隐蔽的,当攻击者与用户共用同一个TCP连接到服务端(比如共享的代理服务)时,就可以完成上述攻击。最好的情况下,攻击者可以通过这种攻击使用户认为应用程序已经被修改了,导致用户对应用程序的安全性失去信心。最糟的情况下,攻击者可以模仿应用程序,提供相似的内容,诱使用户将私有信息(如账号和密码)发送给攻击者。跨站脚本:一旦攻击者可以控制应用程序发送的响应,他们就可以选择各种恶意内容来提供给用户。跨站脚本是最常见的攻击,它在用户浏览器上执行响应中包含的恶意JavaScript或者其他代码。基于XSS的攻击种类几乎是无限的,但是它们通常会包括:传送私有数据(如cookies或者其他session信息)给攻击者,将受害者的浏览器重定向到攻击者所控制的web内容中,或者假借有漏洞的站点在用户机器上进行其他恶意操作。针对一个有漏洞的应用程序的用户,最常见的也是最危险的攻击方式是通过JavaScript将session和认证信息发送给攻击者,使得攻击者能够完全控制受害者的账号。页面劫持:除了通过有漏洞的应用程序来发送恶意内容给用户以外,这种攻击还能够将服务端生成的要发送给用户的敏感内容重定向给攻击者。攻击者通过一个请求生成两个响应,一个是服务端原有的响应,一个是攻击者制造的响应。然后攻击者通过一个中间节点,如共享的代理服务,将服务端产生的发给用户的响应指向攻击者。因为攻击者制造的请求产生了两个响应,第一个作为攻击者请求的响应,第二个会保留在响应池中。当用户通过同一个TCP连接提出HTTP请求时,由于攻击者制造的响应已经存在,所以该响应会被当作用户的响应发送给用户。然后攻击者发送第二次请求给服务端,此时代理服务器就会把服务端生成的原本要发给用户的响应当作攻击者的响应发送给攻击者,这样攻击者就能从该响应中获取危及用户安全的敏感信息。
l解决方案
要想进行HTTP响应截断攻击,应用程序必须允许响应头中包含CR(回车,即%0d或者\r)和LF(换行,即%0a或者\n)字符。所以我们可以在数据进入应用程序之前把可能的危险拦截,针对CR和LF字符进行过滤。