Added Basic Auth support for MITMWeb interface

This commit is contained in:
Guillem Anguera 2016-02-11 00:14:38 +01:00
parent b0dc44428e
commit 5306523446
4 changed files with 75 additions and 4 deletions

View File

@ -760,6 +760,21 @@ def mitmweb():
action="store_true", dest="wdebug",
help="Turn on mitmweb debugging"
)
group.add_argument(
"--wsingleuser",
action="store", dest="wsingleuser", type=str,
metavar="USER",
help="""
Allows access to a a single user, specified in the form
username:password.
"""
)
group.add_argument(
"--whtpasswd",
action="store", dest="whtpasswd", type=str,
metavar="PATH",
help="Allow access to users specified in an Apache htpasswd file."
)
common_options(parser)
group = parser.add_argument_group(

View File

@ -125,6 +125,9 @@ def mitmweb(args=None): # pragma: no cover
web_options.wdebug = options.wdebug
web_options.wiface = options.wiface
web_options.wport = options.wport
web_options.wsingleuser = options.wsingleuser
web_options.whtpasswd = options.whtpasswd
web_options.process_web_options(parser)
server = get_server(web_options.no_server, proxy_config)

View File

@ -3,6 +3,8 @@ import collections
import tornado.ioloop
import tornado.httpserver
from netlib.http import authentication
from .. import controller, flow
from . import app
@ -113,6 +115,9 @@ class Options(object):
"wdebug",
"wport",
"wiface",
"wauthenticator",
"wsingleuser",
"whtpasswd",
]
def __init__(self, **kwargs):
@ -122,13 +127,30 @@ class Options(object):
if not hasattr(self, i):
setattr(self, i, None)
def process_web_options(self, parser):
if self.wsingleuser or self.whtpasswd:
if self.wsingleuser:
if len(self.wsingleuser.split(':')) != 2:
return parser.error(
"Invalid single-user specification. Please use the format username:password"
)
username, password = self.wsingleuser.split(':')
self.wauthenticator = authentication.PassManSingleUser(username, password)
elif self.whtpasswd:
try:
self.wauthenticator = authentication.PassManHtpasswd(self.whtpasswd)
except ValueError as v:
return parser.error(v.message)
else:
self.wauthenticator = None
class WebMaster(flow.FlowMaster):
def __init__(self, server, options):
self.options = options
super(WebMaster, self).__init__(server, WebState())
self.app = app.Application(self, self.options.wdebug)
self.app = app.Application(self, self.options.wdebug, self.options.wauthenticator)
if options.rfile:
try:
self.load_flows_file(options.rfile)

View File

@ -4,6 +4,7 @@ import tornado.web
import tornado.websocket
import logging
import json
import base64
from netlib.http import CONTENT_MISSING
from .. import version, filt
@ -40,7 +41,31 @@ class APIError(tornado.web.HTTPError):
pass
class RequestHandler(tornado.web.RequestHandler):
class BasicAuth(object):
def set_auth_headers(self):
self.set_status(401)
self.set_header('WWW-Authenticate', 'Basic realm=MITMWeb')
self._transforms = []
self.finish()
def initialize(self, **kwargs):
self.wauthenticator = kwargs.get("wauthenticator")
def prepare(self):
if self.wauthenticator:
auth_header = self.request.headers.get('Authorization')
if auth_header is None or not auth_header.startswith('Basic '):
self.set_auth_headers()
else:
self.auth_decoded = base64.decodestring(auth_header[6:])
self.username, self.password = self.auth_decoded.split(':', 2)
if not self.wauthenticator.test(self.username, self.password):
self.set_auth_headers()
raise APIError(401, "Invalid username or password.")
class RequestHandler(BasicAuth, tornado.web.RequestHandler):
def set_default_headers(self):
super(RequestHandler, self).set_default_headers()
@ -100,7 +125,7 @@ class FiltHelp(RequestHandler):
))
class WebSocketEventBroadcaster(tornado.websocket.WebSocketHandler):
class WebSocketEventBroadcaster(BasicAuth, tornado.websocket.WebSocketHandler):
# raise an error if inherited class doesn't specify its own instance.
connections = None
@ -284,8 +309,11 @@ class Settings(RequestHandler):
class Application(tornado.web.Application):
def __init__(self, master, debug):
def __init__(self, master, debug, wauthenticator):
self.master = master
self.additional_args = dict(
wauthenticator=wauthenticator,
)
handlers = [
(r"/", IndexHandler),
(r"/filter-help", FiltHelp),
@ -302,6 +330,9 @@ class Application(tornado.web.Application):
(r"/settings", Settings),
(r"/clear", ClearAll),
]
for i, handler in enumerate(handlers):
handlers[i] += (self.additional_args,)
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),