强化遗留 Rails 应用程序的第一步是什么?

What are the first steps in hardening a legacy Rails application?

有一个已经 运行 多年的生产系统,首先作为 PHP 应用程序,然后作为与 Rails 的混合体,现在完全在 Rails。目前还不清楚它存在了多久。最早的 git 提交是 5 年前的。

我们的目标是不惜一切代价让系统 运行保持稳定。只要没有问题,我们使用什么代码都没有关系。当前为 Rails 版本 3.2.33

如果我们不升级任何 gem,我们 运行 有可能变得过时和无法部署。如果我们升级,我们将需要更改代码,从而导致潜在的错误蔓延。我们不仅面临代码腐烂,而且还会因 AWS 中断而停机。

确保万无一失的第一步是什么?我花了几个月的时间编写黄瓜(集成)测试,但很难涵盖所有边缘情况。该应用程序已经 运行ning 了这么久,大多数错误都已修复,几乎没有新的异常。测试从一开始就不是优先事项,因此大部分代码都没有记录。

老实说,我发现 Rails 上的 Ruby 不适合此类应用程序。 Ruby 和 Rails 都有非常积极的发布计划,而且 Rails 尤其不怕放弃向后兼容性。 Rails 非常适合敏捷开发,其中事物总是在变化,但以长期稳定性为代价。

我假设您的应用程序足够大,您不想切换到其他任何应用程序。 Sinatra,例如,变化不大,将是一个更稳定的选择。

如果您坚持使用 Rails,我建议您尽可能多地摆脱 gem 依赖项。它们总是存在不再开发或可能引入错误或漏洞的危险。

另外,尽可能优先使用 PORO(普通旧 Ruby 对象)而不是 Rails 相关代码是个好主意。它通常需要更多的工作,但您最终会得到更稳定和可重用的代码。

我意识到在这样的应用程序中投入的工作可能比您想要做的要多,但这是我最好的建议。

第一步是为 gem 文件中使用的所有 gem 添加特定的 gem 版本。

例如

 gem 'rspec-rails'

可能会变成

 gem 'rspec-rails', '2.14.1'

您可以通过查看您的 Gemfile.lock 来确定当前正在使用哪些版本,例如,Gemfile.lock 中的这一行显示为 rspec 选择的版本:

rspec (2.14.1)

即使 Gemfile 没有版本,例如

gem chronic

Gemfile 具有使用的版本,例如

chronic (0.10.2)

如果我们不升级任何 gem,我们 运行 有可能变得过时和无法部署。如果我们升级,我们将需要更改代码,导致潜在的错误蔓延。

是的,这就是你的困境。没有任何魔法,您必须选择要解决的这两个优先事项中的哪一个。正如 aNoble 所指出的,RoR 不是一个可以 'stay in place' 的框架。 gem 组成大多数应用程序的不断变化意味着 RoR 应用程序不会老化。

你应该向项目所有者解释并重复,重复,重复。通常这是 "accepted" 的那种原则 - 但不是真的 - 因为继续提出相同的问题 "despite that, how can I upgrade it, how can I make sure nothing changes or breaks, etc."

如果应用程序将在几个月后停用,没问题。如果没有真正计划停止应用程序并且它将继续成为业务的重要组成部分,那么您只需要使用资源来维护它。 RoR 土地上没有免费的午餐。

编写许多测试是关键的第一步,很高兴看到这一点。检查你的语句覆盖率,看看你是否遗漏了重要代码区域的测试,并确保你有覆盖关键序列的集成测试。这个想法是修改您的测试以降低更改导致用户可见故障的风险。

现在在某些系统上设置连续测试。

设置您的 Gemfile 和 .ruby-version 以便您可以具体控制加载所有内容的确切版本。这不会自动更新 - 但它确保您可以 控制 更新的内容。签入 Gemfile 和 Gemfile.lock.

此时您可以慢慢增加版本号。不要跳过很多版本号 - 通常最好缓慢升级,这样您才能看到弃用警告。修复这些,冲洗,重复。

将您的(输入)验证器修改为挑剔的白名单 ("it must be of this form or I won't accept it")。如果您可以防止不良数据进入您的系统,它就更有可能正常工作并且通常更难受到攻击。

为了安全,考虑添加secureheaders,并尽可能地设置CSP。

开始添加一些静态分析器。 Rubocop 和 Brakeman 非常有用。您可能必须将 Rubocop 配置为仅抱怨几件事,然后慢慢增加他们报告的内容。将所有检查添加到默认的 "rake" 命令,以便您只需键入 "rake" 到 运行 静态分析器和测试套件。

无论您使用什么框架,都没有魔法。人都会犯错,假装是没有用的。

您可能 CII Best Practices badge project 是一个有用的示例。它使用 RoR,我领导它。具体参见: * CONTRIBUTING * Security information (assurance case)

来自贡献者: "In general we try to be proactive to detect and eliminate mistakes and vulnerabilities as soon as possible, and to reduce their impact when they do happen. We use a defensive design and coding style to reduce the likelihood of mistakes, a variety of tools that try to detect mistakes early, and an automatic test suite with significant coverage."