在 Python 中使用黑名单和白名单进行字符串处理



我正在创建一个程序,我需要创建一个处理 eval(( 函数的用户输入的逻辑。 输入将是一个数学函数,我想处理一些不规则之处,并确保字符串是数学函数而不是恶意代码。

为此,我创建了一个逻辑,将字符串的所有字符与黑名单和白名单进行比较,问题是字符串只能包含特定排列的几个字符,例如cos,字符串不能包含c + o * s

whitelist = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(', ')',
'x', 'y', 'sin', 'cos', 'tg', '+', '-', '*', '/', ' ']
blacklist = ['a', 'b', 'd', 'f', 'h', 'i', 'j', 'k', 'l', 'm', 'p', 'q',
'r', 'u', 'v', 'w', 'z']
def stringTreat(string):
if not any(ch in string for ch in blacklist):
if all(ch in whitelist for ch in string):
print('OK!!')
else:
print('stop at whitelist')
else:
print('stop at blacklist')
string = input('input:')
stringTreat(string)

如果我12 + 67 - 82设置为此示例的输入,则输出为OK!!,但如果cos(x)是输入,则输出更改为stop at whitelist

如何创建一个逻辑来接受子字符串,例如(sin,cos,tg(,字符例如(0,1,2,3...(,并且不接受其他子字符串和字符,例如(a,f,@,$,ls,mv(?

您尝试构建的内容通常称为解析器,对于解析器,您可能会发现许多已建立的算法很有用(请考虑查看ply包(。

通常,这分为两个步骤:分词器和语法。分词器将输入字符串分解为多个部分,并可能用一些额外的信息标记它们(例如,12 + cos(3)可能会变得[NUM(12), OP(+), FUNC(cos), LPAREN, NUM(3), RPAREN](。请注意,您可以使用如下所示的正则表达式构建一个非常简单的分词器:

In [1]: re.split(r'b', '12 + 16 - cos(2)')
Out[1]: ['', '12', ' + ', '16', ' - ', 'cos', '(', '2', ')']
In [2]: [v.strip() for v in re.split(r'b', '12 + 16 - cos(2)') if v.strip()]
Out[2]: ['12', '+', '16', '-', 'cos', '(', '2', ')']

然后,语法会寻找标记的模式,并可以告诉如何处理它们,通常将它们形成某种"语法树",以后更容易操作。 例如,您可能会认为整个函数是单个一元表达式,EXPR(cos, NUM(3)),然后将加法运算视为另一个二进制表达式,EXPR(add, NUM(12), EXPR(cos, NUM(3))).请注意,此树现在很简单:当您遇到表达式时,查看第一个位置的运算符('add'、'cos' 等(,并使用它来确定如何处理剩余的操作数。这些可以递归处理,因此内部表达式解析为某个数字,然后外部表达式可以使用该数字解析为最终的单个数字。

您不必以这种方式做事,但是拥有这种背景表明,与其像您正在尝试的那样一次完成所有操作,不如先尝试使用一个标记器,然后您只需要一些令牌STR(cos)STR(ls),您可以轻松地将前者识别为有效输入,并在遇到另一个(或不在白名单上的任何其他内容(时抛出错误。

作为旁注,您通常只有一个白名单或黑名单,而不是两者兼而有之。白名单通常假定其他任何内容无效,而黑名单假定其他任何内容都有效,因此,如果两个列表或两个列表都属于两个列表或两个列表,则两者都会引入问题。

最后一点,由于您使用的是 Python,如果您小心并且允许通用 Python 语法,您可以使用evalexec为您解析和执行。 例如:

In [1]: import math
In [2]: eval('12 + 16 - cos(2)', {'cos': math.cos}, {})
Out[2]: 28.416146836547142

您可以在这些字典中指定您希望用户有权访问的函数,并阻止他们与程序状态中的任何其他内容进行交互。我仍然可能不会这样做,除非你至少稍微信任用户,或者如果他们只能通过搞砸事情来伤害自己。

最新更新