Skip to content

Commit 5592d31

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Reject open redirection in the console proxy"
2 parents 6b5fa14 + 781612b commit 5592d31

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

nova/console/websocketproxy.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import copy
2222
from http import cookies as Cookie
23+
from http import HTTPStatus
24+
import os
2325
import socket
2426
from urllib import parse as urlparse
2527

@@ -271,6 +273,27 @@ def new_websocket_client(self):
271273
def socket(self, *args, **kwargs):
272274
return websockifyserver.WebSockifyServer.socket(*args, **kwargs)
273275

276+
def send_head(self):
277+
# This code is copied from this example patch:
278+
# https://bugs.python.org/issue32084#msg306545
279+
path = self.translate_path(self.path)
280+
if os.path.isdir(path):
281+
parts = urlparse.urlsplit(self.path)
282+
if not parts.path.endswith('/'):
283+
# redirect browser - doing basically what apache does
284+
new_parts = (parts[0], parts[1], parts[2] + '/',
285+
parts[3], parts[4])
286+
new_url = urlparse.urlunsplit(new_parts)
287+
288+
# Browsers interpret "Location: //uri" as an absolute URI
289+
# like "http://URI"
290+
if new_url.startswith('//'):
291+
self.send_error(HTTPStatus.BAD_REQUEST,
292+
"URI must not start with //")
293+
return None
294+
295+
return super(NovaProxyRequestHandler, self).send_head()
296+
274297

275298
class NovaWebSocketProxy(websockify.WebSocketProxy):
276299
def __init__(self, *args, **kwargs):

nova/tests/unit/console/test_websocketproxy.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,40 @@ def test_malformed_cookie(self, validate, check_port):
588588
self.wh.socket.assert_called_with('node1', 10000, connect=True)
589589
self.wh.do_proxy.assert_called_with('<socket>')
590590

591+
def test_reject_open_redirect(self):
592+
# This will test the behavior when an attempt is made to cause an open
593+
# redirect. It should be rejected.
594+
mock_req = mock.MagicMock()
595+
mock_req.makefile().readline.side_effect = [
596+
b'GET //example.com/%2F.. HTTP/1.1\r\n',
597+
b''
598+
]
599+
600+
# Collect the response data to verify at the end. The
601+
# SimpleHTTPRequestHandler writes the response data by calling the
602+
# request socket sendall() method.
603+
self.data = b''
604+
605+
def fake_sendall(data):
606+
self.data += data
607+
608+
mock_req.sendall.side_effect = fake_sendall
609+
610+
client_addr = ('8.8.8.8', 54321)
611+
mock_server = mock.MagicMock()
612+
# This specifies that the server will be able to handle requests other
613+
# than only websockets.
614+
mock_server.only_upgrade = False
615+
616+
# Constructing a handler will process the mock_req request passed in.
617+
websocketproxy.NovaProxyRequestHandler(
618+
mock_req, client_addr, mock_server)
619+
620+
# Verify no redirect happens and instead a 400 Bad Request is returned.
621+
self.data = self.data.decode()
622+
self.assertIn('Error code: 400', self.data)
623+
self.assertIn('Message: URI must not start with //', self.data)
624+
591625
@mock.patch('nova.objects.ConsoleAuthToken.validate')
592626
def test_no_compute_rpcapi_with_invalid_token(self, mock_validate):
593627
"""Tests that we don't create a ComputeAPI object until we actually
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
security:
3+
- |
4+
A vulnerability in the console proxies (novnc, serial, spice) that allowed
5+
open redirection has been `patched`_. The novnc, serial, and spice console
6+
proxies are implemented as websockify servers and the request handler
7+
inherits from the python standard SimpleHTTPRequestHandler. There is a
8+
`known issue`_ in the SimpleHTTPRequestHandler which allows open redirects
9+
by way of URLs in the following format::
10+
11+
http://vncproxy.my.domain.com//example.com/%2F..
12+
13+
which if visited, will redirect a user to example.com.
14+
15+
The novnc, serial, and spice console proxies will now reject requests that
16+
pass a redirection URL beginning with "//" with a 400 Bad Request.
17+
18+
.. _patched: https://bugs.launchpad.net/nova/+bug/1927677
19+
.. _known issue: https://bugs.python.org/issue32084

0 commit comments

Comments
 (0)