使用Python清理用户输入
为基于Python的Web应用程序清除用户输入的最佳方法是什么?是否有单个功能删除HTML字符和任何其他必要的字符组合以防止XSS或者SQL注入攻击?
解决方案
回答
如果我们使用的是django之类的框架,则该框架可以使用标准过滤器轻松为我们完成此操作。实际上,我敢肯定django会自动执行此操作,除非我们告诉我们不要这样做。
否则,我建议在接受来自表单的输入之前使用某种正则表达式验证。我认为问题没有灵丹妙药,但是使用re模块,我们应该能够构造所需的东西。
回答
杰夫·阿特伍德(Jeff Atwood)本人描述了StackOverflow.com如何清除Stack Overflow博客上的用户输入(以非特定语言的术语):http://blog.stackoverflow.com/2008/06/safe-html-and-xss/
但是,正如Justin指出的那样,如果我们使用Django模板或者类似模板,则它们可能仍会清理HTML输出。
SQL注入也不应该成为问题。 Python的所有数据库库(MySQLdb,cx_Oracle等)始终清除传递的参数。这些库供所有Python的对象关系映射器(例如Django模型)使用,因此我们也不必担心那里的环境卫生。
回答
这是一个片段,它将删除不在白名单上的所有标签,以及不在属性白名单上的所有标签属性(因此我们不能使用onclick
)。
它是http://www.djangosnippets.org/snippets/205/的修改版,在属性值上使用了正则表达式,以防止人们使用" href =" javascript:...",以及在http://ha.ckers.org/xss.html。 (例如
<a href="javascript:alert('hi')">或者
<a href="ja vascript:alert('hi')">`等)。
如我们所见,它使用了(很棒的)BeautifulSoup库。
import re from urlparse import urljoin from BeautifulSoup import BeautifulSoup, Comment def sanitizeHtml(value, base_url=None): rjs = r'[\s]*(&#x.{1,7})?'.join(list('javascript:')) rvb = r'[\s]*(&#x.{1,7})?'.join(list('vbscript:')) re_scripts = re.compile('(%s)|(%s)' % (rjs, rvb), re.IGNORECASE) validTags = 'p i strong b u a h1 h2 h3 pre br img'.split() validAttrs = 'href src width height'.split() urlAttrs = 'href src'.split() # Attributes which should have a URL soup = BeautifulSoup(value) for comment in soup.findAll(text=lambda text: isinstance(text, Comment)): # Get rid of comments comment.extract() for tag in soup.findAll(True): if tag.name not in validTags: tag.hidden = True attrs = tag.attrs tag.attrs = [] for attr, val in attrs: if attr in validAttrs: val = re_scripts.sub('', val) # Remove scripts (vbs & js) if attr in urlAttrs: val = urljoin(base_url, val) # Calculate the absolute url tag.attrs.append((attr, val)) return soup.renderContents().decode('utf8')
正如其他发布者所说的那样,几乎所有的Python db库都负责SQL注入,因此这几乎涵盖了我们。
回答
我不再做网络开发,但是当我这样做时,我做了类似的事情:
当不应该进行任何解析时,我通常只是在存储数据时转义数据以不干扰数据库,并在显示数据时转义从数据库读取的所有内容以不干扰html(cgi.escape() Python)。
如果有人尝试输入html字符或者东西,他们实际上确实希望无论如何都将其显示为文本。如果他们不这样做,那就很难了:)
简而言之,总是逃避什么会影响当前数据目标的事情。
当我确实需要一些解析(标记或者其他方法)时,我通常尝试将该语言与html保持在一个非相交的集合中,因此我仍然可以将其适当地转义存储(在验证语法错误之后),并在显示时不解析为html不必担心用户放入的数据会干扰html。
另请参见转义HTML
回答
防止XSS的最好方法不是尝试过滤所有内容,而是简单地进行HTML实体编码。例如,自动将<转换为<。假设我们不需要接受任何html输入(在用作标记的论坛/评论区域之外,很少需要接受HTML),这是理想的解决方案;通过备用编码进行的排列如此之多,以至于除了超限制白名单(例如a-z,A-Z,0-9)外,其他任何东西都无法通过。
如果只是构建查询字符串,则与其他观点相反,仍然可以进行SQL注入。例如,如果仅将传入参数连接到查询字符串,则将具有SQL注入。防止这种情况的最佳方法也不是过滤,而是虔诚地使用参数化查询,并且永远不要连接用户输入。
这并不是说过滤仍然不是最佳实践,但是就SQL注入和XSS而言,如果忠实地使用参数化查询和HTML实体编码,我们将受到更大的保护。
回答
编辑:漂白剂是html5lib的包装,这使得它更容易用作基于白名单的清除工具。
html5lib带有基于白名单的HTML过滤器,可以很容易地对其进行子类化以限制允许用户在网站上使用的标签和属性,如果允许使用style
属性,它甚至会尝试清理CSS。 。
现在,在堆栈溢出克隆的sanitize_html实用工具函数中使用它:
http://code.google.com/p/soclone/source/browse/trunk/soclone/utils/html.py
我已经抛出了ha.ckers.org的XSS备忘单中列出的所有攻击(使用python-markdown2将Markdown转换为HTML后,可以很容易地以XML格式使用XML格式的攻击,而且看来还可以。
尽管实际上我必须禁用JavaScript才能测试XSS备忘单攻击,但Stackoverflow当前使用的WMD编辑器组件是一个问题,因为将它们全部粘贴到WMD中最终给了我警告框并清空了页面。
回答
要清理要存储到数据库中的字符串输入(例如客户名称),我们需要对其进行转义或者从中删除所有引号(',"),这有效地防止了经典的SQL注入,如果我们这样做从用户传递的字符串中组装一个SQL查询。
例如(如果可以完全删除引号,则可以接受):
datasetName = datasetName.replace("'","").replace('"',"")