考虑一个前端 JavaScript 应用程序,其中需要根据一些简单的逻辑(用户拥有的角色和其他一些逻辑状态)显示或隐藏菜单项。
引入了一种简单的语言以简洁易读的方式定义此逻辑,并且为每个菜单项分配了一个字符串条件,如下所示"isLoggedIn() AND NOT role(PARTNER)"
:
为了实际检查这个条件,实现了一个简单的编译器,它将这种语言翻译成一个有效的 JavaScript 代码字符串,并使用eval()
最后返回的布尔结果来执行它。
编译器通过替换一些结构来工作,比如AND
,OR
和NOT
有效的 JavaScript 等价物,比如&&
,||
并!
使用简单的正则表达式:
private compileExpression(expression: string) {
// Adding commas around function arguments (to make them strings)
expression = expression.replace(/(\w+)\((.*?)\)/g, `$1('$2')`);
// Replacing "AND"
expression = expression.replace(/\sAND\s/g, ' && ');
// Replacing "OR"
expression = expression.replace(/\sOR\s/g, ' || ');
// Replacing "NOT"
expression = expression.replace(/(\s|^)NOT\s/g, ' !');
// Prefixing predicates
expression = expression.replace(/(\w+)\((.*?)\)/g, 'Π.$1($2)');
return expression;
}
这变成"isLoggedIn() AND NOT role(PARTNER)"
,"Π.isLoggedIn() && !Π.role('PARTNER)"
然后由 执行eval()
:
public matchCondition = (condition: string): boolean => {
// Using greek letter "P" for predicate (for shortness and uniqueness)
// noinspection NonAsciiCharacters
const Π = this.predicates;
const expression = this.compileExpression(condition);
const result = eval(expression);
return result;
}
Π
是一个具有简单谓词函数的对象,它返回布尔值,如isLoggedIn(): boolean
和role(roleName: string): boolean
。
正在翻译和执行的表达式作为字符串静态存储在本地对象中,并且无法从全局上下文中访问。此外,所有表达式都是由开发人员编写的,而不是以任何方式来自应用程序的用户。
使用eval()
这种方式是否安全,还是应该不惜一切代价避免(例如“eval 是邪恶的”、“从不使用 eval”等)?
有哪些可能的攻击向量可以用来破坏这种 eval 的使用?
如果表达式字符串将使用 XHR/Fetch 从 HTTPS 服务器加载,它会在安全方面改变情况吗?
引入这种语言而不是直接在代码中定义规则的原因是它要求这些条件可以在字符串值中定义,例如在 JSON 文件中。另一个原因是这样的语言更容易一目了然。