require Origin: header for password-based auth, and check Origin: header against session token

This commit is contained in:
Jude Nelson
2017-07-17 11:29:47 -04:00
parent 2183a12749
commit e79813f95a

View File

@@ -45,6 +45,7 @@ import jsontokens
import subprocess
import platform
import shutil
import urlparse
from jsonschema import ValidationError
from schemas import *
import client as bsk_client
@@ -62,7 +63,7 @@ import backend.drivers as backend_drivers
import proxy
from proxy import json_is_error, json_is_exception
from .constants import BLOCKSTACK_DEBUG, BLOCKSTACK_TEST, RPC_MAX_ZONEFILE_LEN, CONFIG_PATH, WALLET_FILENAME, TX_MIN_CONFIRMATIONS, DEFAULT_API_PORT, SERIES_VERSION, TX_MAX_FEE
from .constants import BLOCKSTACK_DEBUG, BLOCKSTACK_TEST, RPC_MAX_ZONEFILE_LEN, CONFIG_PATH, WALLET_FILENAME, TX_MIN_CONFIRMATIONS, DEFAULT_API_PORT, SERIES_VERSION, TX_MAX_FEE, DEFAULT_UI_PORT
from .method_parser import parse_methods
from .wallet import make_wallet
import app
@@ -285,6 +286,30 @@ class BlockstackAPIEndpointHandler(SimpleHTTPRequestHandler):
return ret
def verify_origin(self, allowed):
"""
Verify that the Origin: header is present and listed
in our list of allowed origins
Return True if so
Return False if not
"""
allowed_urlparse = [urlparse.urlparse(a) for a in allowed]
for a in allowed_urlparse:
assert a.netloc, "Invalid origin {}".format(a)
allowed_origins = dict([(a.netloc, a) for a in allowed_urlparse])
origin_header = self.headers.get('origin', None)
if origin_header is not None:
origin_info = urlparse.urlparse(origin_header)
if origin_info.netloc in allowed_origins.keys():
allowed_origin = allowed_origins[origin_info.netloc]
if origin_info.scheme == allowed_origin.scheme and origin_info.netloc == allowed_origin.netloc:
return True
return False
def verify_session(self, qs_values):
"""
Verify and return the application's session.
@@ -453,6 +478,7 @@ class BlockstackAPIEndpointHandler(SimpleHTTPRequestHandler):
if decode_err:
log.error('Current decode error:')
log.exception(decode_err)
log.error('Legacy decode error:')
log.exception(ve)
return self._reply_json({'error': 'Invalid authRequest token: does not match any known request schemas'}, status_code=401)
@@ -3490,7 +3516,14 @@ class BlockstackAPIEndpointHandler(SimpleHTTPRequestHandler):
},
},
}
LOCALHOST = [
'http://localhost:{}'.format(DEFAULT_UI_PORT),
'http://{}:{}'.format(socket.gethostname(), DEFAULT_UI_PORT),
'http://127.0.0.1:{}'.format(DEFAULT_UI_PORT),
'http://::1:{}'.format(DEFAULT_UI_PORT)
]
path_info = self.get_path_and_qs()
if 'error' in path_info:
self._send_headers(status_code=401, content_type='text/plain')
@@ -3514,12 +3547,27 @@ class BlockstackAPIEndpointHandler(SimpleHTTPRequestHandler):
use_session = whitelist_info['auth_session']
use_password = whitelist_info['auth_pass']
log.debug("\nfull path: {}\nmethod: {}\npath: {}\nqs: {}\nheaders:\n {}\n".format(self.path, method_name, path_info['path'], qs_values, '\n'.join( '{}: {}'.format(k, v) for (k, v) in self.headers.items() )))
log.debug("\nfull path: {}\nmethod: {}\npath: {}\nqs: {}\nheaders:\n{}\n".format(self.path, method_name, path_info['path'], qs_values, '\n'.join( '{}: {}'.format(k, v) for (k, v) in self.headers.items() )))
have_password = False
session = self.verify_session(qs_values)
if not session:
have_password = self.verify_password()
# password authentication
# don't even try to authenticate with the password unless the Origin is set appropriately
if self.verify_origin(LOCALHOST):
# can authenticate
have_password = self.verify_password()
else:
log.warning("Origin is absent or not local")
else:
# got a session.
# check origin.
app_domain = session['app_domain']
if not self.verify_origin([app_domain]):
# invalid session
log.warning("Invalid session: app domain '{}' does not match Origin '{}'".format(app_domain, self.headers.get('origin', '')))
session = None
authorized = False
@@ -3800,6 +3848,7 @@ class BlockstackAPIEndpointClient(object):
elif self.session:
headers['Authorization'] = 'bearer {}'.format(self.session)
headers['Origin'] = 'http://localhost:{}'.format(DEFAULT_UI_PORT)
return headers