如何在基于Python的Web应用程序中对用户输入进行最佳清理?是否有单个函数可用于删除HTML字符和任何其他必要的字符组合,以防止XSS或SQL注入攻击?
如何在基于Python的Web应用程序中对用户输入进行最佳清理?是否有单个函数可用于删除HTML字符和任何其他必要的字符组合,以防止XSS或SQL注入攻击?
这里有一段代码片段,它会删除白名单之外的所有标签和标签属性(因此您不能使用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注入问题,因此这应该基本上可以满足您的需求。
编辑: 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的XSS防范清单 所列出的所有攻击方式进行了测试(这些攻击方式可以方便地以XML格式提供),在使用python-markdown2进行Markdown到HTML转换后,看起来它们都能够正常运行。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模型)使用,因此您在那里也不需要担心消毒问题。
我已经不再从事Web开发了,但是当我还在从事时,我的做法通常是这样的:
如果不需要解析,我通常只是将数据进行转义以避免存储时干扰数据库,并将从数据库读取的所有内容进行转义以避免在显示时干扰HTML(在Python中使用cgi.escape())。
有可能,如果有人尝试输入HTML字符或其他内容,他们实际上希望将其显示为文本。如果他们没有这样的意图,那就很难处理 :)
总之,始终要转义可能影响当前数据目标的内容。
如果需要进行一些解析(标记或其他),我通常会尝试将该语言保持在与HTML不重叠的集合中,以便在存储时适当地进行转义(在验证语法错误后),并在显示时将其解析为HTML,而不必担心用户输入的数据会干扰您的HTML。
另请参阅Escaping HTML
为了清理要存储到数据库中的字符串输入(例如客户姓名),您需要对其进行转义或直接删除其中的任何引号(',")。这有效地防止了经典的SQL注入攻击,因为如果您正在从用户传递的字符串组装SQL查询,则可能会发生此类攻击。
例如(如果完全删除引号是可以接受的):
datasetName = datasetName.replace("'","").replace('"',"")
pg_catalog.pg_user
不包含引号,但您可能不希望在生成的查询中出现它。相反,像这样做:datasetName = datasetName if datasetName in DATASETNAME_WHITELIST else sulk()
- SingleNegationElimination如果您正在使用像django这样的框架,该框架可以使用标准过滤器轻松为您完成此操作。实际上,我相信django会自动执行此操作,除非您告诉它不要这样做。
否则,在接受表单输入之前,我建议使用某种正则表达式验证。我认为没有解决您问题的万能方法,但是使用re模块,您应该能够构建所需内容。