防御性编程

时间:2020-03-05 18:38:46  来源:igfitidea点击:

在编写代码时,我们有意识地进行防御性编程以确保高质量的程序并避免可能被恶意利用的代码,例如通过缓冲区溢出漏洞还是代码注入?

我们将始终对代码应用的"最低"质量级别是多少?

解决方案

回答

我一直在努力防止诸如注射攻击之类的事情。但是,当我们在内部Intranet站点上工作时,大多数安全功能感觉像是在浪费精力。我仍然在做,也许不是很好。

回答

好吧,有一些关于安全性的最佳实践。至少,对于数据库应用程序,我们需要当心SQL注入。

其他诸如哈希密码,加密连接字符串等的内容也是标准的。

从这里开始,它取决于实际应用。

幸运的是,如果我们使用的是.Net等框架,则会内置许多安全保护。

回答

我要说,即使对于内部应用程序,我们也必须始终进行防御性编程,这仅仅是因为用户可以凭借纯粹的运气编写一些破坏我们应用程序的内容。当然,我们可能不必担心尝试用金钱欺骗我们,但仍然可以。始终进行防御性编程,并假定该应用程序将失败。

回答

在我的工作中,我们的代码必须是最高质量的。
因此,我们专注于两个主要方面:

  • 测验
  • 代码审查

那些把钱带回家。

回答

与abyx相似,我在团队中的开发人员始终使用单元测试和代码审查。除此之外,我还旨在确保我不会合并人们可能会使用的代码,而我倾向于仅针对所指定的对象要起作用的基本方法集编写代码。我发现,合并可能永远不会使用但提供功能的方法可能会无意中将"后门"或者意外/意外的使用引入系统。

稍后再回过头来介绍要问的方法,属性和属性,而不是预料到永远不会出现的事情,要容易得多。

回答

使用测试驱动开发肯定会有所帮助。我们一次编写一个组件,然后在编写代码之前枚举所有潜在的输入案例(通过测试)。这样可以确保我们已经覆盖了所有基础知识,并且没有编写任何人都不会使用但可能会破坏的出色代码。

尽管我不做任何正式的事情,但我通常会花一些时间看每节课,并确保:

  • 如果它们处于有效状态,则它们保持有效状态
  • 没有办法在无效状态下构造它们
  • 在特殊情况下,它们将尽可能优雅地失败(通常是清理并抛出)

回答

这取决于。

如果我真的是在为自己的使用目的进行黑客入侵,那么我将编写无需考虑的最佳代码。让编译器成为警告等问题的朋友。但是,我不会自动为其创建类型。

使用代码的可能性更高,即使是偶尔,我也会提高检查级别。

  • 最小魔数
  • 更好的变量名
  • 全面检查和定义的数组/字符串长度
  • 通过合同声明进行编程
  • 空值检查
  • 异常(取决于代码的上下文)
  • 基本的解释性意见
  • 可访问的使用说明文件(如果有perl等)

回答

我将采用防御性编程的不同定义,这是乔什·布洛赫(Josh Bloch)的有效Java所倡导的定义。在书中,他讨论了如何处理调用者传递给代码的可变对象(例如,在setter中),以及如何传递给调用者的可变对象(例如,在getter中)。

  • 对于设置器,请确保克隆所有可变对象,并存储克隆。这样,在事实破坏程序的不变性之后,调用者便无法更改传入的对象。
  • 对于getter,如果接口允许,则返回内部数据的不变视图;否则,返回true。否则返回内部数据的克隆。
  • 当使用内部数据调用用户提供的回调时,请视情况在不可变的视图或者克隆中发送,除非我们打算让回调更改数据,在这种情况下,我们必须在事后对其进行验证。

提示信息是确保没有任何外部代码可以为我们在内部使用的任何可变对象保留别名,以便我们可以维护不变量。

回答

我建议对输入"组件"或者框架的数据采取防御措施。在"组件"或者框架内,应该认为数据是"正确的"。

这样想。调用者应提供正确的参数,否则所有功能和方法都必须检查每个传入的参数。但是,如果仅对调用方执行检查,则只需要检查一次。因此,参数应该是"正确的",因此可以传递给较低的级别。

  • 始终检查来自外部来源,用户等的数据
  • "组件"或者框架应始终检查传入的调用。

如果有错误,并且在调用中使用了错误的值。真正正确的做法是什么?只有一个迹象表明该程序正在处理的"数据"是错误的,其中一些类似ASSERTS,但其他一些则希望使用高级错误报告和可能的错误恢复。无论如何,发现数据都是有缺陷的,在少数情况下,最好继续进行处理。 (请注意,如果服务器至少不死,那就很好了)

从卫星发送的图像可能是尝试在...上进行高级错误恢复的一种情况。

回答

我非常认为正确的编程可以防止这些风险。诸如避免避免过时的功能(至少在Microsoft C ++库中)由于安全漏洞而被过时的事情,以及验证跨越外部边界的所有内容。

仅从代码中调用的函数不需要进行过多的参数验证,因为我们可以控制调用者,即不跨越任何外部边界。由其他人的代码调用的函数应该假定传入的参数在某些时候将是无效的和/或者恶意的。

我处理公开函数的方法是简单地崩溃,并在可能的情况下提供有用的信息。如果调用者无法正确获取参数,则问题出在他们的代码中,他们应该修复它,而不是我们。 (显然,我们已经为函数提供了文档,因为它已经公开。)

仅当应用程序能够提升当前用户的权限时,代码注入才是一个问题。如果某个进程可以将代码注入到应用程序中,那么它可以轻松地将代码写入内存并以任何方式执行它。无法获得对系统的完全访问权限,代码注入攻击是毫无意义的。 (这就是为什么管理员使用的应用程序不能由较小的用户编写的原因。)

回答

以我的经验,积极采用防御性编程并不一定意味着我们最终会提高代码质量。别误会,我们需要采取防御性的程序来捕获用户遇到的各种问题,当程序崩溃时,用户会不喜欢它,但这不太可能使代码更易于维护,测试,等等。

几年前,我们制定了在所有软件级别上使用断言的政策,并且将其与单元测试,代码审查等一起使用,此外,我们现有的应用程序测试套件对代码质量产生了显着的积极影响。

回答

我建议人们编写在开发环境中是法西斯主义而在生产中是仁慈的代码。

在开发过程中,我们希望尽早捕获错误的数据/逻辑/代码,以防止问题不被发现或者导致后来的问题,而根本原因难以跟踪。

在生产中尽可能优雅地处理问题。如果确实是不可恢复的错误,请进行处理并将该信息提供给用户。

例如,这是我们的标准化向量的代码。如果在开发中提供错误的数据,它将尖叫,在生产中,它将返回安全值。

inline const Vector3 Normalize( Vector3arg vec )
{
    const float len = Length(vec);
    ASSERTMSG(len > 0.0f "Invalid Normalization");
    return len == 0.0f ? vec : vec / len;
}

回答

Java,签名的JAR和JAAS。

防止缓冲区溢出和指针/堆栈重击漏洞的Java。

不要使用JNI。 (Java本机接口),它使我们可以使用DLL /共享库。

已签署JAR来停止类加载是一个安全问题。

JAAS可以让应用程序不信任任何人,甚至不信任任何人。

J2EE具有(基于有限的)对基于角色的安全性的内置支持。

其中有些开销,但是安全漏洞消失了。

回答

简单的答案:这取决于。
过多的防御性编码可能会导致严重的性能问题。