如何停止HTTP(和rfc822,电子邮件)标头注入? [英] How to stop HTTP (and rfc822, email) header injection?

查看:211
本文介绍了如何停止HTTP(和rfc822,电子邮件)标头注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我问这个问题(并回答它),以便获取一些(希望有用的)信息,因为我无法使用搜索引擎轻松找到它。但是,随意回答它并添加有用的信息:-) 。)

(I am asking this question (and answering it), to make accessible some (hopefully useful) information, since I could not find this readily using search engines. However, feel free to answer it and add useful information :-).)

如何在Python中转义/引用HTTP标头?

How can HTTP headers be escaped/quoted in Python?

和/或它们如何成为验证以确保它们不包含任何上下文转义值?

And/Or how can they be validated to make sure they do not contain any context-escaping values?

换句话说,我们如何为HTTP标头做什么, cgi。转义 urllib.quote 方法(和清理)对HTML和URL做什么?这可用于防范 HTTP标头注入和类似攻击。

In other words, how can we do for HTTP headers, what cgi.escape and urllib.quote methods (and sanitizing) do for HTML and URLs? This can be used to guard against HTTP header injection and similar exploits.

我们让用户提供一个应重定向的URL。我们希望防止注入攻击(其中 SQL注入是众所周知的)。如果我们决定重定向使用位置:标题,我们如何逃避用户提供的URL以防止HTTP标头注入(或检测)如果它包含对HTTP有危险的值)?

We have the user providing a URL to which one should be redirected. We want to protect against injection attacks (of which SQL injection is a well known one). Setting aside (for this discussion) security concerns (concerning surreptitious automatic forwarding to a URL in a domain which the user can choose), if we decide to redirect using the Location: header, how can we escape the user-supplied URL to prevent HTTP-header injection (or detect if it contains values dangerous for HTTP)?

# on a "posix sh"-like command-line...
# ...(it contains a malicious HTTP value)
$ redirect_to 'http://example.com'"\r\n"'Set-Cookie: malicious=value'

现在,在实现 redirect_to 命令的python代码中,我们想像上面那样输入要么被转义(使它无害),要么是一个错误。我们怎么办呢?

Now, in our python code implementing the redirect_to command, we want to input like the above to either be escaped (rendering it harmless), or to be an error. How can we do so?

推荐答案

如果输入数据包含在标题字段参数中(例如<$ c的 filename 参数$ c> Content-Disposition 标题),可以用 email.utils.encode_rfc2231 (如约束由这些规范,它定义了 rfc2231编码)。

If the input data is being included in a header field parameter (for example the filename parameter of the Content-Disposition header), it can be encoded with email.utils.encode_rfc2231 (as constrained by these specifications, which define a variation of the rfc2231 encoding).

如果包含头字段参数,那么它似乎这种方法无法使用。在这种情况下,最安全的赌注可能是不包括输入,如 Julian Reschke所写;但是,如果您坚持要包含输入,则可能需要尝试以下方法之一:

If it is not being included a header field parameter, then it seems that this method cannot be used. In such a situation, the safest bet would likely be to just not include the input, as Julian Reschke wrote; however, if you insist on including the input, you may want to try one of the following methods:

(这可能是不安全的,因为 HTTP不是符合MIME的协议,因此除非使用 MIME-Version 标题(甚至可能使用使用它?),HTTP的这些方式可能无法正常工作。)

(which may be insecure, since HTTP is not a MIME-compliant protocol, and so unless the MIME-Version header is used (and possibly even if it is used?), these ways may not work correctly for HTTP.)

这样做,虽然它可能不完全万无一失(编辑:它万无一失(当自己使用时);它接受 \\\\\ n ,它会终止标题并启动正文!因此 \ r 并且需要处理 \ n ,除非前面是非 \ r / \ n 空格(如制表符或空格。)),是使用 email.header 模块。这是专门为 rfc822标题设计的(编辑 :但是(似乎,因为电子邮件包曾经是几个单独的模块(示例) )不适用于HTTP标题!),因此似乎是工作。此标头类用于编码标头,而不是完整的标头名称:值,这是这份工作的候选人(我们想要在其中放弃或逃避价值)。

