使用具有相同值的重复响应 header 可以吗?

Is it fine to use duplicate response header with same value?

我发现一个响应,其中重复的 header 被具有相同值的应用程序使用。谁能告诉我,这是一个好的编程实践,还是用于安全角度或其他任何方面?

  
     HTTP/1.1 200 
     Accept-Ranges: bytes
     Cache-Control: no-cache, must-revalidate, private
     Content-Type: text/html
     Date: Mon, 20 Nov 2017 04:08:51 GMT
     Expires: 0
     Last-Modified: Thu, 16 Nov 2017 14:04:48 GMT
     Pragma: 
     Public-Key-Pins: pin-sha256="5w0XrTCAbsVO7vTngDViNHPutlvB43qYionPbpV2ky0=";  
     max-age=5184000; includeSubDomains;
     Server: Any
     Set-Cookie: ********************* httponly; secure; path=/
     Strict-Transport-Security: max-age=31536000 ; includeSubDomains
     Strict-Transport-Security: max-age=31536000; includeSubDomains
     X-Content-Type-Options: nosniff
     X-Content-Type-Options: nosniff
     X-Frame-Options: SAMEORIGIN
     X-Frame-Options: SAMEORIGIN
     X-XSS-Protection: 1; mode=block
     Content-Length: 559
     Connection: Close

此应用程序正在使用具有相同值的重复 X-Content-Type-Options header、Strict-Transport-Security、X-Frame-Options header。

这是一个 programming/configuration 错误。 RFC 7230 (Section 3.2.2) 说:

A sender MUST NOT generate multiple header fields with the same field name in a message unless either the entire field value for that header field is defined as a comma-separated list [i.e., #(values)] or the header field is a well-known exception (as noted below).

所以像这样使用多个 header:

Strict-Transport-Security: max-age=31536000 ; includeSubDomains
Strict-Transport-Security: max-age=31536000; includeSubDomains

是未定义的行为。同一节说:

A recipient MAY combine multiple header fields with the same field name into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field value to the combined field value in order, separated by a comma.

所以上述 header 的一种可能解释是:

Strict-Transport-Security: max-age=31536000 ; includeSubDomains, max-age=31536000; includeSubDomains

根据 https://www.rfc-editor.org/rfc/rfc6797#section-6.1,这不是有效语法,可能会被拒绝(因此,HSTS 政策不适用)。

请注意,并非所有实施都可能遵循此指定行为。例如,Python http 库只是 returns 一个 header 的列表,在上面的例子中它可能看起来像:

[
    # ...
    ('Strict-Transport-Security', 'max-age=31536000 ; includeSubDomains'),
    ('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
    # ...
]

根据实现的不同,至少可以实现以下三种行为:

  • 合并 headers,用逗号分隔(由 RFC 7230 指定)。
  • 使用第一个 header 匹配项。
  • 使用最后一个 header 匹配项。

验证和使用之间的解释不匹配可能会导致攻击,尽管这对于 HSTS 可能不是很严重 header。