让用户输入公式而不会产生安全漏洞的最佳方法?

时间:2014-05-07 04:00:34

标签: python python-2.7

我想让用户在给定一些参数的情况下输入计算公式。在没有安全漏洞的情况下,最好的方法是什么?

有点像这样:

def generate_bills():
    land_size = 100
    building_size = 200    
    class = 1
    formula = "(0.8*land_size)+building_size+(if class==1 10 else if class==2 5 else 2)"
    bill = calculate(formula,{'land_size':land_size,'building_size':building_size})

1 个答案:

答案 0 :(得分:0)

最简单的方法是清理输入。基本上,您只想关注您定义的参数并丢弃其他所有参数。数值方程式的卫生遵循几个简单的步骤:

  1. 提取静态的已知方程式部分(变量名称,运算符)
  2. 提取数值(如果用户可以定义自己的函数,则应该允许这样做)。
  3. 使用这些提取的部分重建功能。这会丢弃您未处理的所有内容,并且在使用Python的asteval时可能会出现问题。
  4. 这是一个非常强大的消毒剂,我改编自另一个项目。代码如下,但这里有一些示例输入和输出:

    在理想情况下,输入和输出是相同的:

    enter func: building_size*40+land_size*20-(building_size+land_size)
    building_size*40+land_size*20-(building_size+land_size)
    

    但是,如果用户使用空格/句点/制表符/甚至换行符(喘气),输出仍然很漂亮:

    enter func: 
        building_size *    500 + land_size-20+building_size.
    building_size*500+land_size-20+building_size
    

    无论用户尝试何种误导,恶意注入,输入都非常干净:

    enter func: land_size + 2 * building_size quit()
    land_size+2*building_size
    
    enter func: 1337+land_size h4x'; DROP TABLE members;
    1337+land_size
    

    更重要的是,您可以非常轻松修改功能,以便在消毒后将实际值输入等式中。我的意思是从land_size+2*building_size100+2*200使用简单的replace语句。这样,您的功能就可以evalast {/ 3}} parseable。{/ p>

    代码如下:

    import re
    
    # find all indices of a given char
    def find_spans(ch, s):
        return [tuple((i, i+1)) for i, ltr in enumerate(s) if ltr == ch]
    
    # check to see if an unknown is a number
    def is_number(s):
        try:
            float(s)
        except:
            return False
        return True
    
    # these are the params you will allow
    # change these to add/remove parameters/operators
    allowed_params = ['land_size', 'building_size']
    operators = ['+', '-', '*', '/', '(', ')']
    
    # get input
    in_formula = raw_input('enter func: ')
    
    # dictionary that will hold every allowed function element found in the input and its position(s)
    found_params = {}
    
    # extract param indices
    for param in allowed_params:
        found_params[param] = [i.span() for i in re.finditer(param, in_formula)]
    
    # extract operator indices
    for op in operators:
        found_params[op] = find_spans(op,in_formula)
    
    # get all index regions that are "approved", that is, they are either a param or operator
    allowed_indices = sorted([j for i in found_params.values() for j in i])
    
    # these help remove anything unapproved at beginning or end
    allowed_indices.insert(0,(0,0))
    allowed_indices.append((len(in_formula),len(in_formula)))
    
    # find all index ranges that have not been approved
    unknown_indices = [(allowed_indices[i-1][1], allowed_indices[i][0]) for i in range(1,len(allowed_indices)) if allowed_indices[i][0] <> allowed_indices[i-1][1]]
    
    # of all the unknowns, check to see if any are numbers
    numbers_indices = [(''.join(in_formula[i[0]:i[1]].split()),i) for i in unknown_indices if is_number(in_formula[i[0]:i[1]])]
    
    # add these to our final dictionary
    for num in numbers_indices:
        try:
            found_params[num[0]].append(num[1])
        except:
            found_params[num[0]] = [num[1]]
    
    # get final order of extracted parameters
    final_order = sorted([(i[0],key) for key in found_params.keys() for i in found_params[key]])
    
    # put all function elements back into a string
    final_function = ''.join([i[1] for i in final_order])
    
    #
    # here you could replace the parameters in the final function with their actual values
    # and then evaluate using eval()
    #
    
    print final_function
    

    如果有什么事情没有意义,请告诉我,我很乐意解释。