mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-14 08:49:47 -05:00
Add an error 401 handler, and make it start counting invalid password guesses instead of 404 errors for rate limiting
This commit is contained in:
parent
2a50bbc3bc
commit
79b87c3e30
@ -44,6 +44,7 @@ class Web(object):
|
||||
REQUEST_UPLOAD_FINISHED = 8
|
||||
REQUEST_UPLOAD_CANCELED = 9
|
||||
REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 10
|
||||
REQUEST_INVALID_SLUG = 11
|
||||
|
||||
def __init__(self, common, is_gui, mode='share'):
|
||||
self.common = common
|
||||
@ -55,6 +56,7 @@ class Web(object):
|
||||
template_folder=self.common.get_resource_path('templates'))
|
||||
self.app.secret_key = self.common.random_string(8)
|
||||
self.auth = HTTPBasicAuth()
|
||||
self.auth.error_handler(self.error401)
|
||||
|
||||
# Verbose mode?
|
||||
if self.common.verbose:
|
||||
@ -95,7 +97,8 @@ class Web(object):
|
||||
|
||||
self.q = queue.Queue()
|
||||
self.slug = None
|
||||
self.error404_count = 0
|
||||
|
||||
self.reset_invalid_slugs()
|
||||
|
||||
self.done = False
|
||||
|
||||
@ -141,10 +144,7 @@ class Web(object):
|
||||
return _check_login()
|
||||
|
||||
@self.app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
"""
|
||||
404 error page.
|
||||
"""
|
||||
def not_found(e):
|
||||
return self.error404()
|
||||
|
||||
@self.app.route("/<slug_candidate>/shutdown")
|
||||
@ -164,18 +164,26 @@ class Web(object):
|
||||
r = make_response(render_template('receive_noscript_xss.html'))
|
||||
return self.add_security_headers(r)
|
||||
|
||||
def error401(self):
|
||||
auth = request.authorization
|
||||
if auth:
|
||||
if auth['username'] == 'onionshare' and auth['password'] not in self.invalid_slugs:
|
||||
print('Invalid password guess: {}'.format(auth['password']))
|
||||
self.add_request(Web.REQUEST_INVALID_SLUG, data=auth['password'])
|
||||
|
||||
self.invalid_slugs.append(auth['password'])
|
||||
self.invalid_slugs_count += 1
|
||||
|
||||
if self.invalid_slugs_count == 20:
|
||||
self.add_request(Web.REQUEST_RATE_LIMIT)
|
||||
self.force_shutdown()
|
||||
print("Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.")
|
||||
|
||||
r = make_response(render_template('401.html'), 401)
|
||||
return self.add_security_headers(r)
|
||||
|
||||
def error404(self):
|
||||
self.add_request(Web.REQUEST_OTHER, request.path)
|
||||
if request.path != '/favicon.ico':
|
||||
self.error404_count += 1
|
||||
|
||||
# In receive mode, with public mode enabled, skip rate limiting 404s
|
||||
if not self.common.settings.get('public_mode'):
|
||||
if self.error404_count == 20:
|
||||
self.add_request(Web.REQUEST_RATE_LIMIT, request.path)
|
||||
self.force_shutdown()
|
||||
print("Someone has made too many wrong attempts on your address, which means they could be trying to guess it, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.")
|
||||
|
||||
r = make_response(render_template('404.html'), 404)
|
||||
return self.add_security_headers(r)
|
||||
|
||||
@ -198,7 +206,7 @@ class Web(object):
|
||||
return True
|
||||
return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
|
||||
|
||||
def add_request(self, request_type, path, data=None):
|
||||
def add_request(self, request_type, path=None, data=None):
|
||||
"""
|
||||
Add a request to the queue, to communicate with the GUI.
|
||||
"""
|
||||
@ -226,18 +234,15 @@ class Web(object):
|
||||
log_handler.setLevel(logging.WARNING)
|
||||
self.app.logger.addHandler(log_handler)
|
||||
|
||||
def check_slug_candidate(self, slug_candidate):
|
||||
self.common.log('Web', 'check_slug_candidate: slug_candidate={}'.format(slug_candidate))
|
||||
if self.common.settings.get('public_mode'):
|
||||
abort(404)
|
||||
if not hmac.compare_digest(self.slug, slug_candidate):
|
||||
abort(404)
|
||||
|
||||
def check_shutdown_slug_candidate(self, slug_candidate):
|
||||
self.common.log('Web', 'check_shutdown_slug_candidate: slug_candidate={}'.format(slug_candidate))
|
||||
if not hmac.compare_digest(self.shutdown_slug, slug_candidate):
|
||||
abort(404)
|
||||
|
||||
def reset_invalid_slugs(self):
|
||||
self.invalid_slugs_count = 0
|
||||
self.invalid_slugs = []
|
||||
|
||||
def force_shutdown(self):
|
||||
"""
|
||||
Stop the flask web server, from the context of the flask app.
|
||||
|
@ -113,7 +113,7 @@ class ReceiveMode(Mode):
|
||||
"""
|
||||
# Reset web counters
|
||||
self.web.receive_mode.upload_count = 0
|
||||
self.web.error404_count = 0
|
||||
self.web.reset_invalid_slugs()
|
||||
|
||||
# Hide and reset the uploads if we have previously shared
|
||||
self.reset_info_counters()
|
||||
|
@ -147,7 +147,7 @@ class ShareMode(Mode):
|
||||
"""
|
||||
# Reset web counters
|
||||
self.web.share_mode.download_count = 0
|
||||
self.web.error404_count = 0
|
||||
self.web.reset_invalid_slugs()
|
||||
|
||||
# Hide and reset the downloads if we have previously shared
|
||||
self.reset_info_counters()
|
||||
|
@ -143,7 +143,7 @@ class WebsiteMode(Mode):
|
||||
"""
|
||||
# Reset web counters
|
||||
self.web.website_mode.visit_count = 0
|
||||
self.web.error404_count = 0
|
||||
self.web.reset_invalid_slugs()
|
||||
|
||||
# Hide and reset the downloads if we have previously shared
|
||||
self.reset_info_counters()
|
||||
|
@ -472,7 +472,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
||||
|
||||
if event["type"] == Web.REQUEST_OTHER:
|
||||
if event["path"] != '/favicon.ico' and event["path"] != "/{}/shutdown".format(mode.web.shutdown_slug):
|
||||
self.status_bar.showMessage('[#{0:d}] {1:s}: {2:s}'.format(mode.web.error404_count, strings._('other_page_loaded'), event["path"]))
|
||||
self.status_bar.showMessage('{0:s}: {1:s}'.format(strings._('other_page_loaded'), event["path"]))
|
||||
|
||||
if event["type"] == Web.REQUEST_INVALID_SLUG:
|
||||
self.status_bar.showMessage('[#{0:d}] {1:s}: {2:s}'.format(mode.web.invalid_slugs_count, strings._('invalid_slug_guess'), event["data"]))
|
||||
|
||||
mode.timer_callback()
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
"not_a_readable_file": "{0:s} is not a readable file.",
|
||||
"no_available_port": "Could not find an available port to start the onion service",
|
||||
"other_page_loaded": "Address loaded",
|
||||
"invalid_slug_guess": "Invalid password guess",
|
||||
"close_on_autostop_timer": "Stopped because auto-stop timer ran out",
|
||||
"closing_automatically": "Stopped because transfer is complete",
|
||||
"large_filesize": "Warning: Sending a large share could take hours",
|
||||
@ -34,7 +35,7 @@
|
||||
"gui_receive_quit_warning": "You're in the process of receiving files. Are you sure you want to quit OnionShare?",
|
||||
"gui_quit_warning_quit": "Quit",
|
||||
"gui_quit_warning_dont_quit": "Cancel",
|
||||
"error_rate_limit": "Someone has made too many wrong attempts on your address, which means they could be trying to guess it, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.",
|
||||
"error_rate_limit": "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.",
|
||||
"zip_progress_bar_format": "Compressing: %p%",
|
||||
"error_stealth_not_supported": "To use client authorization, you need at least both Tor 0.2.9.1-alpha (or Tor Browser 6.5) and python3-stem 1.5.0.",
|
||||
"error_ephemeral_not_supported": "OnionShare requires at least both Tor 0.2.7.1 and python3-stem 1.4.0.",
|
||||
|
19
share/templates/401.html
Normal file
19
share/templates/401.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>OnionShare: 401 Unauthorized Access</title>
|
||||
<link href="/static/img/favicon.ico" rel="icon" type="image/x-icon" />
|
||||
<link rel="stylesheet" rel="subresource" type="text/css" href="/static/css/style.css" media="all">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="info-wrapper">
|
||||
<div class="info">
|
||||
<p><img class="logo" src="/static/img/logo_large.png" title="OnionShare"></p>
|
||||
<p class="info-header">401 Unauthorized Access</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user