rubyrails on rails中怎么使用按钮触发一个编辑好的rubyrails程序

如果没有听说过 Rails那么欢迎您外煋旅行归来,近几年大概只有那个地方没有听说过 rubyrails on Rails 了Rails 最吸引人的地方是能够很快地建立功能完备的应用程序并运行起来。Rails 为 Ajax 而内置集成嘚 应用程序的东西也许您会发现您希望一直编写 Rails 代码。

}

本文是翻译文章原文地址:

该錯误最初是由名为ooooooo_q的研究员发现和报告的,以下是他们的报告的一部分涵盖CVE-,只有一些很小的修改


rubyrails on RailsActiveStorage组件中被爆出一个不安全的反序列化漏洞。此漏洞的产生原因是在HTTP URL中反序列化一个rubyrails对象时使用了Marshal.load()却没有进行充分验证。

Rails是一个用rubyrails语言编写的开源Web应用程序模型视图控制器(MVC)框架

Rails旨在鼓励软件工程模式和范例,例如约定优于配置(CoC)——也称为按约定编程不重复自己(DRY)和活动记录模式。Rails作为以下單独组件发布:

其中Marshal序列化格式由Marshal类实现,可以分别通过load()dump()方法对对象进行序列化和反序列化

如上所示,Marshal序列化格式使用类型长度值表示来序列化对象

默认情况下,Active Storage会向Rails应用程序添加一些路由本报告感兴趣的是以下两条分别负责下载上传文件的路由:

在正常使用Φ,这些变量是由MessageVerifier.generate()它们生成的它们的结构如下:

由于通过使用MessageVerifier密钥对数据进行签名摘要。对于开发中的Rails应用程序此密钥始终是应用程序名称,这是公知的

可以通过CVE-来获取这些文件的内容。一旦完整性检查通过已经经过base64解码,并且结果字节流作为参数调用Marshal.load(),无需进┅步验证

远程未经身份验证的攻击者可以通过发送将恶意序列化对象嵌入到易受攻击的应用程序的精心设计的HTTP请求来利用此漏洞。成功利用将导致在受影响的rubyrails on Rails应用程序的安全上下文中执行任意代码

以下代码片段截取自Rails版本5.2.1,趋势科技添加的评论已经突出显示

有一个公开嘚可以演示此漏洞也可以使用以下独立。用法很简单:

此漏洞在2019年3月从供应商获得了除了这个bug,该补丁还提供了修复中文件内容披露嘚bug以及,一个在行动视图中的拒绝服务的bug

如果您无法立即应用修补程序则可以通过在开发模式下指定密钥来缓解此问题。

唯一的另一個显著缓解是限制对受影响端口的访问

Rails版本6.0.0.X5.2.X中存在此错误。鉴于此漏洞的CVSS v3得分为9.8Rails的用户务必尽快升级或应用缓解措施

威胁研究团队將在未来回归其他伟大的漏洞分析报告。在此之前请关注ZDI ,获取最新的漏洞利用技术和安全补丁

}

使用了该语言的灵活性这样一來,无需太多样板或额外的代码就可以轻松编写高度结构化的程序:无需额外工作就可以获得大量标准的行为。虽然这种轻松自由的行為并不总是完美的但毕竟您可以无需太多工作就可以获得很多好的架构。

例如rubyrails on Rails 基于模型-视图-控制器(Model-View-Controller,MVC)模式这意味着大多数 Rails 应用程序都可以清晰地分成三个部分。模型部分包含了管理应用程序数据所需的行为通常,在一个 rubyrails on Rails 应用程序中模型和数据库表之间的关系昰 1:1;rubyrails on Rails 默认使用的对象关系映射(ORM)ActiveRecord 负责管理模型与数据库的交互,这意味着 rubyrails on Rails 程序通常都具有(如果有的话)很少量的 SQL 代码第二个部分是視图,它包含创建发送至用户的输出所需要的代码;它通常由 HTML、JavaScript 等组成最后的一个部分是控制器,它将来自用户的输入转变为正确的模型然后使用适当的视图呈现响应。

Rails 的倡导者通常都乐于将其易用性方面的提高归功于 MVC 范型 ― 以及 rubyrails 和 Rails 二者的其他一些特性并称很少有程序员能够在较短的时间内创建更多的功能。当然这意味着投入到软件开发的成本将能够产生更多的商业价值,因此 rubyrails on Rails 开发愈发流行

不过,最初的开发成本并不是事情的全部还有其他的后续成本需要考虑,比如应用程序运行的维护成本和硬件成本rubyrails on Rails 开发人员通常会使用测試和其他的敏捷开发技术来降低维护成本,但是这样一来很容易忽视具有大量数据的 Rails 应用程序的有效运行。虽然 Rails 能够简化对数据库的访問但它并不总是能够如此有效。
Rails 应用程序为何运行缓慢

Rails 应用程序之所以运行缓慢,其中有几个很基本的原因第一个原因很简单:Rails 总昰会做一些假设为您加速开发。通常这种假设是正确而有帮助的。不过它们并不总能有益于性能,并且还会导致资源使用的效率低下 ― 尤其是数据库资源

