如何在不向竞争对手公开秘密的情况下安全地调试PHP Web应用程序?


11

最近我做了一个程序。我忘记删除2行代码。这个错误每天使我每天损失$ 800。

我当时在用PHP编程。如果访问者使用代理,它将重定向到其他地方。使用调试器是不可能的,因为某些代码包含ioncube。由于该程序无论如何都只是重定向到其他地方,因此很难看到代码的哪一部分被执行。

所以我到处都放了一堆调试信息。我以为我以后还是要删除它们。

当然,最自然的调试方法是将调试信息放入文件中。问题是我经常使用代理。因此,在更改程序后,我经常必须使用filezilla下载文本文件。通常,文本文件没有显示我认为应该显示的内容。最后,我决定只在网络上显示错误。

我考虑过使用调试模式。但是,恐怕我会忘记删除调试信息。

例如,如果用户执行?debuggingmode = 1,我考虑使用调试模式。但是,我对自己的竞争对手以某种方式可以猜出secret关键字感到疑惑。

我删除了大多数调试信息。我忘记删除一个,并且只有在用户使用来自正确国家/地区的代理服务器时才会显示一个。原来我没有来自正确国家/地区的代理,也没有意识到这一点。该程序运行24小时后,我将其上传到了我的主域。

我的竞争对手,使用代理,请参阅调试代码。他抄袭了这个主意,这就是我每天损失800美元的原因。

回想起来,我真的很难知道我哪里出了问题。我一直非常小心。却发生了。

如何在不向竞争对手公开秘密的情况下安全地调试PHP Web应用程序?


5
绝对不能确定任何事情,更不用说无bug软件了。
Tar

1
每次对程序/应用程序进行更改后,都进行一次彻底的测试,即使更改很小。
Rolen Koh 2014年

16
“一个人应该如何安全地调试Web应用程序而又不向竞争对手公开秘密?” -通过创建模仿您的生产环境的测试环境。实时调试确实很少需要。
CodeCaster

8
我不知道对于两行调试代码有何重要意义,每天价值800美元。它会转储您的私人密码学密钥吗?
菲利普2014年

2
如果在此之前的一段时间里,您每天的收入超过800美元,这甚至重要吗?但是,是的,请不要在线调试代码!您可以在文件中使用配置调试模式布尔值。仅在debug == true时才打印调试代码。至少这是一种快速而肮脏的方式,不值得得到答案!
Anon343224user 2014年

Answers:


37

我真的很难看我哪里出了错

主要的错误是您重新发明了轮子。您没有使用默认的机制来记录日志,而是发明了自己的,该机制在页面中显示了信息。日志记录框架宁愿将日志存储在日志文件中,让您稍后通过SSH到服务器来查阅这些日志。

至于错误,产生没有错误的代码意味着要使用诸如形式证明之类的特定技术。考虑到它们的昂贵性,这些技术适用于对生命至关重要的应用程序,例如控制飞机交通或航天飞机的应用程序,但对于几乎所有业务应用程序来说都是多余的。

■请参阅他们在《快速公司》杂志上写下正确的内容
本文介绍了NASA所使用的方法以及以这种方式生产软件的成本。

■请参见机械化证明(Mackenzie 2004)。
该书总结了软件自动证明的历史,解释了这种证明的利弊,以及企业通常不使用它来编写可靠的软件的原因。

话虽这么说,用于商业应用程序的一堆技术可以确保软件质量。这些包括但不限于:

  • 非正式代码审查,
  • 正式代码检查
  • 测试,
  • 个人桌面检查代码,
  • 等等

■参见完整的代码(McConnell 2004),编程效率(Jones 1986a),软件缺陷消除效率(Jones 1996)和我们所了解的消除缺陷的知识(Shull et al。2002)。

另外,不要忘记持续集成和持续交付。当经过修订的版本似乎存在在代码审查和单元测试期间遗漏的问题,但在部署应用程序后发现该问题时,它有助于自动将生产中的应用程序回滚到工作版本。

■请参阅安全连续部署的秘诀(视频)
它说明了Google设置了哪些技术来防止在部署之前无法发现的错误在生产中停留太长时间。它还描述了pdiff如何捕获缺陷,包括与表示层无关的缺陷。


13

您永远不要在生产中进行调试。

