RFC 2616によると:
継続行がスペースまたは水平タブで始まる場合、HTTP /1.1ヘッダーフィールド値を複数の行に折りたたむことができます。 折りたたみを含むすべての線形空白は、SPと同じセマンティクスを持っています。 受信者は、フィールド値を解釈したり、メッセージをダウンストリームに転送したりする前に、線形空白を単一のSPに置き換えることができます(MAY)。
ただし、werkzeugは、この規則に従っている場合でも、改行を含むヘッダー値を受け入れません。
>>> import werkzeug
>>> werkzeug.Headers().add('foo', 'bar\n baz')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../venv/local/lib/python2.7/site-packages/werkzeug/datastructures.py", line 1136, in add
self._validate_value(_value)
File ".../venv/local/lib/python2.7/site-packages/werkzeug/datastructures.py", line 1143, in _validate_value
raise ValueError('Detected newline in header value. This is '
ValueError: Detected newline in header value. This is a potential security problem
また、この制限は一貫して適用されません。
>>> werkzeug.Headers([('foo', 'bar\n baz')])
Headers([('foo', 'bar\n baz')])
ヘッダーを介したクライアント証明書のnginx転送に関連するテストケースを書き込もうとしたときにこの問題が発生したため、これを適切にサポートするための実際のユースケースがあります。
HTTPヘッダーは改行を許可しません。 引用しているセクションでは、折り畳みについて説明しています。折り畳みは、展開された値から改行を削除する必要があります。 この:
"""foo
bar"""
次のようなものに展開されるはずです:
"foo bar"
それとは別に、WerkzeugはこのレベルでHTTPを解析しません。これは、WSGIサーバーの仕事です。 リクエストを解析するときに改行を拒否する唯一の理由は、セキュリティの問題をキャッチすることです。
このチケットは、nginxプロキシ転送クライアント証明書の背後にある開発モードでのFlaskの実際の動作によって動機付けられました。 その設定で、アプリケーションに渡されるヘッダーの改行を観察しました。 しかし、単体テストでこれを複製しようとすると、リクエストヘッダーを作成するときに上記のValueError
が得られました。
この問題についてもう少し調査したところ、次のことがわかりました。
HTTP仕様( RFC 2616 )は、ヘッダーの改行は単一のスペースに置き換えることができると述べていますが、そうなることが保証されているわけではありません(上記の引用を参照)。
WSGIが拡張するCGI仕様( RFC 3875 )では、要求ヘッダーの改行を置き換える必要があります。
同様に、複数行にまたがるヘッダーフィールドは、1行にマージする必要があります。
WSGI仕様( PEP 333 )では、応答ヘッダーの改行も禁止されています。
各
header_value
は、改行や改行などの制御文字を埋め込みまたは最後に含めることはできません。
つまり、ここには2つのバグがあるということです。
werkzeug開発サーバーは、リクエストヘッダーの折りたたまれた文字列を正しく正規化しません。
Headers
オブジェクトは、ヘッダー値の改行の受け入れに関して一貫性がありません。 改行はコンストラクターで受け入れられますが、 add()
メソッドでは受け入れられません。 BaseResponse
WSGI応答を実際に構築するときは、 Headers
オブジェクトが許容範囲を維持し、検証を実行する方がおそらく良いでしょう。 そうすれば、コンストラクターで改行を禁止しても、おそらく不適合なWSGIサーバー(werkzeug自身の開発サーバーなど)との互換性が損なわれることはありません。
けっこうだ。 これに関係する#1070もあります。
#1070の修正でこれが修正されたことは間違いありません。 そうでない場合は、再現可能な例でお知らせください。
@davidism前のコメントで述べたように、ここには実際には2つのバグがあり、どちらも現在のマスターブランチでは修正されていません。
最初のバグは、werkzeug開発サーバーが行でラップされたヘッダーを処理する方法に関係しています。 これは、 X-Example
ヘッダーの値を出力する次のサーバーコードで再現できます。
from werkzeug.serving import run_simple
from werkzeug.wrappers import Request, Response
def app(environ, start_response):
request = Request(environ)
print(repr(request.headers.get('X-Example')))
response = Response(status=204)
return response(environ, start_response)
run_simple('localhost', 8080, app)
次に、複数行にまたがるヘッダーを使用してリクエストを送信できます。
GET / HTTP/1.1
Host: localhost:8080
Connection: close
X-Example: foo
bar
予想されるサーバー出力:
ヘッダー値は1行にマージされます
'foo bar'
実際のサーバー出力(Python 2):
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 57361)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 649, in __init__
self.handle()
File "/home/.../venv/local/lib/python2.7/site-packages/werkzeug/serving.py", line 320, in handle
rv = BaseHTTPRequestHandler.handle(self)
File "/usr/lib/python2.7/BaseHTTPServer.py", line 340, in handle
self.handle_one_request()
File "/home/.../venv/local/lib/python2.7/site-packages/werkzeug/serving.py", line 355, in handle_one_request
return self.run_wsgi()
File "/home/.../venv/local/lib/python2.7/site-packages/werkzeug/serving.py", line 238, in run_wsgi
self.environ = environ = self.make_environ()
File "/home/.../venv/local/lib/python2.7/site-packages/werkzeug/serving.py", line 217, in make_environ
for key, value in self.get_header_items():
File "/home/.../venv/local/lib/python2.7/site-packages/werkzeug/serving.py", line 441, in get_header_items
key, value = header[0:-2].split(":", 1)
ValueError: need more than 1 value to unpack
----------------------------------------
実際のサーバー出力(Python 3):
ヘッダー値に、WSGI仕様で許可されていない改行文字が含まれています
'foo\r\n bar'
2番目のバグは、 Headers
オブジェクトが改行値を処理する方法に関係しています。
>>> from werkzeug import Headers
>>> h1 = Headers([('X-Example', 'foo\r\n bar')])
>>> h2 = Headers()
>>> h2.add('X-Example', 'foo\r\n bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/.../venv3/lib/python3.6/site-packages/werkzeug/datastructures.py", line 1166, in add
self._validate_value(_value)
File "/home/.../venv3/lib/python3.6/site-packages/werkzeug/datastructures.py", line 1173, in _validate_value
raise ValueError('Detected newline in header value. This is '
ValueError: Detected newline in header value. This is a potential security problem
期待される結果:
ValueError
あろうと成功であろうと、両方の操作で同じ結果が得られるはずです。
実結果:
Headers
コンストラクターは改行を許可し、 Headers.add()
はValueError
ます。
最近、私たちのプロジェクトの1つ(Python 2.7とFlask)でValueError
されましたが、これはnginxプロキシの背後にあります。 最終的に0.14.1
戻り( Flask
バンドルされているバージョンをオーバーライドします)、エラーはなくなりました。 0.15.x
ブランチがこの問題を引き起こした(またはリクエストヘッダーの処理方法に新しい問題を引き起こした可能性がある)ため、追加したいと思いました。
- - - 更新 - - - -:
渡したヘッダーを追跡しました。そのうちの1つは、pem形式の複数行の証明書でした。
SSL_CLIENT_CERT: -----BEGIN CERTIFICATE-----
MIIFHzCCAwegAwIBAgICEDgwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCdXMx
GDAWBgNVBAoMD3Uucy4gZ292ZXJubWVudDEPMA0GA1UECwwGcGVvcGxlMQwwCgYD
VQQLDANkYWUxEDAOBgNVBAsMB2NoaW1lcmExEDAOBgNVBAMMB0ludGVyQ0EwHhcN
MTcwODMxMTUwMzEwWhcNMjcwODI5MTUwMzEwWjBwMQswCQYDVQQGEwJVUzEYMBYG
A1UECgwPVS5TLiBHb3Zlcm5tZW50MRAwDgYDVQQLDAdjaGltZXJhMQwwCgYDVQQL
....
-----END CERTIFICATE-----
私たちのnginxサーバーは次のように構成されました:
proxy_set_header SSL_CLIENT_CERT $ssl_client_cert;
私たちは、おそらく使用する必要があります$ssl_client_escaped_cert
の代わりに$ssl_client_cert
(これはとにかく推奨されませんので)。 ただし、その変更によってヘッダー解析の問題が修正されるかどうかはわかりません。
これがこの問題に直面している他の人に役立つことを願っています。 現時点では、Python 2.7では0.15.1
がPEM証明書のような複数行のヘッダーを適切に処理していないようです。
これも、開発サーバーのヘッダー処理が原因で発生する2.7の問題です。 ヘッダーの折りたたみを処理する機能を、リクエストヘッダーの2.7互換性コードに追加しています。
開発サーバーのバグがPython2と3の両方で修正されたことを確認しました。 Headers
オブジェクトに関する2番目の問題は引き続き存在します。 #1608を開きました。
最も参考になるコメント
@davidism前のコメントで述べたように、ここには実際には2つのバグがあり、どちらも現在のマスターブランチでは修正されていません。
最初のバグは、werkzeug開発サーバーが行でラップされたヘッダーを処理する方法に関係しています。 これは、
X-Example
ヘッダーの値を出力する次のサーバーコードで再現できます。次に、複数行にまたがるヘッダーを使用してリクエストを送信できます。
予想されるサーバー出力:
ヘッダー値は1行にマージされます
実際のサーバー出力(Python 2):
実際のサーバー出力(Python 3):
ヘッダー値に、WSGI仕様で許可されていない改行文字が含まれています
2番目のバグは、
Headers
オブジェクトが改行値を処理する方法に関係しています。期待される結果:
ValueError
あろうと成功であろうと、両方の操作で同じ結果が得られるはずです。実結果:
Headers
コンストラクターは改行を許可し、Headers.add()
はValueError
ます。