HTTP头部值允许使用哪些字符?

64
在研究了HTTP/1.1标准,特别是第31页及相关内容后,我得出结论:HTTP标头值中可以存在任何8位八位组。即,任何代码在[0,255]范围内的字符。

然而,我尝试过的HTTP服务器拒绝接受代码大于127的任何内容(或大多数US-ASCII不可打印字符)。

以下是标准中使用的语法摘录:

message-header = field-name ":" [ field-value ]
field-name     = token
field-value    = *( field-content | LWS )
field-content  = <the OCTETs making up the field-value and consisting of
                  either *TEXT or combinations of token, separators, and
                  quoted-string>

CR             = <US-ASCII CR, carriage return (13)>
LF             = <US-ASCII LF, linefeed (10)>
SP             = <US-ASCII SP, space (32)>
HT             = <US-ASCII HT, horizontal-tab (9)>
CRLF           = CR LF
LWS            = [CRLF] 1*( SP | HT )
OCTET          = <any 8-bit sequence of data>
CHAR           = <any US-ASCII character (octets 0 - 127)>
CTL            = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
TEXT           = <any OCTET except CTLs, but including LWS>

token          = 1*<any CHAR except CTLs or separators>
separators     = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\"
               | <"> | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT

quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext         = <any TEXT except <">>
quoted-pair    = "\" CHAR

