使用eval实现Hackerrank列表题是否安全?有无替代方案?
Hackerrank列表操作题中eval的安全性分析与替代方案
我用Python 3.10的match语句完成了Hackerrank的列表操作题,通过eval动态执行输入的命令。我清楚动态执行代码存在安全风险,也不认同“完全禁用eval”这类绝对观点,认为应该理解风险并掌握规避方法。我原以为自己的实现是安全的——eval仅能执行单个表达式,测试恶意注入代码时会触发语法错误,但仍不确定是否存在遗漏的风险点,也不清楚如何通过globals和locals限制eval的风险。
我的实现代码如下:
def call_fun(i_lst): args_num = len(i_lst) match args_num: case 1: if i_lst[0] == "print": eval(i_lst[0] + "(o_list)") else: eval("o_list.{}()".format(*i_lst)) case 2: eval("o_list.{}({})".format(*i_lst)) case 3: eval("o_list.{}({},{})".format(*i_lst)) if __name__ == '__main__': N = int(input()) o_list = [] for x in range(N): command = input().split() call_fun(command)
当前实现的安全风险
你的实现并不安全,存在被代码注入的风险:
- 攻击者可以构造恶意的方法名字符串,通过属性链访问危险的内置对象。比如输入以下命令:
这段输入会被__class__.__base__.__subclasses__()[132].__init__.__globals__['os'].system('echo hacked')eval执行为o_list.__class__.__base__.__subclasses__()[132].__init__.__globals__['os'].system('echo hacked')(),从而调用系统命令执行恶意操作。 eval的“单个表达式”限制并不能阻止这类攻击,因为属性访问、函数调用本身就是合法的表达式组成部分。
如何用globals和locals限制eval风险
如果一定要用eval,可以通过传入严格限制的全局和局部命名空间来降低风险:
- 禁用所有内置函数,只暴露必要的对象:
但这种方式会导致# 在eval调用时传入限制后的命名空间 eval("o_list.{}()".format(*i_lst), {'__builtins__': None}, {'o_list': o_list})print无法直接使用(因为print属于内置函数),需要单独处理print逻辑,或者手动将print加入局部命名空间。 - 搭配方法白名单:提前定义允许调用的列表方法,只有方法名在白名单内时才执行
eval。
更安全的替代方案:避免使用eval
完全可以不用eval,通过getattr直接获取列表的方法并调用,同时配合白名单机制,这是更安全且可读性更高的实现:
def call_fun(i_lst): # 定义允许调用的列表方法白名单 allowed_methods = {'append', 'insert', 'remove', 'pop', 'sort', 'reverse'} cmd = i_lst[0] if cmd == 'print': print(o_list) elif cmd in allowed_methods: # 获取列表的对应方法 method = getattr(o_list, cmd) # 转换参数类型(题目中参数均为整数) args = map(int, i_lst[1:]) method(*args) else: # 处理非法命令,比如抛出异常或忽略 pass if __name__ == '__main__': N = int(input()) o_list = [] for x in range(N): command = input().split() call_fun(command)
这种方式完全杜绝了代码注入风险,同时逻辑清晰,符合Python的最佳实践。
内容的提问来源于stack exchange,提问作者Juan Paulo Pérez-Tejada
相关产品推荐
相关产品推荐

