您现在的位置是:首页 > IT基础架构 > 网络与安全 >
在应用程序交付前查找和修补漏洞
2008-09-10 19:31:00作者:Michal Chmielewski来源:
摘要 在软件开发过程中,一个很小的编码错误就可能会导致严重漏洞,并最终降低整个系统或网络的安全性。...
在软件开发过程中,一个很小的编码错误就可能会导致严重漏洞,并最终降低整个系统或网络的安全性。很多时候,安全漏洞并不是由单个错误造成的,而是由于整个开发过程中所发生的一系列错误导致:引入一个编码错误后,在测试阶段未能检测出来,而可用的防范机制又未能阻挡成功的攻击。
在软件开发过程的所有阶段中都必须将安全性置于首要地位。防范重点在于防止出现软件漏洞 — 当然,最好在发布之前就将其检测出来,此外还要限制它们的实际影响(例如,通过减少产品的攻击面)。在 Microsoft,这种整体安全性方法是通过安全开发生命周期 (SDL) 实施的,它涵盖了软件开发的所有主要阶段,包括培训开发人员、改进设计、利用编码和测试惯例并做好产品发布后的紧急响应准备,如图 1 所示。SDL 不是进行代码审阅的唯一方法,但它确实构成了我们这里所要探讨的多数内容的基础。
图 1 安全开发生命周期
在本文中,我们将探讨由开发人员或安全专家人工执行的安全代码审阅工作。在 SDL 所定义的过程中,这些工作通常发生在安全性推进工作或渗透测试过程中,并与最终的安全性检查相关。可以使用不同的方法来查找编码错误,但即使与最先进的工具相比,人工代码审阅在精确性和质量方面的价值也毫不逊色。遗憾的是,执行人工代码审阅的成本也是最高的。
我们还将详细探讨大型软件项目环境中安全代码审阅的优缺点。本文凝聚了过去若干年来对 Microsoft 发布的主要产品所积累的评审工作经验。
软件安全漏洞
对于较大和较复杂的软件产品,决不能想当然地认为不存在任何安全漏洞。应当采取所有可能的步骤来限制编码错误的数量并减少它们的实际影响,但是实际上总会有所遗漏。影响安全性的软件错误(称为漏洞)可存在于应用程序的不同级别,并且可以在开发周期的不同阶段引入。
漏洞不仅限于代码。早在进行要求定义之时就可能引入错误,具体表现为无法以安全方式实现某项要求。产品的基本设计也可能包含缺陷。例如,可能选择了不恰当的技术,或者从安全角度看,它们的使用可能不正确。理想情况下,所有这些问题都应在产品开发的早期阶段通过设计审查或威胁建模发现。
安全代码审阅的主要目的是找出仍会导致大多数安全漏洞的代码级别问题。这些工作也可能会发现设计问题,但是这类问题可能与威胁建模的必要改进以及开发过程的其他方面相关。源代码审阅的开展也可以根据非安全性相关的优先级来执行。但在此特定环境中,其目标是找出可被用来破坏产品的各项安全保证或有损系统安全性的代码漏洞。
可能导致安全问题(例如因缺乏验证而导致的问题)的编码错误必须在满足特定条件时才会构成安全漏洞。必须存在要攻击的安全边界,并且攻击者需要对数据或环境拥有某种程度的控制能力。如果代码在与攻击者相同的安全环境中执行,则代码中可能存在的问题不会为攻击者利用漏洞提供任何帮助。在另外一些情况下,虽然存在漏洞,但它们位于无法执行的代码中,因为攻击者无法访问它们。虽然这些编码错误可能会影响产品的可靠性,但不应将它们视为真正的漏洞。安全代码审阅的最终目标是找出攻击者可以访问、并且可能允许攻击者绕过安全边界的代码漏洞。
请注意,漏洞的可访问性并不等同于它的可利用性;平台功能的增强(例如 /GS 标志、/SafeSEH 标志或地址空间布局随机化 (ASLR) 仍有可能削弱成功攻击的影响。
代码漏洞的可利用性不在代码审阅过程中所执行的调查范围之内,主要原因是通常无法证明是否拥有足够的应对漏洞利用情况的缓解措施。代码审阅者的责任仅在于确认某代码漏洞的存在,并且它得到了适当的会审。此时只应将 bug 视为其他某个需要修复的问题。
发现代码漏洞是安全代码审阅的主要目标,但从中也会得到其他收获。审阅者可以提供有关整体代码质量、冗余情况、死代码或不必要复杂性的反馈。审阅者还可能会提供一些有关减小攻击面、数据验证、代码清理或代码可读性等方面的改进建议,例如改进注释。但是,由于整理这些内容需要时间,因此应当在开始之前就决定是要在结果中包含这类建议,还是只应专注于标识安全问题。
查找编码错误
查找编码错误有多种不同方法。由于每种方法都各有其独特的优点以及实践的局限性,因此了解代码审阅与其他选择之间的差异非常重要。为了撰写本文的目的,我们假设源代码和设计文档都可供使用,并执行了一种所谓的“白盒”产品分析(一种内部分析,与专注于外部可见行为的“黑盒”方法相对)。
代码审阅可以描述为一种查找编码错误的人工静态方法。使用类似的描述,我们可以将另外两种常用方法描述为自动静态方法和自动动态方法。自动静态方法通常采用静态代码分析工具的形式,用这些工具操作源代码以尝试发现使用模式定义的已知问题类型。例如,PREfix 和 PREfast 就属于这种方法。自动动态方法是指各种动态代码测试技术(例如模糊化),这些技术主要专注于文件、协议和 API。虽然这些解决方案也适用于黑盒方法,但如果能够获得并正确使用有关内部元素(例如文件格式)的信息,则通常可以获得更好的结果。
每种方法都有某些实践上的优点和局限性。静态代码分析工具可以通过自动执行来处理更多代码,但是所查找的结果会严格局限于一系列已知问题类型的预定义模式。而且结果通常会包含大量误报,使得在有限的资源下难以解决这些问题。模糊测试可轻松实现自动连续执行,但它至少要在一种部分随机的模式下运行,并且在触及更深层次的代码部分时可能会出现问题。大多数情况下,执行基本模糊化比较简单,但要完全覆盖关键代码路径就要困难得多。
与自动方法相比,人工源代码审阅的优缺点主要与评审人员的直接参与有关。评审的结果可能会有很大差异,因为这取决于参与者在特定技术、体系结构和各种情况方面的经验。一般而言,应该说人工审阅者还是要优于基于计算机的解决方案。审阅者可以了解整个过程和软件组件的环境,并可与设计人员和开发人员沟通。此外,审阅者还可以提供反馈,其中不仅限于详细的问题报告,还可以包括一些高层次的建议。
但是,与此同时,审阅者也容易受到人为错误、疲劳或厌倦情绪的影响。评审的范围通常很有限,评审质量可能难以评估,因为这通常是基于审阅者的主观臆断。
但是,这些缺点是可以弥补的。您可以设计一个特定任务的评审,对一个代码块进行多次分析,每次仅针对一种错误类型。另一种方法是由多位审阅者对一段关键代码独立进行评审,这样便可以限制出现人为错误的可能性。代码审阅属于这样一种特定情况,即通过冗余操作可以获得巨大的潜在价值,因为这样可以克服人工参与的局限性。
决不能将人工代码审阅视为查找代码漏洞的终极解决方案,或者认为它可以取代其他方法,而只应将其视为一种互补性解决方案。代码审阅决不能用来取代威胁建模、模糊化或实施编码最佳实践。与其他方法相比,代码审阅的成本通常更高,因此应主要将其用在其他方法难以发挥有效作用的最关键领域和问题上。
代码审阅过程
如果在其他安全相关活动(例如威胁建模)环境中计划和执行安全代码审阅,则会取得最佳效果(请参见图 2)。此外,代码审阅的结果还可以通过改进其他安全任务(例如测试和设计)来提供更多附加价值。
图 2 代码审阅
良好的威胁模型对于代码审阅而言至关重要。若要执行一次成功的代码审阅,审阅者必须非常清楚产品的目标、具体设计以及使用的实现技术。前两项也包含在威胁建模的范围内,虽然它们专注于查找高层面的问题,但其中包含的研究工作对于代码审阅也很有用。通常这两类安全检查措施是互补的;威胁建模帮助找出关键的代码部分,然后对此代码部分进行详细审阅。代码审阅的结果也可用来验证(或质疑)威胁模型中所指定的安全性假设。
在理想情况下,安全代码审阅将首先评审质量威胁模型和设计规范,然后再转到源代码。在开始之前,应完成评审范围内的所有代码开发工作。为了捕获较为简单的问题,应在人工评审开始之前针对代码运行适当的安全工具。最后,为避免查找已知的问题,应修复之前发现的所有安全 bug。
安全团队应在开发生命周期前期就充分做好代码审阅工作的计划,以便确定最适宜的执行时机。实际计划还应明确组织决策,这可能会影响评审工作的整体效果。例如,选择代码审阅参与者的方法可能有三种。第一种,审阅者可以不了解代码和产品(例如来自渗透测试团队的代码审阅专家)。第二种,审阅者可以不了解代码但熟悉产品(例如来自同一公司内其他团队的开发人员)。最后,审阅者可以既熟悉代码又熟悉产品(例如代码的开发人员)。
每种选择都有优点和局限性。如果要采用不熟悉产品的审阅者,通常会选择有经验的安全专家,由于其独特的经验和视角,他们的意见往往大相径庭。但是,多数情况下,与产品团队的成员相比,他们对产品的内部和实现详情了解较少。通常,负责编写特定代码段的开发人员对代码了解最深,但是他们也最易受到代码创建者偏好的影响,并因此而可能忽视明显的编码错误。
没有一个选择是可自动应用于每种情况的终极解决方案。代码审阅更多取决于参与人员而不是技术解决方案,因此需要负责执行评审的团队进行取舍。有些团队通过在团队会议上讨论和分析代码获得最佳结果。在其他情况下,开发人员可通过自行检查代码获得最佳结果。团队应选择能够使参与者有效评审产品的所有策略。这一基本规则不仅适用于代码审阅的计划和组织,也适用于此类过程中需要面对的所有难题。
排定评审优先级
代码审阅最重要的规则是要意识到一点,即永远没有足够的时间来审阅您希望审阅的所有代码。因此,最大的难题之一就是选择主要分析(或至少是初步分析)范围中包含的产品组件或代码段,并排定其优先级。为实现此目标,必须了解产品的设计以及特定实现技术的角色。排定优先级是一种高层面的分析,旨在定义整个过程的框架。
排定优先级的第一步是分析威胁模型和可用于产品的其他文档。正确完成后,威胁模型可以提供大量有用的数据,它们与不同组件间的关系、入口点、安全边界、依赖性以及设计或实现中的假设有关。由于排定优先级的目的是了解实现细节,因而接下来必须将来自威胁模型的信息与代码本身进行比较,分析有关代码结构、质量或开发过程的信息。
排定优先级可分为四个主要任务:
第一项任务涉及了解代码的技术环境。该环境不仅涵盖产品中使用的特定技术,还包括操作系统和第三方依赖性以及开发中使用的各种工具。此任务的目的是标识产品与其他系统、应用程序或服务之间的关系。基于这些关系,可以确定产品所依赖的组件以及依赖于您的产品的其他软件。在安全环境中,这些关系确定了产品与系统其余部分如何相互影响。通过此过程可以暴露某些高危险区域。
高危险区域通常与诸如安全边界和潜在攻击隐患等概念相关联。在实践中,所有这些都可以归结到可由攻击者控制的数据(应将其视为不受信任的数据)以及这类数据可能源自的入口点。团队必须根据传入数据的保证和假设对每个入口点进行分析。一个关键问题是何时以及如何验证数据。该问题通常与实际数据特征(例如,能否异步更改或不按顺序发送?)相关。它还可能与入口点的特征相关,例如默认情况下它是否可用(网络服务或编程接口),或者它的创建是否为用户操作(单击或打开数据文件)的结果。
入口点的特征也在下一项重要任务考察范围之内:即信任关系和访问控制分析。产品可能具有许多不同的入口点,但其实际攻击面只由那些可由攻击者跨越安全边界访问的入口点定义。因此,应调查每个入口点以确认谁以及在什么条件下可以访问它。与只能使用管理权限访问的入口点相比,显然攻击者对不经身份验证即可访问广泛功能更感兴趣。对于后者而言,评审工作可以只限定在负责实际身份验证的代码段(包括其前面的代码)。
最后但并不容忽视的一点是,非技术性源也可以为排定代码审阅的优先级提供有用数据。代码通常由开发人员团队创建;随着不断有新成员加入以及有人离去,团队通常会随时间推移而发生变动。代码也有其自己的安全历史记录,有时不仅限于特定产品的范围内。多数情况下,可以假定新代码的质量比旧代码好,但也会有例外。和开发人员的交流、查阅以前评审的评审文档或对代码取样都可能会提供非常有用的信息。为了充分利用提供的资源,安全团队应使用所有数据以帮助审阅者有效完成整个过程。
代码审阅策略
大型应用程序的代码审阅面临的挑战主要来自软件的规模和复杂性。现代产品都是使用各种不同的技术、编程模型、编码风格以及代码管理策略创建的。在实践中,很难获得完整和最新的文档。审阅者通常会面临大量信息,难以在一个可接受的时间范围内由人工处理完毕。为了应对评审复杂产品过程中超负荷的信息量,人们开发了一些实践方法,称之为策略。我们将探讨其中最常用的两种。
第一种策略称为上下文代码分析,它是排定优先级和高层分析过程的自然结果。它依赖于对数据流(指处理跨越安全边界的不受信任数据所直接涉及的代码区域的数据流)进行精确和感知上下文的分析。这种有针对性的方法的优点是可以专注于更有可能受到攻击的代码,从而更有效地涵盖最关键的代码路径范围。
上下文代码分析可分为两个阶段。第一阶段从一个入口点开始,并了解攻击者对输入数据的控制程度。由此开始,开发人员将执行一个数据流分析,旨在跟踪变量和数据结构如何随程序执行而发生变化。该分析将针对每个函数执行,从输入参数开始,然后经过所有执行路径,同时监视变量和结构的状态变化。
对于被调用且接受由攻击者控制的数据(来自入口点但尚未经过验证)的每个函数,都应按照相同方式进行分析。同样,应对传播到全局数据结构的所有数据进行标记,这样以后便可分析对它的每个引用。此时需要通览整个代码,以加深对它的了解,掌握其结构,并标记需要进一步详细审查的位置。
显然,在安全代码审阅过程中,需要特别注意更有可能存在安全问题的代码部分。
如果特定代码没有处理任何可能源自某个入口点的数据,或者处理已经过验证的数据,那么它并不涉及安全问题,审阅者可以继续其他部分。在一天的工作结束后,审阅者应当对组件的代码以及代码中标记为潜在兴趣点的位置列表有个基本了解。使用此列表,审阅者可以开始第二阶段的上下文分析,此过程专注于深入调查选定的代码部分。
我们将第二个代码审阅策略称为以模式为中心的代码分析。在此情况下,审阅者可以从代码中的任意位置开始,并查找已知类型的潜在安全漏洞。虽然可以应用不同的支持工具,但“已知”并非一定特指任何正式的模式,而是指审阅者个人的经验和直觉。对于每个可能的漏洞,审阅者将检查所有代码路径以确定编码错误是否确实代表漏洞 — 处理可由攻击者越过安全边界进行控制的数据。如果在任何级别标识了正确的验证,则不应将该错误视为安全漏洞,尽管它仍可能被标识为需要修复的深层防御问题或非安全性问题。
使用以模式为中心的代码分析无需对应用程序有更多了解,因为审阅者可以专注于选定代码段的质量和正确性,而不是它们在系统中的角色或位置。这样便可以针对特定模式(例如不良代码构造或有问题的 API 调用)覆盖大量代码。但是,在脱离整个应用程序的环境之外分析潜在编码错误通常无法提供足够信息来确定它是否确实是一个问题。这可能会导致忽视严重的 bug 或修复不相关的 bug,为应用程序带来长期的后续附加影响。如果只在本地环境中描述某个 bug,则可能会引入一个本地修复,而不是更高级别的更完备的验证。这种修复编码错误的方法可能会导致冗余验证、增加混乱情况、降低性能,以及出现与代码管理有关的一般问题。
漏洞类型
在安全代码审阅过程中,请尝试保持攻击者的视角,并查看可能受控于攻击者的一切事项。执行代码审阅不应局限于查找危险的 API 和数据复制操作。就安全性而言,细节是非常重要的,历史经验表明,即使最小的缺陷也可利用。本部分包含了某些类型的编码错误示例,但这里无法涵盖所有情况。“安全资源”侧栏会将您导向更多有关可能漏洞的综合信息源。
安全代码漏洞的一般概念通常与缓冲区溢出相关联。在处理不受信任数据的情况下,与范围和数据类型相关的编码错误仍是非常普遍的安全问题源。但是,这类漏洞不仅限于缓冲区溢出。与缓冲区溢出相关的情况是将数据复制到堆栈、数据段或堆时不考虑其大小,这可能会导致写到定义的范围之外,并可能会因为覆盖控制程序流或其他敏感数据的结构而造成各种危险状况。
与范围验证相关,而且同样常见和严重的编码错误示例还有缓冲区外的读写。当使用从不受信任的源接收的指针(使用指针作为 cookie 或句柄值)或错误计算的索引和偏移量时,经常会发生这些特定问题。它们使攻击者能够访问进程内存中的任意位置(例如数据变量或函数指针),并会导致程序行为发生改变,包括执行任意代码。
在计算范围内,很多数值类型变量的转换(加、减、乘等)可能会导致计算脱离其定义的范围并在无提示的情况下换行,从而导致整数溢出或下溢。如果在转换后使用该变量,同时又在别处使用它(未经转换或经不同方式转换),这就会成为一个问题。因此,现有的检查可能最终并不充分,从而导致缓冲区溢出或缓冲区外的读写。
其他问题与数值类型变量的符号有关。程序处理的许多抽象实体对于负值没有明确的意义。例如,字符串字节长度或数组元素计数作为负值没有实际意义 — 使用带符号的类型来处理这些数量值将会带来许多问题。
强制类型转换(显式或隐式)是另一个潜在的不安全数据元素转换示例。强制转换可能会使相关项变大(从位的角度衡量),因而可能会将带符号的位传播到新值中。同样,强制类型转换还会导致值的截断。再次重申,如果数据项在程序的某个部分中以转换后的状态使用,同时又在另一个部分中以未转换或不同的转换状态使用,则通常会出现问题。
有一些与使用动态内存相关的特定安全代码漏洞。使用未初始化的数据元素的程序行为可能未被定义。攻击者或许能够在调用初始化变量的 API 之前通过调用 API 获取或设置未初始化的变量,从而控制一些未初始化的变量。将未初始化的内存返回给攻击者会导致许多问题。由于堆分配所来自的资源可能由服务于不同安全环境(客户端)的不同线程共享,因而敏感信息可能会在客户端之间泄漏。这可能还会给攻击者提供可用来更广泛利用其他安全缺陷(例如帮助攻击者预测地址)的信息。还可能出现其他与重复释放内存、使用已经释放的内存、内存泄漏或内存耗尽相关的问题。造成的后果有多种可能,例如拒绝服务、信息泄漏或执行恶意代码等。
还有一组非常特别的安全代码漏洞,它们与同步和计时错误有关。TOCTOU(time-of-check,time-of-use)竞争条件就是很好的例子。如果数据或资源可能会在检查之后并在实际使用之前被攻击者更改,则安全和净化检查将毫无意义。验证必须在不能异步更改的私有数据副本上执行。需要正确引用资源以确保它们不会被删除或替换。
在对共享对象和资源的访问同步方面,多线程环境尤其对代码(服务、内核组件)提出了严格要求。允许攻击者访问并发函数调用或其异步取消操作的编程接口为可能的竞争条件打开了一个窗口。由缺少锁、删除锁和假设保留状态、误用联锁的操作或者使用一组不相交的锁引起的问题可能会导致不安全的内存操作或死锁。
对于对象生存期管理问题,如果代码通过处理全局数据(发布)使得某个新对象可用于其他线程,那么它需要处于一致(通常已完全初始化)的状态。对象生存期的正确管理由适当的引用计数机制确定。已毁坏对象的析构和清理逻辑可能会给攻击者提供一个机会,使其能够提前卸载代码(例如一个插件)并释放仍在使用中的内存。
很多安全编码错误并不直接与操作不受信任的数据相关,而是与它的实际转译或它对程序行为和结果的影响有关。注入攻击就是典型的例子,当攻击者提交的数据用于参数化某些其他内容(例如脚本、网页、命令行或格式字符串)时便可能发生这种攻击。通过使用特殊的 escape 字符或控制序列,攻击者可以强行将不受信任的数据转译为可执行的脚本或命令的一部分。当使用不受信任的数据对即将创建或使用的资源(文件、套接字、注册表、共享区、全局对象)构造名称和路径时也会出现问题。目录遍历或规范化问题使代码易受所有类型的重定向的影响,从而可能导致使用攻击者控制的资源或泄漏敏感信息(例如,用户秘密或网络凭据)。
安全漏洞也可能源自有关数据源和目标(客户端 ID、网络地址、会话 ID 或上下文句柄)的错误假定、传入顺序(网络消息、API 调用)以及数量(数据过多或过少)。通常,攻击者能够在某种程度上控制代码的执行环境,方法是在真正的产品创建或使用命名对象之前创建它们(名称占用攻击)、填满磁盘空间,或者阻断或重定向网络通信。无法解决这些情况可能会使代码容易受到提升权限或拒绝服务攻击的影响。其他常见问题源于对代码所使用技术的假设,即有关其安全保证的假设(信道的完整性)或其 API 的内部工作的假设(提供不正确的参数组合并且不检查返回值,而不是授予 API 约定)。
代码审阅的结果通常不仅限于找出按代码级别分类的问题。审阅者会在实际实现级别上执行操作,虽然他们可以借助设计级别的文档(包括规范和威胁模型),但他们不应完全依赖这些数据。因此,代码审阅仍会发现设计级别的问题或规范与实现之间的不一致性。常见的各种类型的问题包括暴露危险功能、错误的协议实现、使用自定义的伪安全机制(例如身份验证方案和访问检查),或者发现了绕过安全屏障的途径。
处理结果
如果产品的安全性未得到改进,则认为安全代码审阅是不成功的。代码审阅应提供有用的成果,以供开发团队对产品进行有意义的改进。达到这种基于评审的改进与两个实践要求有关,即完整的文档和准确的会审。
已发现的每个安全代码漏洞的文档记录应包含定位和了解问题所需的所有详细信息。详细信息应包含缺陷代码的指针、问题的说明以及此处为漏洞的理由。添加修复建议是一种很好的做法,但选择和准备实际解决方案则是代码所有者的责任。如果缺失任何数据或者不清楚为什么编码错误属于安全漏洞,则需要由产品团队进行额外的研究;但由于资源或时间限制,这并非总是可行的。
另一项重要要求与代码漏洞的准确会审有关。如果将问题的严重性定得过低,则产品团队可能不会修复它。另一方面,如果将严重性定得过高,则可能会导致选择修复该问题而不是另一个具有更高实际影响的问题。会审过程在很大程度上取决于安全 bug 阈值的质量,但是也与代码审阅者(负责进行会审)以及开发人员(根据会审结果做出响应)的优先级观点有关。如前所述,安全代码错误的会审不应受到利用防范机制的影响。
结束语
代码审阅工作可以提供更多有用信息,而不仅仅是一个安全问题列表。如果可能,审阅者还应记录代码覆盖率、特定代码部分的可信度以及重新设计和清理代码的一般建议。代码审阅也为丰富组织知识、增强安全意识以及提高安全工具的效率提供了独特的机会。最后但显然不容忽视的一点是,开发团队可以使用代码审阅的结果来帮助排定未来产品安全工作的优先级。
我们确信,软件始终会存在安全漏洞,但那些漏洞的本质和实际影响会随时间的推移而变化。自动安全工具能够发现越来越多的编码错误,但是仍会漏掉某些漏洞(未被检测到或者隐藏在大量误报之下)。人工源代码分析不能替代这些经过实践检验的工具,但它通常可以与这些工具集成使用以实现互补。
人工代码审阅方法成本很高,难度较大,并且在很大程度上取决于参与者的经验和工作态度。但是,在许多情况下,项目会要求执行此项任务以便对产品及其重要组件的安全性获得可接受的信任度。有经验的人工审阅者仍可以发现工具可能会遗漏的问题。只要人会成为安全问题的起因,那么人也应成为解决方案的一部分。
有关作者:Michal Chmielewski是 Microsoft SWI 攻击团队的一名安全软件工程师。他专门负责分析产品的安全漏洞;Neill Clift是 Microsoft 的 SWI 团队的一名安全软件工程师。他是内核与驱动程序问题领域的主题专家;Sergiusz Fonrobert目前是 SWI 攻击团队的一名安全软件工程师,负责执行各种 Microsoft 产品的渗透测试;Tomasz Ostwald目前是 SWI 团队的一名安全性项目经理,他负责对 Microsoft 发布的产品进行最终安全性检查。
(本文不涉密)
责任编辑:
上一篇:在公共场所访问公司网络的安全提示
下一篇:将权限管理深入企业安全流程