您应该始终拥有一个与生产环境相同的测试环境,并在那里进行调试。

当您需要在测试环境中更改代码(例如添加调试语句)时,应确保它们不会投入生产。

专业设置通常如下所示:

Production
   ^
Staging
   ^
Development

应用程序的“生产”,“暂存”和“开发”实例应尽可能相同,以便可以在“暂存”和“开发”中重现在“生产”中发生的错误,但仍应与彼此之间的关联,这样在一个实例中发生的任何事情都不会影响其他实例。

当您需要分析问题时,可以在“开发”中进行分析。混乱与调试语句,并尝试所有您想要的。找到解决方案后,可以将该修补程序应用于“登台”中未更改的代码库,并验证该修补程序是否有效。然后,您将修补程序升级为“生产”。

适当的版本控制和构建管理系统可以为您提供帮助。


1
那就是我所做的。我首先在其他域中进行调试。之后,我转到新的。但是,该其他域上的错误仍然存​​在。
user114310 2014年

1
@ user114310,您的开发/测试环境应该完全不受外界访问(无论是在localhost上还是需要VPN或类似的东西)。如果您插入了一些调试代码并且在投入生产之前没有将其删除,那是人为错误,没有什么技术可以防范它。只是要更加小心。
Brian S

3
@ user114310对不起,我听不懂。您已在登台中修复了该错误,但是当您将该修复应用于生产时,该错误仍然存​​在吗?那将意味着您没有正确地重现该错误,并意外地修复了其他内容。当您无法重现开发中的错误时,这意味着您的开发环境与生产环境不相同。当您发现需要先解决此问题后,才能正确研究此错误。
菲利普2014年

4

很少这样做,因为这样做是不值得的。即使您每天损失800美元,迅速证明程序正确的努力也要比那大,这意味着没有任何商业理由可以这样做。

如果确定值得的(例如用于航天飞机软件或导弹控制),那么您将进行形式验证,对所有可能输入的详尽测试等。当然,这也非常困难,缓慢且昂贵。但是预算达十亿美元的项目往往也会吸引非常聪明的人。(或者也许他们只是过去-现在的头条新闻似乎与该规则相矛盾。)


1
甚至美国宇航局也弄错了。您可以根据需要进行最大的努力,但是有些事情是人类无法理解的,因此很难保证。
Phoshi 2014年

1
+1:形式验证是一回事,如果您可以进一步了解它,那将是一件好事。我知道这是一回事,但我无话可说。例如,通过测试所有输入进行验证,简化为有限状态机,简化为一阶逻辑。这是什么意思?此验证是否为证据?我认为是这样。
Lyndon White

有形式验证,但也有启发式验证,虽然不确定,但可以提供一定置信度的验证。我对大学的话题不太记得,但是我们在课程中使用的一种工具是Spin,我相信它也可以进行详尽的验证。关于它的一些描述可能会回答正确的单词是什么。
钻机

2

有时您确实需要调试实时系统。是的,您应该具有开发或暂存副本。但是总会有差异。如果代码在客户硬件上用完了,那就尤其如此。或潜在地,许多不同的客户安装。

我过去曾经使用过&debugging = 1技术。我怀疑大多数PHP开发人员都有。这就翻转了代码中的开关,从而在应用程序中启用了更多详细的调试功能。该信息通常会转储到日志文件-通常是apache日志(使用error_log())。但是,您也可以输出它。例如,我们的探查器将收集信息,然后在页面底部输出各种基准。您还可以将调试信息输出为HTML注释,或以仅当您查看页面源时才可见的隐藏元素形式输出。

如果您的站点具有“用户”,则可以将调试限制为仅特定用户。这样,如果有人确实尝试启用调试,则除非他们以该用户身份登录,否则它仍然无法运行。

现在,您提到您正在使用FileZilla连接到服务器。开发人员确实应该具有对服务器的SSH访问权限。这将使调试更加容易。例如,如果要将调试语句输出到Apache错误日志,则可以轻松地将该文件grep用作IP地址,并查看上一页请求生成的调试信息。


1

其他人都已经涵盖了一般情况:

正式验证(/已验证代码):尽管有4000行OS内核,并且已被正式证明,但在现实世界中的程序并不可行,这花费了许多俄罗斯CS博士生几个月的时间(请记住该项目的名称,请发表评论)

