在Javascript中安全评估算术表达式

17
我需要在JavaScript中评估用户输入的算术表达式,例如“2 *(3 + 4)”,但出于安全考虑,我不想使用eval
我可以剥离所有不是数字或运算符的字符,但我不确定这样做是否安全,并且如果用户可以使用cossqrt等函数,则这将很好。
是否有任何JavaScript库可进行算术表达式评估?

除非你自己动手编写程序,否则你无法解决任何安全问题。 - James
这个回答解决了你的问题吗?在JavaScript中将字符串作为数学表达式进行评估 - outis
5个回答

23

您可以尝试使用JavaScript表达式计算器

该库是Raphael Graf的ActionScript表达式解析器的修改版本。当我编写JavaScript函数绘图器时,我想要一个更好的替代JavaScript的eval函数。目前没有安全风险,因为您只能在自己的浏览器中运行代码,但对于数学来说不太方便(例如Math.pow(2^x)而不是2^x等)。

然后您的代码将会像这样:

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

源代码在GitHub上,并且已经发布到npm上作为expr-eval。可以这样使用:

import { Parser } from 'expr-eval';

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

1
谢谢!这正是我在寻找的内容,我发誓我已经用所有可能的“表达式求值”变体进行了谷歌搜索。 - Giuseppe Ottaviano

11

如前所述,任何用户可能造成的最大损害基本上就是他们可以在任何主要浏览器中使用内置控制台所能做的。但是,如果您想限制用户只能使用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,但是正则表达式应该可以使它相当安全,因为它排除了任何不是Math对象的直接属性和大多数非数学JS运算符,包括赋值运算符(=)和二元运算符。更难的方法是编写一个分词器来解析数学表达式,因为它不是一种常规语言。
如果你能或者注意到问题,请随意尝试打破我写的工作示例,并留下评论,我会尽力修复它。
else if (Math.hasOwnProperty($0.toUpperCase())
    return "Math."+$0.toUpperCase();

将其添加在ifelse语句之间(示例)。

6
“任何用户可能造成的最大损害,基本上就是他们可以在任何主流浏览器的内置控制台中已经能做到的。” 这种说法是基于错误的前提。字符串是通过服务器和URL从浏览器外部获取的。最好的策略是编写代码时假设输入函数的数据来自于你不期望的地方,就像其他开发人员添加你的应用程序一样,这意味着不要使用过于强大的运算符例如eval - Mike Samuel
1
@Mike:我很难完全同意。虽然你说的在评估其他来源的输入时是正确的,但是当你仅评估用户输入时,我坚持我的答案。 - Andy E
1
@AndyE,我的理解是,“评估用户输入”这个短语包括评估用户Carol在用户Alice的浏览器中的输入。在每个产品经理和他们的母亲都试图使自己的网站“社交化”之前,更有限的理解可能是合理的,但今天我认为更广泛的理解应该成为默认选项,除非应用程序规范明确说明了其他情况。 - Mike Samuel
1
使用案例可以包括在服务器端评估用户输入。然后安全性就变得很重要。 - Hubert OG

5
您可以使用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)');

1
尝试使用 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>


1

你可以使用正则表达式来替换除白名单以外的所有内容。但由于 JavaScript 在客户端执行(除非你正在运行 AOL http 服务器),任何能够修改输入的人都可以修改代码 - 所以你并没有使它比原来更安全或不安全。


2
这取决于输入待评估表达式的用户是否与评估该表达式的用户相同。 - Bruno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接