正如您所看到的field-content可以是一个quoted-string,这是一个带引号的TEXT序列(即除"和值从[0-8, 11-12, 14-31, 127]范围之外的任何8位八进制数)或quoted-pair(后跟来自[0, 127]范围内的任何值的\)。也就是说,通过加引号和添加\前缀的特殊符号,可以传递任何8位字符序列。

(注意,标准不会以任何特殊方式处理NUL(0x00)字符)

但是,很显然,我尝试过的所有服务器都不符合规范,或者自1999年以来标准已经改变,或者我无法正确阅读它。

那么... HTTP标头值允许使用哪些字符以及为什么?

附言:我正在寻找在HTTP标头值中传递utf-8编码序列的方法(如果可能的话,不需要额外的编码)。


似乎没有人真正认真对待标准的这一部分。最终,我只是对头部值进行了URL编码。 - C.M.
请注意,field-names中的separators也需要进行编码。此外,如果您使用WinHTTP,则必须对field-name中的单引号符号进行编码,否则请求将失败。 - C.M.
@JulianReschke 我之前不知道 RFC 7230,它是官方的 HTTP/1.1 标准吗?(问这个问题是因为 greenbytes 的链接指向一个标记为“PROPOSED STANDARD”的文档) - C.M.
4
RFC 7230并没有改写RFC 2616,而是将其澄清了,这让人感到欣慰。[https://tools.ietf.org/html/rfc7230#section-3.2](§3.2)使用标记VCHAR来指定允许的字段内容;在[https://tools.ietf.org/html/rfc7230#section-1.2](§1.2)中,VCHAR被定义为任何可见的USASCII字符。这个澄清的标记消除了像RFC 2616那样花费时间去剔除非可见字符的需要,但是没有扩展1999/1982年的定义以包括128-255。 OP的问题是“HTTP标题值中允许哪些字符以及原因”。我已经回答了这个问题,并提供了参考。 - Geek Stocks
1
@C.M. - 是的,请参见https://www.rfc-editor.org/info/rfc2616。 - Julian Reschke
显示剩余4条评论
3个回答

19

RFC 2616已经过时,其中的相关部分已被RFC 7230所替代。

NUL八位字节在注释和引号字符串文本中不再被允许使用,并且对它们中反斜杠转义的处理已经得到澄清。quoted-pair规则不再允许转义除HTAB之外的控制字符。在头字段和原因短语中的非US-ASCII内容已被弃用并且变得不透明(TEXT规则已被删除)。(第3.2.6节)

实质上,RFC 2616默认使用ISO-8859-1,但这既不足够也不具有互操作性。因此,RFC 7230已废除了字段值中的非ASCII字符。建议在此基础上使用转义机制(例如RFC 8187或纯URI百分比编码)。


35
RFC 2616已经过时了吗?是的。这是否回答了提问者的问题:“HTTP头值允许使用哪些字符以及原因是什么?”不是。 - Geek Stocks
4
非ASCII字符已过时。您可以发送它们,但不能保证接收方会按照您期望的方式处理。这就是规范所说的,也是答案 :-) - Julian Reschke
2
@JulianReschke 我终于看了RFC 7230。我没有在p3.2.6中看到任何关于非美国ASCII内容的“废弃”--它似乎允许在quoted-string中使用任何0x80-0xFF字符。然而,0x00-0x7F范围则被消除了。也就是说,根据此标准,只要您转义0x00-0x7F范围的“禁止”部分,您就可以传递标题值中的UTF-8数据。我错了吗? - C.M.
1
惯例上,以“obs-”为前缀的ABNF规则名称表示“已过时”的语法规则,这些规则出于历史原因而出现。 - Julian Reschke
请注意,RFC 9110取代了RFC 7230,并对此进行了更新讨论,我认为这非常有帮助:https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5 - recvfrom
显示剩余3条评论

12
对于像我这样为了标题而来到这里的人,“HTTP头值允许使用哪些字符?”
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~
!#$&'()*+,/:;=?@[]
%20,%21,%22,%23,%24,%25,%26,%27,%28,%29,%2A,%2B,%2C,%2F,%3A,%3B,%3D,%3F,%40,%5B,%5D

@C.M.的评论中提到wikipedia的URL编码 RFC 3986第2.2节保留字符(2005年1月) !#$&'()*+,/:;=?@[] RFC 3986第2.3节非保留字符(2005年1月)

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~

百分号编码后的保留字符(␣ == " ")
! " # $ % & ' ( ) * + , / : ; = ? @ [ ]
%20 %21 %22 %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D
  • and a helpful json for our python code:
[
    ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", "_", ".", "~"],
    ["!", "#", "$", "&", "'", "(", ")", "*", "+", ",", "/", ":", ";", "=", "?", "@", "[", "]"],
    {" ": "%20", "!": "%21", "\"": "%22", "#": "%23", "$": "%24", "%": "%25", "&": "%26", "'": "%27", "(": "%28", ")": "%29", "*": "%2A", "+": "%2B", ",": "%2C", "/": "%2F", ":": "%3A", ";": "%3B", "=": "%3D", "?": "%3F", "@": "%40", "[": "%5B", "]": "%5D"}
]

我相信Julian Reschke已经回答了楼主的“为什么”。

-3

看起来HTTP/1.1规范中存在错误。正如你所指出的,§4.2将字段内容描述为OCTET:

field-content = 组成字段值的OCTET

OCTET在§2.2中定义为:

OCTET = 任何8位数据序列

这些行是你得出允许大于127的八位字节的结论的基础,我当然能理解你是如何得出这个结论的。§4.2中提到的OCTET是误导性的错误;它应该是CHAR。

如果你从头阅读§4.2(消息头),你会注意到以下指导:

HTTP头字段...遵循RFC 822第3.1节中给出的相同通用格式

如果我们按照指示去RFC 822,特别是§3.1.2(头字段的结构),我们会了解到以下内容:

字段名称必须由可打印的ASCII字符组成(即,值为33到126之间的字符,十进制形式,除了冒号)。字段主体可以由任何ASCII字符组成,除了CR或LF。

因此,虽然HTTP/1.1是在1999年编写的,但他们使用了1982年的定义来描述字段内容。 1982年,字符0-127被称为“ASCII”,而128-255被称为“扩展ASCII”。 现在,在这个答案中,我不会涉及使用术语“扩展ASCII”时引发的争吵我只会指向RFC 822的§3.3,以获得当时被认为是“任何ASCII字符”的定义:

CHAR = any ASCII character ( Octal: 0-177, Decimal: 0.-127.)

所以,这就是关键证据。“ASCII”在1982年停留在127。RFC 2616 §4.2的书面段落指引您朝正确的方向前进,而该部分中对令牌OCTET的不幸误用则让您陷入了这个兔子洞。


3
那种解释是错误的,请具体查看https://www.greenbytes.de/tech/webdav/rfc2616.html#rfc.section.2.2.p.4。 - Julian Reschke
4
@GeekStocks - 你得出了错误的结论。RFC 2616确实允许使用非ASCII字符。RFC 7230已经弃用了它们,原因就是我提到的那些(而我应该知道,因为我是其中一位作者之一)。“遵循格式”是对格式来源的解释;它不是规范性参考。 - Julian Reschke
2
@JulianReschke - 这真的令人发笑。让我们看看我能否解决这个问题。原帖中指出“...我尝试的服务器拒绝接收任何带有代码> 127的内容”。您自己的RFC 2616 §2.2链接显示了为什么原帖不能发送128...255。它声明_美国标准代码是由ANSI X3.4-1986 [注脚21]定义的编码字符集_。前往注脚21。它是对7位美国标准代码的引用。现在,请告诉我如何只使用7位获得大于127的数字?(_放下麦克风_);-p - Geek Stocks
5
在RFC 2616中,“TEXT”的ABNF为“<任何OCTET,但不包括CTLs,但包括LWS>”。OCTET被定义为“<任何8位数据序列>”。除此之外,RFC 2616非常清楚地指出:“只有按照RFC 2047的规则进行编码时,* TEXT的单词才可以包含字符集其他ISO-8859-1 [22]的字符。”因此,*可以在TEXT中使用来自ISO-8859-1(它是US-ASCII的超集)的字符。我认为这很明确。对US-ASCII的引用适用于ABNF规则中的“US-ASCII”,而不适用于OCTET。 - Julian Reschke
7
我只想知道HTTP头值中的有效字符是什么。解开一个自指ABNF文档的移动目标并不产生成果。我们需要一个工作参考实现和单元测试来澄清边界情况,以解决这个混乱局面。为什么有这么多不同的元数据处理方式?这是为了给网络开发人员创造工作保障,还是为了创建新的安全攻击面供安全公司修复? - Systemsplanet
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接