CI测试 << 自动测试 << 测试:确保使用覆盖率工具来检查您的测试用例是否具有100%覆盖率。

对于将调试代码留在生产环境中的特定情况,我有2个选项,这两个选项都要求在重新部署到新的(阶段/最终测试)环境中重新编译源代码。

  1. 在编译时将其删除。将调试代码包装在C#和C之类的结构中,#ifdef DEBUG以检查构建目标(“调试”还是“发布”)并在编译时自动将其删除。

  2. 在部署时标记它。在不能在实际环境中运行的代码附近添加注释。例如//TODO: Remove This Before Deployment,然后在将其迁移(部署)到Staging之前,在编译代码之前,通过一个简单的脚本运行它,该脚本检查以确保//TODO:源代码中没有剩余的Flag注释(例如 )。

我建议使用前者,因为它是长期的,并且您会再次想要它(例如,冗长的日志记录模式),而如果后者是调试时的快速破解,则建议后者(例如,遍历您的代码的各种printfs)


0

就像其他人在我之前所说的那样,如果可能的话,许多测试(单元和功能)是自动化的,并且具有良好的代码覆盖率是拥有几乎没有错误的软件的关键。

如果我对问题的描述足够了解,那么调试信息将显示在服务器提供的页面上。如果是这种情况,则应考虑将该信息放在服务器上的正确日志中。这样,即使您将“详细”版本部署到生产环境,也永远不会将其显示给最终用户。除非您有充分的理由要这样做,否则无论您正在从事什么项目,这都是一件好事。


0

其他人关于生产调试的说法是正确的。但是我有时还是这样做的。我认为有比您更安全的方法,尽管没有什么是万无一失的。

我自己不需要如此高的安全性,我只是不想让用户在他们的屏幕上看到一堆乱码。

$debug = true;
$allowed_ips = array('192.168.0.220','173.46.89.255'); // limit to your own ip's. If your competitor knows them, though, he can spoof it! But if your stuff is not top secret, it's ok. 

if(!in_array($_SERVER['REMOTE_ADDR'],$allowed_ips) ) {
  $debug = false;
}

if($debug) {
  echo '<pre>' . print_r($some_variable) . '</pre>';
}

现在,除非用户真的愿意,否则他们将看不到您的调试信息。如果您每天800美元的费用取决于将此调试信息保密,则不要使用上面的代码。但是,如果信息不是那么敏感,那么上述内容将比依赖GET变量或向整个国家透露您的秘密要好得多。

或者,您可以创建一个debug类或函数来根据您的标准检查是否允许打印。我就是做这个的。

像这样:

class debug {
  public $on = false;
  public static function print($variable) {
    if($on) {
      echo '<pre>' . print_r($some_variable) . '</pre>';
    }
  }
}

debug::print($some_variable); // class checks if printing is allowed. If not, no printing occurs.

对于我的程序来说,每天赚800美元(开发中),我使用apache mod auth要求密码来访问网站的任何部分,以及将调试信息限制为某些IP。您的问题使我认为我应该研究更好的安全性。


0

实际上,我将在此处进行两个额外的验证。一种是&debug=1请求中的参数。您还可以使用一些特殊的会话变量或cookie设置,只有您可以通过调用“秘密”页面(最好是通过身份验证)来激活它。

第二个验证是在配置文件中包含一个具有一系列IP的阵列,只有该阵列中IP的调用才能触发调试功能。就像是

在配置中:

$debugAllowedIPs = array('127.0.0.1', '192.168.0.1', /* add more IPs here */);

在可以全局访问的位置使用以下方法:

function getClientIp() {
    if (!empty($_SERVER['HTTP_CLIENT_IP']))  return $_SERVER['HTTP_CLIENT_IP'];
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) return $_SERVER['HTTP_X_FORWARDED_FOR'];
    return $_SERVER['REMOTE_ADDR'];
}

在您的代码中调用类似:

if (in_array(getClientIp(), $debugAllowedIPs)) { /* show debug info here */ }

请记住,该getClientIp()方法中的代码不安全,因为它的值$_SERVER['HTTP_X_FORWARDED_FOR']很容易被欺骗,但是它应该用于解决您的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.