【问题标题】:Sanitising user input using Python使用 Python 清理用户输入
【发布时间】:2017-07-11 09:42:17
【问题描述】:

为基于 Python 的 Web 应用程序清理用户输入的最佳方法是什么?是否有一个函数可以删除 HTML 字符和任何其他必要的字符组合以防止 XSS 或 SQL 注入攻击?

【问题讨论】:

  • 您不应该试图通过清理用户输入来修复 SQL 注入!如果正确使用数据库 API,则不会有 SQL 注入的机会。
  • ... if database API is used properly there is no chance of SQL injection。正确地说,您的意思是使用参数化查询吗?这能 100% 覆盖你吗?
  • @buffer,我知道你的评论已经过时了,但是如果你想让除了 OP 之外的其他人看到你的 cmets,你必须用 \@ 符号来称呼他们。

标签: python xss


【解决方案1】:

这里是一个sn-p,它将删除所有不在白名单上的标签,以及所有不在attribues白名单上的标签属性(所以你不能使用onclick)。

它是http://www.djangosnippets.org/snippets/205/的修改版本,在属性值上使用正则表达式以防止人们使用href="javascript:...",以及http://ha.ckers.org/xss.html中描述的其他情况。
(例如<a href="ja	vascript: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 数据库库都负责 SQL 注入,所以这应该几乎涵盖了您。

【讨论】:

  • 我对此表示赞同,但现在我不太确定。我不认为这可以保护 IE 用户免受 src="vbscript:msgbox('xss')" 攻击。
  • 你可以很容易地用另一个用于 vbscript 的正则表达式添加它:就像用于 javascript 的那个:
  • @tghw,这里的 vbscript 示例说明白名单解决方案通常比黑名单解决方案更可取的原因。你怎么能确定你需要的一切都被列入黑名单?有了黑名单,一个新的浏览器可能会在下周出现并且容易受到攻击,因为它支持一种新型的脚本标签。
  • @gnibbler 我同意,其中大部分是白名单解决方案,但对于 href 和 src,真的没有办法轻松地列入白名单。我能想到的唯一选择是通过传入页面 URL 然后遍历每个链接和图像并根据页面 URL 找出绝对 URL 来使所有 URL 成为绝对 URL。我想得越多,这似乎就越容易。我会在上面添加。
  • 通常很难清理 HTML,有很多向量:nick.cleaton.net/xssrant.html
【解决方案2】:

编辑bleach 是 html5lib 的包装器,使其更容易用作基于白名单的清理程序。

html5lib 带有一个基于白名单的 HTML 清理器 - 很容易对其进行子类化以限制允许用户在您的网站上使用的标签和属性,如果您允许使用它,它甚至会尝试清理 CSS style 属性。

现在我在 Stack Overflow 克隆的 sanitize_html 实用函数中使用它:

http://code.google.com/p/soclone/source/browse/trunk/soclone/utils/html.py

我已经抛出了ha.ckers.org's XSS Cheatsheet 中列出的所有攻击(在使用python-markdown2 执行 Markdown 到 HTML 转换后,很容易将 available in XML format 攻击它,并且它似乎保持正常。

不过,Stackoverflow 当前使用的 WMD 编辑器组件是一个问题 - 实际上,我必须禁用 JavaScript 才能测试 XSS Cheatsheet 攻击,因为将它们全部粘贴到 WMD 最终会出现警告框并空白页面。

【讨论】:

    【解决方案3】:

    防止 XSS 的最佳方法不是尝试过滤所有内容,而是简单地进行 HTML 实体编码。例如,自动将

    与其他意见相反,如果您只是构建查询字符串,SQL 注入仍然是可能的。例如,如果您只是将传入参数连接到查询字符串,您将有 SQL 注入。防止这种情况的最佳方法也不是过滤,而是虔诚地使用参数化查询并且从不连接用户输入。

    这并不是说过滤仍然不是最佳实践,但就 SQL 注入和 XSS 而言,如果您虔诚地使用参数化查询和 HTML 实体编码,您将受到更多保护。

    【讨论】:

    • 这在很多情况下是不正确的。请参阅 OSWAP 注释“为什么我不能只对不可信的数据进行 HTML 实体编码?” owasp.org/index.php/…
    【解决方案4】:

    Jeff Atwood 本人在 Stack Overflow 博客上描述了 StackOverflow.com 如何清理用户输入(以非语言特定的术语):https://blog.stackoverflow.com/2008/06/safe-html-and-xss/

    但是,正如 Justin 指出的那样,如果您使用 Django 模板或类似的东西,那么它们可能会清理您的 HTML 输出。

    SQL 注入也不应该是一个问题。所有 Python 的数据库库(MySQLdb、cx_Oracle 等)总是清理您传递的参数。所有 Python 的对象关系映射器(例如 Django 模型)都使用这些库,因此您也不必担心那里的卫生问题。

    【讨论】:

      【解决方案5】:

      我不再做网络开发了,但是当我做的时候,我做了这样的事情:

      当不应该发生解析时,我通常只是在存储数据时转义数据以不干扰数据库,并在显示时转义从数据库中读取的所有内容以不干扰 html (cgi.escape () 在 python 中)。

      如果有人尝试输入 html 字符或内容,他们实际上希望将其显示为文本。如果他们没有,那就太难了:)

      简而言之,总是避开可能影响数据当前目标的内容。

      当我确实需要一些解析(标记或其他)时,我通常会尝试将该语言保持在与 html 不相交的集合中,这样我仍然可以将它适当地转义(在验证语法错误之后)并将其解析为 html显示时不必担心用户放入其中的数据会干扰您的 html。

      另见Escaping HTML

      【讨论】:

        【解决方案6】:

        要清理要存储到数据库的字符串输入(例如客户名称),您需要对其进行转义或明确删除其中的任何引号('、")。这有效地防止了经典的 SQL 注入,它可以如果您从用户传递的字符串组装 SQL 查询,就会发生这种情况。

        例如(如果可以完全删除引号):

        datasetName = datasetName.replace("'","").replace('"',"")
        

        【讨论】:

        • 呃......不......我仍然不会这样做。对于作为数据项的所有内容,请使用参数化查询。对于非数据(动态构建的查询),您确实应该使用白名单。 pg_catalog.pg_user 不包含引号,但您可能也不希望在生成的查询中使用引号。而是做类似datasetName = datasetName if datasetName in DATASETNAME_WHITELIST else sulk()
        【解决方案7】:

        如果您使用像django 这样的框架,该框架可以使用标准过滤器轻松地为您完成这项工作。事实上,我很确定 django 会自动执行此操作,除非您告诉它不要这样做。

        否则,我建议在接受来自表单的输入之前使用某种正则表达式验证。我认为您的问题没有灵丹妙药,但是使用 re 模块,您应该能够构建所需的内容。

        【讨论】:

        猜你喜欢
        • 2013-11-17
        • 2019-07-01
        • 2013-09-14
        • 2021-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-02
        • 2014-09-08
        相关资源
        最近更新 更多