软件安全


软件安全问题


  1. Web应用安全性漏洞中,其中包括未经验证的输入、跨站点脚本、缓冲区溢出、注入攻击( injection flaws )和不恰当的错误处理。程序中的错误代码以及没有充分进行检查和验证的数据造成了这些缺陷的发生。
  2. 软件安全与软件的质量和可靠性紧密相关,但又略有不同。软件的质量和可靠性关心的是一个程序是否意外出错,这些错误是由一些随机的未预料到的输入、系统交互或者使用错误代码引起的,它们服从一些形式的概率分布。提高软件质量通常的方法,是采用某些形式的结构化设计,井通过测试来识别和消除程序中尽量多的bug ,一般包括可能的输入变化和常见的错误测试。
  3. 软件安全不同于软件的质量和可靠性,攻击者不会依据一定的概率分布实施攻击,他们的目标是那些特殊的可以利用的bug ,从而造成程序失败。
  4. 防御性程序设计(defensive programming ):是防御性设计的一种形式,目的是保证软件的连续性功能。
  5. 防御性程序设计有时又被称为安全程序设计( secure programming )。这是因为攻击者可能会利用软件的bug ,实现代码注入攻击、拒绝服务攻击或者其他形式的攻击。防御性程序设计和正常的编程之间的一个不同是不需要任何假设,所有的错误状态都要进行说明和处理。简短地说,程序员在代码里从来不假设一个特定的函数调用或者库函数会像它说明的那样工作,从而不做任何检查就进行处理。
  6. 对一个程序将要接收的输入类型和程序执行的环境做出各种各样的假设。防御性程序设计意味着程序需要验证所有这些假设,所有可能的失败都能安全地、完美地得到解决。
  7. 改编一个程序时,防御性程序设计又意味着程序员必须仔细检查他所做的每一个假设,检查和处理所有可能的错误,仔细检查与存在的代码之间的每一个交互。不能识别和处理这些交互可能会导致错误的程序行为,以及将一个原来安全的程序变成一个存在漏洞的程序。