to do this, although it may not be totally foolproof (edit: it is not foolproof (when used by itself); it accepts \r\n\r\n, which terminates headers and starts the body! Therefore \r and \n would need to handled, unless preceded by non-\r/\n whitespace (like tabs or spaces.)), is to use the email.header module. This is designed specifically for rfc822 headers (edit: but (seemingly, since the email package used to be several separate modules (example)) not for HTTP headers!), so would seem to be the tool for the job. This Header class is meant for encoding header values, not the full Header-Name: value, and so is a candidate for this job (where we want to vaidate or escape the value only).

(提示:许多使用其他MIME格式(编辑:可能还有类似MIME)时,电子邮件模块中的工具也很方便;所以 cgi 模块中的内容, cgi.FieldStorage ,特别是对于HTTP表单解析。)

(Hint: many of the tools in the email module are also handy when working with other MIME-format (edit: and possibly also MIME-like) stuff; so too stuff in the cgi module, cgi.FieldStorage in particular for HTTP-form parsing.)

但是, email.header 如果输入似乎恶意(似乎包含另一个(嵌入式)标头),则只会引发错误;但是,它似乎不会通过转义来处理无效的输入(如果不是这样,请在评论中更正)。 ( charset 参数应该转义标题片段,返回有效输入,但是,它可能与用户代理没有很好的兼容性(电子邮件, HTTP等等);请参阅此处编辑:许多HTTP用户代理支持(不一定是 charset email.header.Header 类的编码参数(除了rfc2231之外,它似乎使用了一些特定于MIME的编码编码),但是) rfc5987 编码。)

However, email.header only will raise an error if the input seems malicious (seems to contain another (embedded) header); however, it will not, it seems, handle invalid input by escaping it (please correct this in the comments if it is not so). (The charset parameter should escape the header-fragment, returning valid input, however, it may not have such good compatibility with user agents (email, HTTP, etc.); see here (edit: many HTTP user agents support (not necessarily the charset parameter of the encoding for the email.header.Header class (which seems to use some MIME-specific encodings besides rfc2231 encoding), but) the rfc5987 encoding).

示例:

import email.header
import re

def check_string_for_rfc822_header(s):
    wip_header_component = str(email.header.Header(s))
    if re.search(r'(\r?\n[\S\n\r]|\r[\S\r])', wip_header_component):
        raise Exception
    else:
        return wip_header_component

# testing...
>>> check_string_for_rfc822_header("aaa")
"aaa"
>>> check_string_for_rfc822_header("a\r\nb")
"a\r\nb"
>>> check_string_for_rfc822_header("a\r\nb: c")
<error>



另一种方式...



to这样做,似乎只需删除 \ r \ n 字符(但每个单独分开;不要只删除完整字符串 \\\\ n ,因为当单独出现时,这仍然会使这些未转义,并且许多(大多数?)HTTP工具将分别接受它们中的每一个!)。同样,我们可以通过替换 \ r \ n \ r 和<$ c来转义标题$ c> \ n ,其前缀为空格(这是转义标题的方法;参见标准)。

Another way...

to do this, it seems, would be to simply remove \r and \n characters (each separately however; do not just remove occurences the full string \r\n, since this would still leave these unescaped when occuring separately, and many (most?) HTTP utils will accept each of them separately!). Similarly, we can escape the header by replacing \r\n, \r, and \n, with themselves prepended by whitespace (which is the way to escape header; see the standard).

但是,这种方法没有考虑标准的细节(例如, rfc822标题必须是ACSII ),可以单独使用。

However, this method does not take into account the details of the standards (for example, rfc822 headers must be ACSII), which may be exploitable on their own.

示例:

def remove_linebreakers(s):
    return s.replace("\n", "").replace("\r", "")

# or...
import re

def remove_linebreakers(s):
    re.sub(r'[\n\r]', '', s)


# testing...
>>> remove_linebreakers("aaa")
"aaa"
>>> remove_linebreakers("a\r\nb")
"ab"
>>> remove_linebreakers("a\r\nb: c")
"ab: c"



< h1>总结......

第一种方式似乎更好,但仅用于验证(不用于转义),除非它是参数值,在这种情况下逃避它使用 email.utils.encode_rfc2231

示例:

# if we are not working with a header param value, the following...
# ...raises email.errors.HeaderParseError if input is poisonous when in a header
wip_header_component = str(email.header.Header('<input>'))
header_component = (raise_error() if re.search(r'(\r?\n[\S\n\r]|\r[\S\r])', wip_header_component) else wip_header_component)
# ...or if we *are* working with a header param value...
email.utils.encode_rfc2231('<input>', 'UTF-8')

这篇关于如何停止HTTP(和rfc822,电子邮件)标头注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