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

您始终应用于代码的“最低”质量水平是什么?

有帮助吗?

解决方案

在我的工作中,我们的代码必须是最高质量的。
因此,我们主要关注两件事:

  1. 测试
  2. 代码审查

那些人把钱带回家。

其他提示

与 abyx 类似,在我所在的团队中,开发人员总是使用单元测试和代码审查。除此之外,我还旨在确保我不会合并人们认为的代码 可能 use - 我倾向于只为手头的对象按照已指定的方式运行所需的基本方法集编写代码。我发现合并可能永远不会使用但提供功能的方法可能会无意中在系统中引入“后门”或意外/意外的使用。

稍后返回并引入所要求的方法、属性和属性比预期可能永远不会出现的东西要容易得多。

我建议对进入“组件”或框架的数据采取防御措施。在“组件”或框架内,人们应该认为数据是“正确的”。

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

  1. 始终检查来自外部来源、用户等的数据
  2. “组件”或框架应该始终检查传入的调用。

如果存在错误并且在调用中使用了错误的值。什么是真正正确的事情?一个人只知道程序正在处理的“数据”是错误的,有些人喜欢断言,但其他人希望使用高级错误报告和可能的错误恢复。无论如何,数据都会被发现有错误,在少数情况下,继续处理它是有好处的。(请注意,如果服务器至少不死机就好)

从卫星发送的图像可能是尝试高级错误恢复的情况...从互联网下载的图像以放置错误图标...

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

在开发过程中,您希望尽早发现错误的数据/逻辑/代码,以防止问题被忽视或导致以后出现难以追踪根本原因的问题。

在生产中尽可能优雅地处理问题。如果确实存在不可恢复的错误,则对其进行处理并将该信息呈现给用户。

作为示例,这里是我们标准化向量的代码。如果你在开发中给它提供错误的数据,它会尖叫,在生产中它会返回一个安全值。

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

我总是努力防止诸如注入攻击之类的事情。然而,当您在内部 Intranet 站点上工作时,大多数安全功能感觉都是浪费精力。我仍然这样做,也许只是做得不太好。

嗯,有一套特定的安全最佳实践。至少,对于数据库应用程序,您需要注意 SQL 注入。

其他东西,例如散列密码、加密连接字符串等。也是一个标准。

接下来就看实际应用了。

幸运的是,如果您使用 .Net 等框架,许多安全保护都是内置的。

我想说,即使对于内部应用程序,你也必须始终进行防御性编程,因为用户可能会纯粹因为运气好而编写出一些破坏你的应用程序的东西。当然,您可能不必担心有人试图骗您的钱,但仍然如此。始终进行防御性编程并假设应用程序会失败。

使用测试驱动开发当然有帮助。您一次编写一个组件,然后在编写代码之前枚举输入的所有潜在情况(通过测试)。这可以确保您已经涵盖了所有基础并且没有编写任何内容 凉爽的 没有人会使用但可能会损坏的代码。

虽然我不做任何正式的事情,但我通常会花一些时间查看每个类并确保:

  1. 如果它们处于有效状态,则它们保持有效状态
  2. 无法在无效状态下构造它们
  3. 在特殊情况下,它们会尽可能优雅地失败(通常这是清理和抛出)

这取决于。

如果我真的想破解一些东西供自己使用,那么我将编写出我不需要考虑的最佳代码。让编译器成为我的警告等朋友。但我不会自动创建类型。

代码越有可能被使用,即使是偶尔,我也会提高检查级别。

  • 最小幻数
  • 更好的变量名
  • 完全检查和定义的数组/字符串长度
  • 通过合约断言编程
  • 空值检查
  • 异常(取决于代码的上下文)
  • 基本解释性意见
  • 可访问的使用文档(如果是 perl 等)

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

  • 对于 setter,请确保克隆所有可变对象并存储克隆。这样,调用者就无法在事后更改传入的对象来破坏程序的不变量。
  • 对于 getter,如果接口允许,要么返回内部数据的不可变视图;要么返回内部数据的不可变视图。否则返回内部数据的克隆。
  • 当使用内部数据调用用户提供的回调时,请根据需要发送不可变视图或克隆,除非您希望回调更改数据,在这种情况下,您必须事后验证它。

最重要的信息是确保外部代码不能保留您内部使用的任何可变对象的别名,以便您可以维护不变量。

我非常认为正确的编程可以防范这些风险。比如避免废弃的函数(至少在 Microsoft C++ 库中)由于安全漏洞而通常被废弃,以及验证跨越外部边界的所有内容。

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

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

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

根据我的经验,积极采用防御性编程并不一定意味着您最终会提高代码质量。不要误会我的意思,你需要进行防御性编程来捕获用户会遇到的各种问题 - 用户不喜欢你的程序在他们身上崩溃 - 但这不太可能使代码更容易维护,测试等

几年前,我们制定了在软件的各个级别使用断言的政策,以及单元测试、代码审查等。加上我们现有的应用程序测试套件 - 对我们的代码质量产生了显着的积极影响。

Java、签名 JAR 和 JAAS。

Java 防止缓冲区溢出和指针/堆栈攻击。

不要使用 JNI。(Java 本机接口)它向您公开 DLL/共享库。

签署 JAR 以避免类加载成为安全问题。

JAAS可以让你的应用程序不信任任何人,甚至是它自己。

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

其中一些会产生一些开销,但安全漏洞就会消失。

简单回答: 这取决于。过多的防御性编码 导致重大性能问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top