例如,使用等同于 SELECT * 的一个 SQL 语句ActiveRecord 会默认选择查询上的所有字段。在具有为数众多的列的情况下 ― 尤其是当有些字段昰巨大的 VARCHAR 或 BLOB 字段时 ― 就内存使用和性能而言这种行为很有问题

另一个显著的挑战是 N+1 问题,本文将对此进行详细的探讨这会导致很多小查询的执行,而不是一个单一的大查询例如,ActiveRecord 无从知道一组父记录中的哪一个会请求一个子记录所以它会为每个父记录生成一个子记錄查询。由于每查询的负荷这种行为将导致明显的性能问题。

其他的挑战则更多地与 rubyrails on Rails 开发人员的开发习惯和态度相关由于 ActiveRecord 能够让如此眾多的任务变得轻而易举,Rails 开发人员常常会形成 “SQL 不怎样” 的一种态度即便在更适合使用 SQL 的时候,也会避免 SQL创建和处理数量巨大的 ActiveRecord 对潒的速度会非常缓慢,所以在有些情况下直接编写一个无需实例化任何对象的 SQL

由于 rubyrails on Rails 常被用来降低开发团队的规模,又由于 rubyrails on Rails 开发人员通常嘟会执行部署和维护生产中的应用程序所需的一些系统管理任务因此若对应用程序的环境知之甚少,就很可能出问题操作系统和数据庫有可能未被正确设置。比如虽然并不最优,MySQL my.cnf 设置常常在 rubyrails on Rails 部署内保留它们的默认设置此外,可能还会缺少足够的监控和基准测试工具來提供性能的长期状况当然,这并不是在责怪 rubyrails on Rails 开发人员;这是非专业化导致的后果;在有些情况下Rails 开发人员有可能是这两个领域的专镓。

最后一个问题是 rubyrails on Rails 鼓励开发人员在本地环境中进行开发这么做有几个好处 ― 比如,开发延迟的减少和分布性的提高 ― 但它并不意味着您可以因为工作站规模的减少而只处理有限的数据集他们如何开发以及代码将被部署于何处之间的差异可能会是一个大问题。即便您在┅个性能良好的轻载本地服务器上处理小规模的数据已经很长一段时间也会发现对于拥塞的服务器上的大型数据此应用程序会有很明显嘚性能问题。

当然Rails 应用程序具有性能问题的原因可能有很多。查出 Rails 应用程序有何潜在性能问题的最佳方法是利用能为您提供可重复、准确度量的诊断工具。

最好的工具之一是 Rails 开发日志它通常位于每个开发机器上的 log/development.log 文件内。它具有各种综合指标:响应请求所花费的总时間、花费在数据库内的时间所占的百分比、生成视图所花时间的百分比等此外,还有一些工具可用来分析此日志文件比如 development-log-analyzer。

在生产期間通过查看 mysql_slow_log 可以找到很多有价值的信息。更为全面的介绍超出了本文的讨论范围更多信息可以在 参考资料 部分找到。

其中一个最强大吔是最为有用的工具是 query_reviewer 插件(参见 参考资料)这个插件可显示在页面上有多少查询在执行以及页面生成需要多长时间。并且它还会自动汾析 ActiveRecord 生成的 SQL 代码以便发现潜在问题例如,它能找到不使用 MySQL 索引的查询所以如果您忘记了索引一个重要的列并由此造成了性能问题,那麼您将能很容易地找到这个列(有关 MySQL 索引的更多信息参见 参考资料)。此插件在一个弹出的 <div>(只在开发模式下可见)中显示了所有这类信息

最后,不要忘记使用类似 Firebug、yslow、Ping 和 tracert 这样的工具来检测性能问题是来自于网络还是资源加载问题

接下来,让我们来看具体的一些 Rails 性能問题及其解决方案

N+1 查询问题是 Rails 应用程序最大的问题之一。例如清单 1 内的代码能生成多少查询?此代码是一个简单的循环遍历了一个假想的 post 表内的所有 post,并显示 post 的类别和它的主体

 

答案:上述代码生成了一个查询外加 @posts 内的每行一个查询。由于每查询的负荷这可能会成為一个很大的挑战。罪魁祸首是对 p.category.name 的调用这个调用只应用于该特定的 post 对象,而不是整个 @posts 数组幸好,通过使用立即加载我们可以修复這个问题。

立即加载 意味着 Rails 将自动执行所需的查询来加载任何特定子对象的对象Rails 将使用一个 JOIN SQL 语句或一个执行多个查询的策略。不过假設指定了将要使用的所有子对象,那么将永远不会导致 N+1 的情形在 N+1 情形下,一个循环的每个迭代都会生成额外的一个查询清单 2 是对 清单 1 內代码的修订,它使用了立即加载来避免 N+1 问题


  

该代码最多生成两个查询,而不管在此 posts 表内有多少行

当然,并不是所有情况都如此简单处理复杂的 N+1 查询情况需要更多的工作。那么做这么多努力值得么让我们来做一些快速的测试。

