辅佐措施员摆脱逆境的十条能力
副标题#e#
安详专家Michael Howard和Keith Brown提出了十条能力来辅佐您摆脱逆境。
安详问题涉及很多方面。安详风险大概来自任那里所。您大概编写了无效的错误处理惩罚代码,可能在赋予权限时过于慷慨。您大概健忘了在您的处事器上正在运行什么处事。您大概接管了所有用户输入。如此等等。为使您在掩护本身的计较机、网络和代码方面有个精采初步,这里展示了十条能力,遵循这些能力可以得到一个更安详的网络计策。
1. 信任用户的输入会将本身置于险境
纵然不阅读余下的内容,也要记着一点,“不要信任用户输入”。假如您老是假设数据是有效的而且没有恶意,那么问题就来了。大大都安详单薄环节都与进攻者向处事器提供恶意编写的数据有关。
信任输入的正确性大概会导致缓冲区溢出、跨站点剧本进攻、SQL 插入代码进攻等等。
让我们具体接头一下这些潜在进攻方法。
2. 防备缓冲区溢出
当进攻者提供的数据长度大于应用措施的预期时,便会产生缓冲区溢出,此时数据会溢出到内部存储器空间。缓冲区溢出主要是一个 C/C++ 问题。它们是种威胁,但凡是很容易修补。我们只看到过两个不明明且难以修复的缓冲区溢出。开拓人员没有预推测外部提供的数据会比内部缓冲区大。溢出导致了内存中其他数据布局的粉碎,这种粉碎凡是会被进攻者操作,以运行恶意代码。数组索引错误也会造成缓冲区下溢和超限,但这种环境
没那么普遍。
请看以下 C++ 代码片断:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc)
{
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
问题在那边?事实上,假如 cBuffSrc 和 cbBuffSrc 来自可信赖的源(譬喻不信任数据并因此而验证数据的有效性和巨细的代码),则这段代码没有任何问题。然而,假如数据来自不行信赖的源,也未获得验证,那么进攻者(不行信赖源)很容易就可以使cBuffSrc 比 cBuffDest 大,同时也将 cbBuffSrc 设定为比 cBuffDest 大。当 memcpy将数据复制到 cBuffDest 中时,来自 DoSomething 的返回地点就会被变动,因为cBuffDest 在函数的仓库框架上与返回地点相邻,此时进攻者即可通过代码执行一些恶意操纵。
补充的要领就是不要信任用户的输入,而且不信任 cBuffSrc 和 cbBuffSrc 中携带的任何数据:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc)
{
const DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}
此函数展示了一个可以或许淘汰缓冲区溢出的正确编写的函数的三个特性。首先,它要求挪用者提供缓冲区的长度。虽然,您不能盲目相信这个值!接下来,在一个调试版本中,代码将探测缓冲区是否真的足够大,以便可以或许存放源缓冲区。假如不能,则大概触发一个会见斗嘴并把代码载入调试器。在调试时,您会诧异地发明竟有如此多的错误。最后也是最重要的是,对 memcpy 的挪用是防止性的,它不会复制多于方针缓冲区存放本领的数据。
在 Windows Security Push at Microsoft(Microsoft Windows? 安详敦促勾当)中,我们为 C 措施员建设了一个安详字符串处理惩罚函数列表。您可以在 Strsafe.h: SaferString Handling in C(英文)中找到它们。
#p#副标题#e#
3. 防备跨站点剧本
跨站点剧本进攻是Web特有的问题,它能通过单个Web页中的一点隐患危害客户端的数据。想像一下,下面的ASP.NET代码片断会造成什么效果:
<script language=c#>
Response.Write("您好," + Request.QueryString("name"));
</script>
有几多人曾经见过雷同的代码?但令人惊奇的是它有问题!凡是,用户会利用雷同如下的URL会见这段代码: http://explorationair.com/welcome.aspx?name=Michael[m
该C#代码认为数据始终是有效的,而且只是包括了一个名称。但进攻者会滥用这段代码,将剧本和HTML代码作为名称提供。假如输入如下的 URL http://northwindtraders.com/welcome.aspx?name=<script>alert(‘您好!’);</script>
您将获得一个网页,上面显示一个对话框,显示“您好!”。您大概会说,“那又奈何?”想像一下,进攻者可以诱导用户点击这样的链接,但查询字符串中却包括一些真正危险的剧本和HTML,由此会获得用户的 cookie 并把它发送到进攻者拥有的网站;此刻进攻者便得到了您的私人cookie 信息,或者会更糟。
#p#分页标题#e#
要制止这种环境,有两种要领。第一种是不信任输入,并严格限制用户名所包括的内容。譬喻,可以利用正则表达式查抄该名称是否只包括一个普通的字符子集,而且不太大。以下 C# 代码片断显示了完成这一步调的要领:
Regex r = new Regex(@"^[\w]{1,40}$");
if (r.Match(strName).Success)
{
// 好!字符串没问题
}
else
{
// 欠好!字符串无效
}
这段代码利用正则表达式验证一个字符串仅包括 1 到 40 个字母或数字。这是确定一个值是否正确的独一安详要领。
HTML 或剧本不行能蒙混过此正则表达式!不要利用正则表达式寻找无效字符并在发明这种无效字符后拒绝请求,因为容易呈现遗漏的环境。第二种防御法子是对所有作为输出的输入举办HTML编码。这会淘汰危险的HTML标志,使之酿成更安详的转义符。您可以在 ASP.NET 中利用 HttpServerUtility.HtmlEncode,可能在 ASP 中利用Server.HTMLEncode 转义任何大概呈现问题的字符串。
4. 不要请求 sa 权限
我们要接头的最后一种输入信任进攻是 SQL 插入代码。很多开拓人员编写这样的代码,即获取输入并利用该输入来成立 SQL 查询,进而与靠山数据存储(如 Microsoft SQL Server 或 Oracle)举办通信。
请看以下代码片断:
void DoQuery(string Id)
{
SqlConnection sql=new SqlConnection(@"data source=localhost;" + "user id=sa;password=password;");
sql.Open();
sqlstring= "SELECT hasshipped" + " FROM shipping WHERE id='" + Id + "'";
SqlCommand cmd = new SqlCommand(sqlstring,sql);
...
}
这段代码有三个严重缺陷。首先,它是以系统打点员帐户 sa 成立从Web处事到SQL Server的毗连的。不久您就会看到这样做的缺陷地址。第二点,留意利用“password”作为 sa帐户暗码的智慧做法!但真正值得存眷的是结构 SQL 语句的字符串通接。假如用户为 ID 输入 1001,您会获得如下 SQL 语句,它是完全有效的。
SELECT hasshipped FROM shipping WHERE id = ‘1001’
但进攻者比这要有创意得多。他们会为 ID 输入一个“’1001′ DROP table shipping –”,它将执行如下查询:
SELECT hasshipped FROM shipping WHERE id = ‘1001’ DROP table shipping — ‘;
它变动了查询的事情方法。这段代码不只会实验判定是否装运了某些货品,它还会继承 drop(删除)shipping 表!操纵符 — 是 SQL 中的注释操纵符,它使进攻者可以或许更容易地结构一系列有效但危险的 SQL 语句!
这时您也许会以为奇怪,怎么任何一个用户都能删除 SQL Server 数据库中的表呢。虽然,您是对的,只有打点员才气做这样的事情。但这里您是作为 sa 毗连到数据库的,而 sa 能在 SQL Server 数据库上做他想做的任何事。永远不要在任何应用措施中以 sa毗连 SQL Server;正确的做法是,假如符合,利用 Windows 集成的身份验证,可能以一个预先界说的具有适当权限的帐户毗连。
修复 SQL 插入代码问题很容易。利用 SQL 存储进程及参数,下面的代码展示了建设这种查询的要领 – 以及如何利用正则表达式来确认输入有效,因为我们的生意业务划定货运ID 只能是 4 到 10 位数字:
Regex r = new Regex(@"^\d{4,10}$");
if (!r.Match(Id).Success)
throw new Exception("无效 ID");
SqlConnection sqlConn= new SqlConnection(strConn);
string str="sp_HasShipped";
SqlCommand cmd = new SqlCommand(str,sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
缓冲区溢出、跨站点剧本和 SQL 插入代码进攻都是信任输入问题的示例。所有这些进攻都能通过一种机制来减轻危害,即认为所有输入都是有害的,除非得到证明。
5. 留意加密代码!
下面我们来看些会让我们受惊的对象。我发明我们查抄的安详代码中百分之三十以上都存在安详裂痕。最常见的裂痕大概就是本身的加密代码,这些代码很大概不堪一击。永远不要建设本身的加密代码,那是徒劳的。不要认为仅仅因为您有本身的加密算法其他人就无法破解。进攻者能利用调试器,他们也有时间和常识来确认系统如何事情 – 凡是在几小时内就会破解它们。您应该利用 Win32? 的CryptoAPI,system.Security.Cryptography 定名空间提供了大量优秀且颠末测试的加密算法。
6. 淘汰本身被进攻的大概性
假如没有百分之九十以上的用户要求,则不该默认安装某一成果。Internet Information Services (IIS) 6.0 遵循了这一安装发起,您可以在这个月宣布的 Wayne Berry 的文章“Innovations in Internet Information Services Let You Tightly Guard Secure Data and Server Processes”中读到相关内容。这种安装计策背后的思想是您不会留意本身并未利用的处事,假如这些处事正在运行,则大概被其他人操作。假如默认安装某成果,则它应在最小授权原则下运行。也就是说,除非须要,不然不要答允利用打点员权限运行应用措施。最好遵循这一忠告。
7. 利用最小授权原则
#p#分页标题#e#
出于若干原因,操纵系统和民众语言运行时有一个安详计策。许多人觉得此安详计策存在的主要原因是防备用户有意粉碎:会见他们无权会见的文件、从头设置网络以到达他们的要求以及其他恶败行为。简直,这种来自内部的进攻很普遍,也需要防御,但尚有另一个原因需要严守这一安详计策。即在代码周围成立起防御壁垒以防备用户有意或(正如常常产生的)无意的操纵对网络造成严重粉碎。譬喻,通过电子邮件下载的附件在Alice
的呆板上执行时被限制为只能会见 Alice 可以会见的资源。假如附件中含有特洛伊木马,那么好的安详计策就是限制它所能发生的粉碎。当您设计、成立并陈设处事器应用措施时,您不能假设所有请求都来自正当用户。假如一个坏家伙发送给您一个恶意请求(进展不会如此)并使您的代码发生恶劣操纵,您会但愿您的应用措施拥有所有大概的防护来限制损害。因此我们认为,您的公司实施安详计策不只是因为它不信任您或您的代码,同时也是为了掩护不受外界有诡计的代码的伤害。
最小授权原则认为,要在最少的时间内授予代码所需的最低权限。也就是说,任何时候都应在您的代码周围竖起尽大概多的防护墙。当产生某些欠好的工作时 – 就象 Murphy定律担保的那样 – 您会很兴奋这些防护墙都处在符合的位置上。因此,这里就利用最小授权原则运行代码给出了一些详细要领。
为您的处事器代码选择一个安详情况,仅答允其会见完成其事情所必须的资源。假如您代码中的某些部门要求很高的权限,请思量将这部门代码疏散出来并单独以较高的权限运行。为安详疏散这一以差异的操纵系统验证信息运行的代码,您最亏得一个单独的历程(运行在具有更高权限的安详情况中)中运行此代码。这意味着您将需要历程间通讯(如COM 或 Microsoft .NET 长途处理惩罚),而且需要设计该代码的接口以使来回行程最小。
假如在 .NET Framework 情况中将代码疏散成措施集,请思量每段代码所需的权限级别。您会发明这是一个很容易的进程:把需要较高权限的代码疏散到可赋予其更多权限的单独的措施会合,同时使其余大部门措施集以较低的权限运行,从而在您的代码周围添加更多的防护。 在举办此操纵时,不要忘了,由于代码会见安详 (CAS) 仓库的浸染,您限制的不只是本身措施集的权限,也包罗您挪用的任何措施集的权限。
很多人成立了本身的应用措施,使得其产物在测试并提供应客户后可以插入新的组件。掩护这种范例的应用措施很是坚苦,因为您无法测试所有大概的代码路径来发明错误和安详裂痕。然而,假如您的应用措施是托管的,则 CLR 提供了一个极好的成果,可以利用它封锁这些可扩展点。通过声明一个权限工具或一个权限集并挪用 PermitOnly 或Deny,您可觉得本身的仓库添加一个标志,它将阻塞授予您挪用的任何代码的权限。通过在挪用某个插件之前举办此操纵,您就可以限制该插件所能执行的任务。譬喻,一个用于计较分期付款的插件不需要任何会见文件系统的权限。这只是最小权限的另一个例子,由此您可以事先掩护本身。请确保记录下这些限制,并留意,具有较高权限的插件可以或许利用Assert 语句逃避这些限制。
8. 留意失败模式
接管它吧。其他人和您一样憎恨编写错误处理惩罚代码。导致代码失败的原因如此浩瀚,一想到这些就让人沮丧。大大都措施员,包罗我们,更愿意存眷正常的执行路径。哪里才是真正完成事情的处所。让我们尽大概快而无痛地完成这些错误处理惩罚,然后继承下一行真正的代码吧。
只惋惜,这种情绪并不安详。相反,我们需要更密切地存眷代码中的失败模式。人们对这些代码的编写凡是很少深入留意,而且经常没有颠末完全测试。还记得最后一次您完全必定调试过函数的每一行代码,包罗个中每一个很小的错误处理惩罚措施是什么时候?
未经测试的代码常会导致安详裂痕。有三件工作可以辅佐您减轻这个问题。首先,对那些很小的错误处理惩罚措施给以和正常代码同样的存眷。思量当您的错误处理惩罚代码执行时系统的状态。系统是否处于有效而且安详的状态中?其次,一旦您编写了一个函数,请慢慢将它彻底调试几遍,确保测试每一个错误处理惩罚措施。留意,纵然利用这样的技能,也大概无法发明很是隐秘的计时错误。您大概需要给您的函数通报错误参数,可能以某种方法调解系统的状态,以使您的错误处理惩罚措施得以执行。通过花时间单法式试代码,您可以慢下来并有足够的时间来查察代码以及系统运行时的状态。通过在调试器中仔细单步执行代码,我们在本身的编程逻辑中发明白很多缺陷。这是一个已获得证明的技能。请利用这一技能。最后,确保您的测试组合能使您的函数举办失败测试。只管使测试组合可以或许检讨函数中的每一行代码。这能辅佐您发明纪律,出格是当使测试自动化并在每次成立代码后运行测试时。关于失败模式尚有一件很是重要的工作需要说明。当您的代码失败时要确保系统处于大概的最安详状态。下面显示了一些有问题的代码:
#p#分页标题#e#
bool accessGranted = true; // 过于乐观!
try
{
// 看看我们可否会见 c:\test.txt
new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read).Close();
}
catch (SecurityException x)
{
// 会见被拒绝
accessGranted = false;
}
catch (...)
{
// 产生了其他工作
}
尽量我们利用了 CLR,我们仍被答允会见该文件。在这种环境下,并没有激发一个SecurityException。可是,譬喻,假如文件的自由会见节制列表 (DACL) 不答允我们会见呢?这时,会激发另一种范例的异常。但由于代码第一行的乐观假设,我们永远也不会知道这一点。编写这段代码的一种更好的要领就是持审慎立场:
bool accessGranted = false; // 保持审慎!
try
{
// 看看我们可否会见 c:\test.txt
new FileStream(@"c:\test.txt",FileMode.Open,FileAccess.Read).Close();
// 假如我们还在这里,那么很好!
accessGranted = true;
}
catch (...) {}
这样会越发不变,因为无论我们如何失败,总会回到最安详的模式。
9. 模仿方法很是容易受到进攻
编写处事器应用措施时,您经常会发明本身直接或间接利用了 Windows 的一个称为模仿的很利便的成果。模仿答允历程中的每个线程运行在差异的安详情况中,凡是是客户端的安详情况。譬喻,当文件系统重定向器通过网络收到一个文件请求时,它对长途客户端举办身份验证,查抄以确认客户端的请求没有违反共享上的 DACL,然后把客户端的标志附加处处理惩罚请求的线程上,从而模仿客户端。然后此线程便可以利用客户端的安详情况会见处事器上的当地文件系统。由于当地文件系统已经是安详的,因此这样做很利便。它会思量所请求的会见范例、文件上的 DACL 和线程上的模仿标志来举办一个会见查抄。假如会见查抄失败,当地文件系统会将其陈诉给文件系统重定向器,然后重定向器向长途客户端发送一个错误。毫无疑问,对文件系统重定向器来说这很利便,因为它只是简朴地把请求传给当地文件系统,让它去做本身的会见查抄,就好象客户端在当地一样。这对付文件重定向器这样简朴的网关而言,一切精采。但模仿经常用在其他更巨大的应用措施中。以一个Web应用措施为例。假如您编写一个经典的非托管 ASP 措施、ISAPI 扩展或ASP.NET 应用措施,在它的Web.config 文件中有如下指定
<identity impersonate=’true’>
那么您的运行情况将有两种差异的安详情况:您将具有一个历程标志和一个线程标志,一般来说,线程标志会被用来做会见查抄(见图)。假设您正在编写一个在Web处事器历程中运行的 ISAPI 应用措施,并假定大大都请求未经身份验证,则您的线程标志大概是 IUSR_MACHINE,而历程标志却是 SYSTEM!假设您的代码能被一个坏家伙通过缓冲区溢出操作。您认为他会只满意作为 IUSR_MACHINE 运行吗?虽然不会。他的进攻代码很大概会挪用 RevertToSelf 以删除模仿标志,从而但愿提高他的权限级别。在这种环境下,他会很容易得到乐成。他还可以挪用 CreateProcess。它不会从模仿标志复制新历程的标志,而是从历程标志复制,这样新历程便可以作为 SYSTEM 运行。
那么奈何办理这个小问题呢?除了首先确保不呈现任何缓冲区溢出外,还要记着最小授权原则。假如您的代码不需要具有 SYSTEM 这样大的权限,则不要将Web应用措施设置为在Web处事器历程中运行。假如只是将Web应用措施设置为在中等或较高的断绝情况中运行,您的历程标志将会是 IWAM_MACHINE。您实际上没有任何权限,因而这种进攻险些不会生效。留意,在 IIS 6.0(即将成为 Windows .NET Server 的一个组件)中,默认环境下用户编写的代码不会作为 SYSTEM 运行。基于这样的认识,即开拓人员确实会出错误,Web 处事器就淘汰赋予代码的权限而提供的任何辅佐都是有益的,以免万一代码中存在安详问题。
#p#分页标题#e#
下面是别的一个 COM 措施员大概碰着的隐患。COM 有一个欠好的倾向就是对于线程。假如您挪用一个历程内 COM 处事器,而其线程模子与挪用线程的模子不匹配,则 COM会在另一个线程上执行挪用。COM 不会流传挪用者线程上的模仿标志,这样功效就是挪用会在历程的安详情况中执行,而不是在挪用线程的安详情况中。何等令人受惊!
下面是另一个由模仿带来的隐患的环境。假设您的处事器接管通过定名管道、DCOM或 RPC 发送的请求。您对客户端举办身份验证并模仿它们,通过模仿以它们的名义打开内核工具。而您又忘了在客户端断开毗连时封锁个中的一个工具(譬喻一个文件)。当下一个客户端进入时,您又对其举办身份验证和模仿,猜猜会产生什么?您仍然可以会见上一个客户端“漏掉”的文件,纵然新的客户端并没有得到会见该文件的权限。出于运行机能的原因,内核仅在第一次打开工具时对其执行会见查抄。纵然您厥后因为模仿其他用户而变动了安详情况,您照旧可以会见此文件。以上提及的这些环境都是为了提醒一点,即模仿为处事器开拓人员提供了利便,但这种利便却具有很大隐患。在您回收一个模仿标志运行措施时,务须要对本身的代码多加留意。
10. 编写非打点员用户可以实际利用的应用措施
这确实是最小授权原则的一定功效。假如措施员继承开拓这样的代码,使得必需是打点员身份的用户才气在 Windows上正常运行,我们就不能期望提高系统的安详性。Windows 有一套很是不变的安详成果,可是假如用户必需具有打点员身份才气举办操纵,他们就不能很好地操作这些成果。
您奈何举办改造呢?首先,本身先实验一下,不以打点员身份运行。您很快就会知道利用没有思量安详设计的措施的疾苦。有一天,我 (Keith) 安装一个由手持设备制造商提供的软件,该软件用于在我的台式机和手持设备之间同步数据。与往常一样,我退出了普通的用户帐户,然后利用内置的打点员帐户再次登录,安装了软件,然后再次登录到普通帐户,而且试图运行软件。功效该应用措施跳出一个对话框,说不能会见某个所需的数据文件,接着便给出一个会见斗嘴信息。伴侣们,这就是某个主流手持设备厂商的软件产物。对这种错误尚有什么捏词吗?
在运行了来自 http://sysinternals.com(英文)的[m FILEMON 之后,我很快发明该应用措施试图打开一个数据文件以举办写入会见,而该文件与应用措施的可执行文件安装在同一目次中。当应用措施如预想的那样安装在 Program Files 目次中时,他们毫不能试图向该目次写入数据。Program Files 具有这样一个限制会见节制计策是有原因的。我们不但愿用户写入这些目次,因为这样会很容易让一个用户留下特洛伊木马措施,而让另一个用户去执行。实际上,这个约定是 Windos XP 的根基符号性要求之一。
我们听到太多的措施员给出捏词说他们为什么在开拓代码时选择作为打点员身份运行。假如我们继承忽略这一问题,只会让工作更糟。伴侣们,编辑一个文本文件并不需要打点员权限。编辑或调试一个措施也不需要打点员权限。在您需要打点员权限时,请利用操纵系统的 RunAs 成果来运行具有较高权限的单独的措施。假如您是在编写给开拓人员利用的东西,那么您将对这个群体负有特另外责任。我们需要遏制这种编写只有以打点员身份才气运行的代码的恶性轮回,要到达这一方针,我们必需从基础上产生改变。