【问题标题】:Safe evaluation of arithmetic expressions in JavascriptJavascript中算术表达式的安全评估
【发布时间】:2011-02-21 13:56:26
【问题描述】:

我需要在 Javascript 中评估用户输入的算术表达式,例如“2 * (3 + 4)”,但出于安全原因,我不想使用 eval

我可以去掉所有不是数字或运算符的字符,但我不确定这是否安全,如果用户可以使用 cossqrt 等函数会很好。 .

有没有进行算术表达式求值的 Javascript 库?

【问题讨论】:

标签: javascript parsing math


【解决方案1】:

你可以试试JavaScript Expression Evaluator:

这个库是一个修改版 Raphael Graf 的 ActionScript 表达式 解析器。当我写 JavaScript 函数绘图仪,我想要一个更好的 替代使用 JavaScript 的 eval 功能。没有安全风险 目前,因为你只能运行 代码在你自己的浏览器中,但它不是 方便数学(Math.pow(2^x) 而不是 2^x 等)。

那么你的代码会是这样的:

console.info ( Parser.evaluate( "2 * (3 + 4)" ) ); //prints 14

The source code is on GitHub,它在 npm 上发布为expr-eval。可以这样使用:

import { Parser } from 'expr-eval';

console.log(Parser.evaluate("2 * (3 + 4)")); // 14

【讨论】:

  • 谢谢!这正是我一直在寻找的,我发誓我在谷歌上搜索了我能想到的所有可能的“表情评估”变化......
【解决方案2】:

如前所述,任何用户可能造成的最大损害几乎就是他们在任何主要浏览器中使用内置控制台已经可以造成的损害。但是,如果您想限制用户使用Math 属性/方法,您可以编写一个简单的正则表达式来为您处理。像这样的东西应该可以工作:

function mathEval (exp) {
    var reg = /(?:[a-z$_][a-z0-9$_]*)|(?:[;={}\[\]"'!&<>^\\?:])/ig,
        valid = true;
       
    // Detect valid JS identifier names and replace them
    exp = exp.replace(reg, function ($0) {
        // If the name is a direct member of Math, allow
        if (Math.hasOwnProperty($0))
            return "Math."+$0;
        // Otherwise the expression is invalid
        else
            valid = false;
    });
    
    // Don't eval if our replace function flagged as invalid
    if (!valid)
        alert("Invalid arithmetic expression");
    else
        try { alert(eval(exp)); } catch (e) { alert("Invalid arithmetic expression"); };
}

我知道您出于安全原因不想使用 eval,但是正则表达式应该使它非常安全,因为它排除了任何不是 @987654326 的 直接 属性的词@object 和大多数非数学 JS 运算符,包括赋值运算符 (=) 和二元运算符。更难的方法是编写分词器来解析数学表达式,因为它不是常规语言。

随意尝试破解我写的working example,如果可以或发现问题,请发表评论,我会看看我能做些什么来解决它。


注意:Yijiang 提到 [在 JavaScript 聊天中](https://chat.stackoverflow.com/rooms/17/javascript) 允许使用小写字母可能也有用,例如 `Math.PI`。如果是这种情况,您可以在替换函数中添加以下 `else if` 语句:
else if (Math.hasOwnProperty($0.toUpperCase())
    return "Math."+$0.toUpperCase();

ifelse 语句之间添加它 (example)。

【讨论】:

  • “任何用户可能造成的最大损害几乎就是他们在任何主流浏览器中使用内置控制台已经可以做到的事情”是基于错误的前提。字符串通过服务器和 URL 来自浏览器外部。最好的策略是编码假设您的函数的输入将来自您不期望的地方,因为其他开发人员会添加到您的应用程序中,这意味着不要使用过于强大的运算符,例如 eval
  • @Mike:我很难 100% 同意。虽然您在评估来自其他来源的输入时所说的内容是正确的,但当您仅评估用户输入时,我支持我的回答。
  • @AndyE,我对“评估用户输入”这一短语的解读包括评估用户 Carol 在用户 Alice 的浏览器中的输入。在每个 PM 和他们的母亲都试图让他们的网站“社交”之前,更有限的阅读是合理的,但今天我认为更广泛的阅读应该是默认的,除非应用规范另有说明。
  • 用例可以包括在服务器端评估用户输入。那么安全性很重要。
【解决方案3】:

您可以使用 math.js 中的高级表达式解析器,它不使用 JavaScript 的 eval。

http://mathjs.org

用法:

var ans = math.evaluate('2 * (3 + 4)');

或使用解析器(支持变量和函数赋值):

var parser = math.parser();
var ans = parser.evaluate('2 * (3 + 4)');

【讨论】:

    【解决方案4】:

    您可以使用正则表达式替换除白名单之外的所有内容。但是由于 javascript 是在客户端上执行的(除非您正在运行 AOL http 服务器)任何可以修改输入的人也可以修改代码 - 所以您并没有真正做到任何或比现在更不安全。

    【讨论】:

    • 这取决于键入要计算的表达式的用户是否与计算该表达式的用户相同。
    【解决方案5】:

    试试nerdamer

    var result = nerdamer('sqrt(4)+cos(1)-2').evaluate();
    document.getElementById('text').innerHTML = result.text();
    <script src="http://nerdamer.com/js/nerdamer.core.js"></script>
    <div id="text"></div>

    【讨论】:

      猜你喜欢
      • 2013-08-24
      • 2019-06-04
      • 1970-01-01
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多