使用清单 3 内的脚本可以发现查询可以達到 ― 多慢 ― 或多快。 清单 3 展示了如何在一个独立脚本中使用 ActiveRecord 来建立一个数据库连接、定义表并加载数据然后,可以使用 rubyrails 的内置基准测試库来查看哪种方式更快快多少。
清单 3. 立即加载基准测试脚本


  

这个脚本使用 :include 子句测试在有和没有立即加载的情况下对 100、1,000 和 10,000 个对象进行循環操作的速度如何为了运行此脚本,您可能需要用适合于您的本地环境的参数替换此脚本顶部的这些数据库连接参数此外,需要创建┅个名为 test 的 MySQL 数据库最后,您还需要 ActiveRecord 和 faker 这两个 gem二者可通过运行 gem

在我的机器上运行此脚本生成的结果如清单 4 所示。
清单 4. 立即加载的基准测試脚本输出


  

在所有情况下使用 :include 的测试总是更为迅速 ― 分别快 5.02、4.52 和 6.86 倍。当然具体的输出取决于您的特定情况,但立即加载可明显导致显著的性能改善

如果您想要引用一个嵌套的关系 ― 关系的关系,又该如何呢 清单 5 展示了这样一个常见的情形:循环遍历所有的 post 并显示作鍺的图像,其中 Author 与 Image 是 belongs_to 的关系
清单 5. 嵌套的立即加载用例


  

此代码与之前一样亦遭遇了相同的 N+1 问题,但修复的语法却没有那么明显因为这里所使用的是关系的关系。那么如何才能立即加载嵌套关系呢

正确的答案是使用 :include 子句的哈希语法。清单 6 给出了使用哈希语法的一个嵌套的竝即加载
清单 6. 嵌套的立即加载解决方案


  

正如您所见,您可以嵌套哈希和数组实量(literal)请注意在本例中哈希和数组之间的惟一区别是哈唏可以含有嵌套的子条目,而数组则不能否则,二者是等效的

并非所有的 N+1 问题都能很容易地察觉到。例如清单 7 能生成多少查询?
清單 7. 间接的立即加载示例用例


  

  

不幸的是答案是 清单 7 和 清单 8 在 post 内每行生成一个额外查询,查找用户的名字 ― 即便 post 对象由 ActiveRecord 从一个已在内存中的 User 對象自动生成简言之,Rails 并不能关联子记录与其父记录

修复方法是使用自引用的立即加载。基本上由于 Rails 重载由父记录生成的子记录,所以需要立即加载这些父记录就如同父与子记录之间是完全分开的关系一样。代码如清单 9 所示
清单 9. 间接的立即加载解决方案


  

虽然有悖於直觉,但这种技术与上述技术的工作原理大致相似但是,很容易使用这种技术进行过多的嵌套尤其是在体系结构复杂的情况下。简單的用例还好比如 清单 9 内所示的,但繁复的嵌套也会出问题在一些情况下,过多地加载 rubyrails 对象有可能会比处理 N+1 问题还要缓慢 ― 尤其是当烸个对象并没有被整个树遍历时在该种情况下,N+1 问题的其他解决方案可能更为适合

一种方式是使用缓存技术。Rails V2.1 内置了简单的缓存访问使用 Rails.cache.read、 Rails.cache.write 及相关方法,可以轻松创建自己的简单缓存机制并且后端可以是一个简单的内存后端、一个基于文件的后端或一个分布式缓存垺务器。在 参考资料 部分可以找到有关 Rails 内置缓存支持的更多信息但您无需创建自己的缓存解决方案;您可以使用一个预置的

当然,并不昰所有的 Rails 问题都与查询的数量有关

Rails 分组和聚合计算

您可能遇到的一个问题是在 rubyrails 所做的工作本应由数据库完成。这考验了 rubyrails 的强大程度很難想象在没有任何重大激励的情况下人们会自愿在 C 中重新实现其数据库代码的各个部分,但很容易在 Rails 内对 ActiveRecord 对象组进行类似的计算但是,rubyrails 總是要比数据库代码慢所以请不要使用纯 rubyrails 的方式执行计算,如清单 10 所示
清单 10. 执行分组计算的不正确方式


  

相反,Rails 提供了一系列的分组和聚合函数可以像清单 11 所示的那样使用它们。
清单 11. 执行分组计算的正确方式


  

不过并不是在 SQL 内能做的所有事情在 Rails 内也能做。如果插件不够可以使用定制 SQL。

假设有这样一个表内含人的职业、年龄以及在过去一年中涉及到他们的事故的数量。可以使用一个定制 SQL 语句来检索此信息如清单 12 所示。


  

  

  

大多数的模式操作比如添加和删除列,都可以使用 Rails 的内置方法完成但如果需要,也可以使用执行任意 SQL 代码的能力

与所有的框架一样,如果不多加小心和注意rubyrails on Rails 也会遭遇性能问题。所幸的是监控和修复这些问题的技术相对简单且易学,而且即便是複杂的问题只要有耐心并对性能问题的源头有所了解,也是可以解决的

}

我要回帖

更多关于 rubyrails 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信