处理程序输入


  1. 程序输入是指程序之外的任意数据源,程序员在编写代码的时候并不清楚地知道这些数据的值。程序输入显然包括了从用户键盘、鼠标、文件或者网络连接读入到程序中的数据。然而,它也包含了在执行环境中提供给程序的数据,程序从文件读入的任意配置值或者其他的数据,以及操作系统提供给程序的值
  2. 任何程序输入都有两个关键点需要我们考虑,这就是输入的长度以及输入的含义和解释
  3. 输入长度和缓冲区溢出
  • 针对缓冲区溢出编写安全的程序代码需要有一种心态,就是认为任何输入都存在危险,在处理输入时不要使程序面临危险。关于输入的长度,这就意味着或者使用动态缓冲区确保有足够的有效空间,或者把输入数据处理成缓冲区长度的数据块。即使使用动态缓冲区,程序员也一定要细心处理,保证请求的空间不会超出内存可用的范围。一旦超出内存可用的范围,程序必须能够很好地处理这个错误。
  • 在缓冲区块中处理输入、抛弃多余的输入、中断程序,或者任何其他的合理响应异常状态的动作。
  1. 程序输入的解释
  • 程序输入的数据可分为文本和二进制两种形式。
  • 当处理二进制数据时,程序假定将未经加工的一些二进制数据解释为整数、浮点数、字符串或者其他一些更复杂结构的数据。当读入二进制数据时,这些假设的解释必须进行验证,如何进行处理的细节很多都取决于信息编码的特殊解释。
  • 程序将文本数据当作程序输入进行处理。依据某些字符集,未经加工的二进制数据被解释为字符。传统方式上假设的字符集是ASCII 字符集。
  • 除了识别输入是什么字符之外,更重要的是确定它们的含义。它们可以表示一个整数或者一个浮点数。它们也可能是一个文件名、一个URL地址、一个电子邮件地址,或者一些形式的标识符。
  1. 注入攻击
  • 涉及多种对输入数据进行无效处理的相关程序有关,特别是当程序输入的数据有意或者无意间影响到程序的执行流的时候,这个问题就发生了。
  • 最常见的一种是,当输入数据作为一个参数传递给系统上的另一个辅助程序的时候,原来的程序会接着处理和使用它的输出。
  • 命令注入攻击:
    ```html

a)中显示的一个perl CGI脚本。一个指定的用户使用UNIX finger命令返回一些基本的细节。这个脚本放在Web服务器 个适当的位置,调用这个脚本能够响应一个简单的表单。
通过在服务器系统上运行一个程序,返回程序的输出,如果有必要在 HTML Web页里对输出的内容重新进行适当的格式化,调用脚本就能从服务器上取回想要得到的信息。
这个脚本存在一个致命的漏洞,用户输入的值作为一个参数直接传递给finger程序。如果提供的是一个合法用户的标识符。例如, lpb, 那么接下会将会输出这个用户的信息。。然而,如果攻击者提供 个值,其中包括shell元字符( shell元字符用于分离和组合多个命令 ),例如, xxx; echo attack success; ls -l finger *,输出的信息就是c)中显示的后一部分内容。
利用Web服务器的特权,攻击者能够运行系统上任何一个程序。
这就是一个命令洼入( command injection )攻击,因为使用的输入数据可以建立一个命令,随后通过拥有Web服务器特权的系统执行这个命令。
为了应对这种攻击,程序员需要明确识别有关输入形式的任何假设,在使用数据之前验证这些数据与其假设是否一致。验证的时候通常将输入的数据与描述假设形式的模式进行比较,一旦测试失败就拒绝该输入。

  • SQL注入攻击

用户提供的输入数据可以建立一个SQL请求,从数据库取回一些信息。
实例:
把一个名字当作输入提供给脚本,这个名字从用户提交的的表单中字段中读取,使用这个值建立一个请求,从数据库中取出与该姓名相关的记录。这段代码的漏洞与前面的命令注入例子非常类似,它们之间的不同仅仅在于SQL注入利用SQL元字符,而命令注入利用shell元字符。
在这里插入图片描述
如果输入元字符Bob’ drop table suppliers= =,那么运行结果将返回特定的数据库记录,但是接着将删除整个表。
为了阻止这类攻击,所有输入在使用之前必须进行验证,任何元字符必须清除,消除它们的影响,或者完全拒绝元字符输入。

  • 代码注入攻击

在这种攻击中,提供的输入包含被攻击的系统能够运行的代码。
实例:
漏洞的产生是由于使用一个变量构造了一个文件名,这个文件随后将被包含到该脚本中。
在这里插入图片描述
主脚本设置了变量$ path的值,该变量指的是包括这个程序、所有代码和数据文件的主要路径。PHP将原来在HTTP请求 中提供的任何输入变量的值分配给同名的全局变量,一个毫无经验的程序员可以很容易地编写这种表单处理程序。,因而一个用户能够为任意一个期望的全局变量随意指定一些值,这些值能够被建立并传递给脚本。
变量$ path不是一个表单字段, PHP的第二行是一个include命令,它不仅能包含本地文件,而且如果提供了一个URL ,那么被包含的掘代码可能源、自网络上的任何地方。
利用一个与图 12-4b相似的请求就可以实现代码注入攻击。这就导致变量$ path 包含一个文件的URL ,而这个文件是攻击者的PHP代码。同时变量$ path 也能定义另一个变量$cmd ,它告诉攻击者脚本可以运行什么命令。在这个例子中,附加的命令可以非常简单地列出当前路径下的所有文件。然而,这个命令也可以是Web服务器上的任何命令,只要拥有特权就能运行。
对策:
阻止将表单字段的值分配给全局变量。
在include (和require )命令中仅使用常量值。它能确保包含的代码真正源于指定的文件。如果不得不使用一个变量,那么在使用之前必须立即进行仔细的验证。

  • 其他的注入攻击,包括mail注入、格式化字符串(format string )注入和解释器(interpreter )注入。

只要一个程序调用一些服务,而这些服务来自于另一个程序、服务或者函数,就可能发生注入攻击;给一个程序传递
来源于外部的一些不可信的、没有进行充分检查和验证的信息时,也可能发生注入攻击。

  • 我们需要识别所有的输入源,在使用这些输入之前验证所有的假设情况,以及理解那些提供给调用程序、服务和函数的数据值的含义和解释。
  1. 跨站点脚本攻击(XSS)
  • 一个用户给程序提供输入,而由此产生的结果输出给另外一个用户。因为这种攻击经常在脚本型的Web应用中看到,因此称为跨站点脚本攻击。
  • 这个漏洞发生在网站应用程序接受用户输入数据却没有做必要的编码,如果对用户输入的数据没有进行正确的编码和过滤,这个被注入恶意脚本将发送给其他用户,对浏览器来说,它没有办法知道应不应该相信一个脚本的合法性,浏览器会正常地把这个脚本当作普通脚本执行,这个时候恶意的操作就不可避免的发生了,大部分的时候XSS是用来窃取cookie,或窃取用户的会话令牌session,以此进行会话劫持。
  • cookie是用户和服务器之间的桥梁,服务器可以使用session来保存用户的身份信息(ID,购物车)等,但是需要访问网页的时候带上想要的cookie,通过cookie中的特定值来识别session ID,才能把单独用户和单独的session联系起来,cookie是有状态HTTP交互的一种机制。
  • 浏览器的同源策略:同源是指域名,协议和端口号都相同,同源策略导致了XSS攻击必须在我们希望的同一个域下触发,例如攻击者想要窃取在www.a.com下的cookie,那就必须在www.a.com这个域下的某一个页面放置XSS代码。
  • 反射型XSS:
    考虑如下代码:
<html>
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>XSS</title> 
</head> 
<body> 
<form action="" method="get"> 
<input type="text" name="input">     
<input type="submit"> 
</form> 
<br> 
<?php 
$XssReflex = $_GET['input'];
echo 'output:<br>'.$XssReflex;
?> 
</body> 
</html> 

当我输入< script > alert(‘xss’)</ script > ,可以看到浏览器成功弹窗,说明我们输出的JavaScript代码成功被执行了。
在这里插入图片描述
当我们输入一个其他的js脚本时,例如document.cookie就可以成功的窃取用户的cookie信息。

  • 存储型XSS
    和反射性XSS的即时响应相比,存储型XSS则需要先把利用代码保存在比如数据库或文件中,当web程序读取利用代码时再输出在页面上执行利用代码。但存储型XSS不用考虑绕过浏览器的过滤问题,屏蔽性也要好很多。
    流程:
    在这里插入图片描述
    考虑下面一段代码:
<span style="font-size:18px;"><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>  
    <html>  
    <head>  
    <title>XssStorage</title>  
    </head>  
    <body>  
    <h2>Message Board<h2>  
    <br>
    <form action="XssStorage.php" method="post">  
    Message:<textarea id='Mid' name="desc"></textarea>  
    <br>  
    <br>  
    Subuser:<input type="text" name="user"/><br> 
    <br>
    <input type="submit" value="submit" onclick='loction="XssStorage.php"'/>  
    </form>  
    <?php  
    if(isset($_POST['user'])&&isset($_POST['desc'])){  
    $log=fopen("sql.txt","a");  
    fwrite($log,$_POST['user']."\r\n");  
    fwrite($log,$_POST['desc']."\r\n");  
    fclose($log);  
    }  
      
    if(file_exists("sql.txt"))  
    {  
    $read= fopen("sql.txt",'r');  
    while(!feof($read))  
    {  
        echo fgets($read)."</br>";  
    }  
    fclose($read);  
    }  
    ?>  
    </body>  
    </html></span>  

这个页面采用POST提交数据,生成、读取文本模拟数据库,提交数据之后页面会将数据写入sql.txt,再打开页面时会读取sql.txt中内容并显示在网页上,实现了存储型xss攻击模拟。
当我们输入时,页面成功弹窗 ,关闭浏览器重新打开,仍会弹窗。因为该输入以及存储在文件中。

  1. 验证输入语法
  • 输入的数据一定要与输入假设进行比较,仅仅接受有效的输入,这是一个重要的原则。另一个原则是将输入的数据和已知的危险数据值比较
  • 这类比较通常采用正则表达式(regular expression )完成。正则表达式是由一系列描述允许的输入变化的字符构成的模式。在正则表达式中的一些字符是逐个处理的,与它们进行比较的输入数据必须包含那些字符。其他的一些字符有特殊的含义,可以是各种字符集的详细说明、字符的分类以及重复的字符。如果输入的数据比较失败,那么就要遭到拒绝。这时,一个适当的错误信息将发送到输入源,允许用户改正或者重新输入。
  • 输入数据的多重编码问题:

Unicode是一种编码方案,Unicode是为了解决传统的字符编码方案的局限产生的,它为每种语言的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台进行文本转换,处理的要求.
这里就有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的
unicode的实现:uft-8编码,如果一个字节则其最高位二进制为0;如果多字节,其第一个字节从最高位开始,连续的二进制为值为1的个数决定了其编码的字节数,其余各字节均以10开头。
在这里插入图片描述
斜杠字符“/”,在一个UNIX 文件名里用于分隔路径,在ASCII UTF-8 中都是十六进制值2F ”。 UTF-8 编码允许冗余,承认更长的编码:“CO AF ”和“EO 80 AF ”。尽管有严格使用最短编码的限制,但很多Unicode解码器接受任何一个同等有效的序列。
有一类攻击企图给脚本提供一个绝对路径的文件,实际上需要的仅是一个简单的当前路径下的文件名。我们通常检查提供的文件名不以“/”开始,或者不包含任何“../”上一级路径的表示形式。如果检查的时候采用的只是斜杠的最短Unicode编码,没有检查其他较长的Unicode 编码,但是攻击者使用的正好是这些编码。
由于多重编码的可能性,因此输入的数据必须首先转换成单一的、标准的、最小的表示形式,这个过程称为标准化( canonicalization ),它包含用一个通用的值代替那些等价的编码。一且完成这个过程,输入的数据就能够与可接受的输入值的一个单一表示进行比较。

  • 一种类型的数据强制转换成另一种类型的数据可能发生问题。
    一个缓冲区长度可以用一个无符号整数表示,它将要和一个可接受的最大缓冲区长度进行比较,在比较的时候无符号值可以转换成一个有符号值来处理,不同的语言可能有不同的处理方式。这就导致一个漏洞,因为负数的最高位为 1,但是最高位为1的无符号整数又是一个较大的正数值。这样攻击者就能指定一个很大的输入长度,这个很大的无符号型输入长度与缓冲区的最大长度进行比较的时候是作为负数处理的。一个负数明显小于一个较小的正数,这说明比较是成功的。然而,使用缓冲区的时候输入数据将会超出缓冲区的实际范围,对输入数据的错误处理产生了缓冲区溢出。
  1. 输入的fuzzing技术
  • 这是一个软件测试技术,它使用随机产生的数据作为程序的输入进行测试。测试的输入范围很大,包括直接的文本或者图形输入、在一个Web和其他的分布式服务上发出的随机网络请求,以及传递给标准库函数或者系统函数的随
    机参数值。测试的目的在于确定程序或者函数是否能够正确处理所有的非正常输入、程序是否受到破坏,以及程序失败以后是否得到正确响应。
  • 对任意的程序、服务或者函数的输入假设来说, fuzzing技术的主要优点就是简单和自由,而且产生大量测试的费用非常低。更进一步的,这样的测试可以帮助程序识别它的可靠性和安全性。
  • 尽管输入数据能够完全随机产生,但它也能依据一些模板随机产生。设计这样的模板可以检查可能存在的bug 。
  • fuzzing技术仅能识别简单类型的输入错误,如果一个bug仅仅在极特殊的输入时才能够触发,那么fuzzing技术就不可能找到它。

编写安全的程序代码


  1. 算法的正确实现
  • 如果一个算法没有正确实现问题的所有情形和变化,那这就可能允许一些似乎合法的程序输入触发一些程序行为,而这些程序行为并不是程序应该完成的,因而给攻击者提供了一些其他的能力。这个错误可能是由于对程序输入不合适的解释或者处理造成的,但它也可能是对本来有效的输入没有进行适当处理而引起的。
  • 一个总所周知的例子:TCP会话欺骗或hijack攻击

攻击的目标不是使服务器处于半开放连接状态,而是欺骗服务器接收伪造源地址的数据包,其中源地址属于可信的主机但数据包却源于攻击者的系统。如果攻击成功,那就会说服服务器运行一些命令或者提供数据访问,前提是这些服务是允许提供给可信主机的。
由于使用一个伪造的源地址,攻击者没有看到来自服务器的响应,所以他们不知道服务器提供的初始序列号。然而,如果攻击者能够正确猜到这个数字,那么就可以建立一个ACK包发送到服务器,接下来假定与服务器的连接已经建立。服务器认为任何后续的数据包都来源于可信的主机,并按照其分配的权限处理这些数据。这次攻击的hijack变量一直等待,直到一些授权的外部用户连接井登录服务器。接下来攻击者尝试猜测序列号,使用伪造的内容模仿授权用户发给服务器的下一个数据包。如果猜测的数字正确,那么攻击者使用授权用户的访问权限和访问许可发出任何请求,服务器都会响应。
由于可信主机确认服务器没有发送数据包,因此这个系统就假设存在一个网络错误井发送一个reset (RST )数据包中断这次连接。攻击者必须确保,在可信主机系统发送中断之前将攻击数据包发送到服务器并且被处理,攻击者在攻击目标服务器的同时向可信主机系统发出一个拒绝服务(denial-of-sevice )攻击,就可以达到这个目的。
由于很多TCP/IP 的实现所使用的初始序列号都容易猜测,这一缺陷为这些攻击提供了机会。另外,使用序列号能够识别一个会话的所有数据包。 TCP/IP 的标准规定每一次连接使用一个新的、不同的序列号,这样就能够区分以前连接的数据包,这个序列号可能是一个随机数.

  • 程序员经常在程序中故意设置一些用于检测和调试的代码,这是这个问题的另一种表现形式。尽管在程序开发过程中设置一些代码是有效的,但在程序的产品发行版中也会经常遗留下这些代码。至少,这些代码会将一些不合适的信息透露给使用该程序的用户。在最坏情况下,它能允许用户绕过安全检查或者程序的其他限制,完成其他一些不允许完成的动作。
  • 一个更进一步的例子涉及高级语言或者中级语言解释器的实现。其假设是解释器可以正确实现特定的程序代码。充分反映语言语法的失败会导致bug 的出现,这些bug可能被攻击者利用。
  1. 保证机器语言与算法一致
  • 第二个关键问题涉及在一些程序设计语言中指定的算法和实现这个算法所运行的机器指令之间的一致性,这个问题是大多数程序员最容易忽略的问题。其假设是编译器或者解释器能真正产生或执行可以有效实现语言语句的代码。
  • 一个恶意的编译器,程序员能够在其中包含一些指令,当它处理一些特殊的输入语句的时候触发附加的代码。这些语句甚至包含了部分编译器,所以当编译器源代码被编译的时候,即使在从编译器掠代码中移除所有附加的代码之后也能重新插入这些变化。
  1. 数据值的正确解释
  • 在计算机的最底层,所有的数据都是以二进制位组的形式进行存储。这些数据存储在内存的字节中,而这些字节会形成一个较大的存储单元,例如一个字( word )或者长字( longword )。
  • 一些语言接受比较自由的数据解释,允许程序代码明显改变对数据值的解释,广泛使用的C语言就具有这个特性。特别是将一个变量的值解释为整数或者内存地址(指针)都可以,在它们之间进行转换非常容易。
  • 针对这些错误最好的防御方棒,是使用具有较强类型的编程语言。
  1. 内存的正确使用
  • 程序需要使用内存的时候就动态分配,使用之后随即释放。如果一个程序没有正确管理这个过程,那后果可能是堆区的可用内存逐步减少,直至完全用尽。这被称为内存泄漏( memory leak ),一旦堆区的可用内存用尽,程序便会崩愤。这给攻击者提供了一个明显的机制来在这样的程序中实现拒绝服务攻击。
  • Java 和C++等其他的语言都是自动管理内存的分配和释放。
  1. 阻止共享内存竞争条件的产生
  • 多个进程或一个进程的多个线程访问通用共享内存的管理。如果没有恰当的访问同步机制,那么这些进程或线程由于重叠访问、使用和替代共享值,就可能导致数据值被破坏或修改丢失。当多个进程或线程通过竞争来获取对一些资源的未加控制的访问时,会导致竞争条件( race condition )的发生
  • 如果我们选择了不正确的同步原语序列,那就有可能造成各种进程或线程的死锁(deadlock ),这时每个进程或线程都在等待访问一个资源,而这个资源正在被其他的进程或线程访问。如果不中断其中的一个或者多个程序,就没有更简单的方怯从死锁中恢复。攻击者能够在存在漏洞的程序里触发一个死锁,实现拒绝服务攻击。

与操作系统和其他程序进行交互


  1. 计算机程序模型的第三个部分,是程序在操作系统的控制下在计算机系统上执行。
  2. 当一个程序运行时,操作系统构造一个进程的执行环境。除了程序代码和程序使用的数据外,该进程还包括一些操作系统提供的信息。这些信息包括用来适应程序操作的环境变量,以及为该程序指定的命令行参数。所有这些数据应被视为程序的外部输入,所以在使用它们之前必须保证其有效性.
  3. 环境变量
  • 环境变量是操作系统中一个具有特定名字的对象,它包含了一个或多个应用程序所将用到的信息。
  • 操作系统在构造进程的内存空间时,将这些信息包含其中。默认情况下,进程拷贝父进程的环境变量值。然而,执行新程序的请求可以指定使用一系列新值。程序可以在任何时候修改进程的环境变量,这些修改依次传递给进程所产生的子进程。
  • 常见的环境变量:
    path环境变量:当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下寻找此程序外,还应到path中指定的路径中去找,用户通过设置环境变量来更好的运行进程。
    LD_LIBRARY_PATH:该环境变量主要用于查找动态连接库时处默认路径之外的其他路径。
    IFS:定义了shell用作字段分隔符的一系列字符,默认情况下会将下列字符当作字符分隔符:空格,制表符,换行符。
  • 这段简单的脚本调用两个不同的程序: sed和grep 。程序员假设这两个程序的标准系统版本会被调用。但此处只是指定了文件名。为了找到具体的程序,shell 将到PATH变量指定的目录中查找对应的文件名。攻击者只需要简单地重定义变量PATH,使其包含攻击者控制的目录,比如该目录中包含一个grep程序。当脚本运行时,攻击者的程grep 代替程序的标准系统版本被调用。这样这个程序就可以按照攻击者的意愿做任何事,因为它具有shell 脚本所具有的权限。
    在这里插入图片描述
    对策:可以对每个程序使用绝对路径名。变量PATH可以被重设为脚本已知的默认值。
    采用这些方法后,这个版本的脚本程序也还有漏洞,这次是因为IFS 变量。该变量被用来区分一行命令中的不同单词。默认情况下为空格、制表符或换行符。然而,可以将它设置成任意的字符序列。我们考虑在该集合中包含“=”的影响。接下来给变量PATH赋一个新值被解释成执行程序PATH的命令,而后面的目录列表被视为程序参数。如果攻击者也修改变量PATH加入了包含攻击程序PATH的目录,那么当脚本运行时,攻击程序就会被执行。
    如果需要一个脚本应用程序,那最好的解决方法是使用一个编译过的包装函数(wrapper function )调用它。在调用需要的脚本程序前,一个编译好的程序可以构建一个相对安全的环境变量集合,然后使用该程序完成属主或组的改变。
    尽管以高优先级运行的是编译过的程序,但是攻击者仍然可能利用环境变量进行攻击。如果这个程序执行另一个程序,依据执行时使用的命令,变量PATH仍然被用于寻找将要执行的程序。因而,所有这类程序必须首先将其重设为已知的安全值。
    环境变量LD LIBRARY_PATH的功能。该变量的使用对动态库的使用提供了一定的灵活度。但同样它也引入了一些可能的攻击机制。攻击者构造一个通用库的自定义版本,将已知的、当程序执行时能被动态链接的库函数的代码替换成攻击代码。通过设置变量LD_LIBRARY_PATH ,程序将首先引用含有攻击代码版本的库函数,当程序执行的时候调用该库函数,攻击代码就会利用目标程序的特权运行。
  1. 使用最小特权原则
  • 攻击者能够利用受到攻击的程序或服务的特权和访问权限来执行代码。如果这些特权比本来为攻击者分配的权
    限高,就会导致特权扩大。
  • 每个程序都应该使用完成其功能所需的最小特权。
  • 正常情况下,当用户运行一个程序的时候,该程序应该和用户具有相同的优先级和访问权限。很多情况下,程序需要使用一些该用户未被授权访问的资源。通常的做越是对一个服务采用一个专门的系统登录,并且仅登录者可以访问该服务所使用的目录和文件。实现该服务的任何一个程序都使用系统用户的访问权限运行,这样的程序被视为特权程序。
  • 另一个需要关心的问题是保证任何特权程序仅能修改所需的文件和目录。
  • 和特权程序相关的最大的安全问题,出现在程序以root或者管理员权限运行的时候。这时该程序对系统拥有很高的访问和控制权限。获取这样的权限正是系统攻击者的主要目的,因而这些特权程序也成了攻击者的主要目标。最小特权原则要求访问权限应该尽可能地小,时间也应尽可能地短。
  • 现在我们考虑良好的防御性程序设计准则,大的复杂程序应尽量分解成小模块,每个模块仅在需要的时候拥有相应的访问权限。这种程序的模块化设计思路使得模块间的隔离度( degree of isolation )更大,从而也降低了在一个组件中的安全问题的影响范围。
  • 最小化特权的更进一步的技术,是在一个特定分隔出的与文件系统隔离的区域内运行存在潜在漏洞的程序。
  1. 系统调用和标准库函数
  • 程序通过操作系统调用来使用系统资源,通过调用标准库函数完成通常的操作。
  • 操作系统和库函数的作用是管理资源,目标是为在系统上运行的程序提供最好的性能。这使得服务请求可以通过缓存、重新排序或其他的修改对系统的使用进行优化。
  • 实例:
  • 安全的文件粉碎是指删除一个文件后其内容不能恢复。我们建议利用多个不同的位模式(bit pattern )反复覆盖数据内容,使得与原来的数据具有较大的差异。因此,一个好的文件粉碎程序可以实现图a所示的算法。

存在的问题:
在文件以写的方式打开时,系统需要将新的数据当作原来的数据写到相同的磁盘块。
当文件被某个样本覆盖的时候,数据立即写入硬盘。第一步是将数据拷贝到一个应用程序的缓冲区中,由标准库文件I/0例程管理。这些例程控制着缓冲区的写人,直到缓冲区充满之后,程序刷新缓冲区,或者关闭文件。如果文件比较小,在程序循环执行、返回到文件的开始以及写入新的样本之前缓冲区不会充满。
当刷新I/0缓冲区以及关闭文件的时候,数据被写入硬盘。然而,在操作系统的文件处理代码中还有另外一层缓冲,这一层缓冲区存储在操作系统上当前正在运行的所有进程读写文件的信息,它对这些读写的数据进行重组或调度,使其更利于物理设备的高效访问。即使程序把来自应用程序的缓冲区的数据刷新到文件系统的缓冲区,数据将不会被立即写入。如果新的替代数据被刷新,那么它们很可能再次替代原来未被写入磁盘的数据,因为文件系统代码将假设早先的值不再需要,而且该替代数据也可能没有被写入硬盘。因此程序必须强制文件系统中的数据与设备上的值同步,才能确保数据被实际地传送到设备上。

  1. 阻止共享资源的竞争条件产生
  • 常用的技术是在共享的文件上设定一个锁 (lock ),保证每个进程轮流访问
  • 文件锁( lockfile ):一个进程必须创建和拥有文件锁,这样才可以获取对共享资源的访问。任何检测到文件锁存在的其他进程必须等待,直到该文件锁被撤销,它才能创建自己的文件锁来获取访问权。
  • 首先程序要检查文件锁,如果不存在就产生一个。遗憾的是这包含一个致命的缺陷。假设有两个进程,每个进程都试图检查并产生这个文件锁。第一个进程检查井确认文件锁不存在,然而,在它产生文件锁之前,系统将该进程挂起而让其他进程运行。而在此时,另一个进程也检查到文件锁不存在并产生一个,并进一步使用共享资源。接着第二个进程也被挂起,控制返回第一个进程,第一个进程也将产生文件锁,进一步同时访问共享资源,这时在共享文件中的数据遭到破坏。这是竞争条件的最传统的解释。问题是检查文件锁不存在和产生文件锁的过程必须是同时进行的,没有中断的可能,这种操作被称为原子操作( atomic operation )。
  1. 安全临时文件的使用
  • 很多程序在处理数据的时候,需要存储数据的临时副本。临时文件通常用于这一目的。
  • 有关临时文件的关键问题是,它们应该是唯一的并且不能被其他进程访问。在某种意义上讲,这与管理共享文件的访问是对立的。构造文件名的一般技术是在名字中包含一个值,例如进程标识符( process identifier )。由于每个进程有不同的标识符,这就保证了文件名的唯一性。程序通过检查确认文件不存在,然后产生临时文件。
  • 攻击者不会依据规则行事。他们试图猜测某些特权程序将要使用的临时文件名,在程序检查文件不存在和产生临时文件的两个动作之间试图产生一个临时文件。
  • 安全临时文件的产生和使用更需要使用随机的临时文件名。文件名的产生应该使用原子操作,正如文件锁的产生过程那样。
  • 对文件实现最小访问也是非常重要的,在大多数情况下,只有创建该文件的程序的所有者才可以访问。
  • 当在共享的临时目录中创建文件时,其权限应指定为仅文件的所有者或者系统管理员可以删除。
  1. 与其他程序的交互
  • 除了使用操作系统和标准库函数提供的功能之外,程序也可以使用其他程序提供的服务。
  • 与保护多个程序问数据流的机密性和完整性有关。当多个程序运
    行在同一个计算机系统上时,合理使用比如管道和临时文件等系统功能可以提供这样的保护。如果程序运行在不同的系统上,且系统间通过网络相连,那么这些网络连接应该使用相应的安全机制。可供选择的安全机制有IP Security (IPSec )、 Transport Layer/Security Socket Layer Security ( TLS/SSL )或Secure Shell (SSH )连接。
  • 从安全的角度讲,检测和处理程序交互中产生的异常和错误也很重要。当一个进程调用另一个程序作为子进程时,父进程必须保证子进程正常终止井接受它的退出状态。进程还必须捕获井处理与其他程序或操作系统交互过程中产生的信号。

处理程序的输出


实例:
  • 第一个例子涉及使用的纯文本终端。这些终端通常支持一系列的功能键,编写这个程序要求当按下这些功能键的时候可以发送任何需要的字符序列,程序通过发送一个特定的转义(escape )序列实现。终端识别这些序列,不是将这些字符显示出来,而是执行请求的操作。
  • 攻击者操纵正常用户在其终端上显示一些经过仔细设计的文本。这可以通过诱使正常用户运行一个程序实现,比如将程序包含在电子邮件中,或者直接写在其终端上,如果用户允许的话。除了显示一些无关的信息扰乱正常用户外,文本中还可以包含一些转移序列,首先编程实现发送特定命令的功能键,接着是发送文本的命令,就好像经编程的功能键被按下一样。如果显示文本的程序不久就退出了,那么它发送的对应特定功能键的文本就被视为目标用户键入的下一个命令。因而,攻击者可以完成该用户的所有操作,可能包括删除用户文件或更改用户的口令。
  • 我们吸取的主要教训与用户期望的发送到用户终端显示的输出数据的类型有关。另一个教训是要确保不可信资源不允许直接输出到用户界面上
  • 第三个例子,是利用一些Web服务器上的留言板进行的跨站点脚本(XSS )攻击。如果该留言板应用程序没有充分检查和清理用户提供的输入,那么接下来在用户浏览这些留言时这个输入能够用于实现一次攻击。
  • 是不同的字符集允许对元字符的不同编码方式,这会改变对有效输出的解释。如果特定的显示程序或设备对于采用的具体的编码方式并不知情,那么就可能引发对程序的不同假设,从而可能导致过滤失败。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章