Python smtplib 和数据净化

1 投票
1 回答
1248 浏览
提问于 2025-04-18 17:54

我正在使用一个非常基础的smtplib功能(几乎完全是文档中的例子)。我通过使用request.forms.get()从一个Bottle FormsDict中获取主题和消息,然后用这段代码发送邮件。

msg = MIMEText(message)
msg['Subject'] = subject
msg['From'] = config['from_email']
msg['To'] = config['to_email']
s = smtplib.SMTP('localhost')
s.sendmail(config['from_email'], [config['to_email']], msg.as_string())
s.quit()

我习惯于对用户输入进行清理,以防止像XSS这样的安全问题(通常只是依赖Jinja2的魔法)。但在这种情况下,我只是通过邮件发送用户的输入,我应该怎么做呢?会有什么样的安全隐患呢?

1 个回答

2

我现在也在琢磨这个问题,有一点我可以告诉你的是,绝对应该使用 email.header.Header 来检测头部注入:

from email.header import Header
>>> Header('Test').encode()
'Test'
>>> Header('Test\n').encode()
'Test'
>>> Header('Test\nTest2').encode()
'Test\nTest2'
>>> Header('Test\nFrom').encode()
'Test\nFrom'
>>> Header('Test\nFrom:').encode()
(...)
HeaderParseError: header value appears to contain an embedded header: 'Test\nFrom:'

另外,看看 这个回答,我觉得如果输入可能有危险,最好直接拒绝,因为这可能意味着有人在尝试做一些不好的事情。

编辑:

结果发现,MIME 消息会自动验证头部,即使你不使用 email.header.Header,而且还会很好地编码邮件内容:

>>> msg = MIMEText('something\r\nsomething2', 'plain', 'UTF-8')
>>> msg.as_string()
'MIME-Version: 1.0\nContent-Type: text/plain; charset="utf-8"\nContent-Transfer-Encoding: base64\n\nc29tZXRoaW5nDQpzb21ldGhpbmcy\n'
>>> msg['From'] = 'me@localhost\r\nSubject: injected subject'
>>> msg.as_string()
(...)
HeaderParseError: header value appears to contain an embedded header: 'me@localhost\nSubject: injected subject'

你可以在 这里找到更多可能的注入问题。

所以我想说,为了安全起见,你不需要做什么特别的事情,因为:

  • 头部注入是默认检测的,所以黑客无法通过修改 subject 的值来添加头部信息。
  • 邮件内容是经过编码/引用的,所以如果有任何恶意字符序列可能会导致问题,它们应该会被中和。
  • 即使邮件内容没有被编码,我认为唯一能造成伤害的方式就是通过注入 MIME 边界来改变消息结构(比如多部分消息;示例: https://bugzilla.mozilla.org/show_bug.cgi?id=600464)。但边界是一个很长的随机字符串,所以猜测你的银行密码可能更容易。
  • <CRLF>.<CRLF> 序列 终止消息内容,但这不是问题,因为 MIME 类会将 CRLF 替换为 LF:

    >>> MIMEText('something\r\n.\r\nsomething2', 'plain', _charset='iso-8859-1').as_string()
    'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nsomething\n.\nsomething2'
    

撰写回答