Skip to content

Commit

Permalink
Limit maximum request size.
Browse files Browse the repository at this point in the history
Request size shouldn't be unbounded because there is no need for it and
it can lead to DoS in some cases.
  • Loading branch information
Denis Kasak committed Apr 6, 2021
1 parent c951f17 commit 89071a1
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
23 changes: 20 additions & 3 deletions sydent/http/httpcommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@
from twisted.web._newclient import ResponseDone
from twisted.web.http import PotentialDataLoss
from twisted.web.iweb import UNKNOWN_LENGTH
from twisted.web import server


logger = logging.getLogger(__name__)

# Arbitrarily limited to 512 KiB.
MAX_REQUEST_SIZE = 512 * 1024


class SslComponents:
def __init__(self, sydent):
self.sydent = sydent
Expand Down Expand Up @@ -61,7 +67,7 @@ def makeTrustRoot(self):
fp = open(caCertFilename)
caCert = twisted.internet.ssl.Certificate.loadPEM(fp.read())
fp.close()
except:
except Exception:
logger.warn("Failed to open CA cert file %s", caCertFilename)
raise
logger.warn("Using custom CA cert file: %s", caCertFilename)
Expand All @@ -70,7 +76,6 @@ def makeTrustRoot(self):
return twisted.internet.ssl.OpenSSLDefaultPaths()



class BodyExceededMaxSize(Exception):
"""The maximum allowed size of the HTTP body was exceeded."""

Expand Down Expand Up @@ -123,7 +128,7 @@ def dataReceived(self, data) -> None:
# discarded anyway.
self.transport.abortConnection()

def connectionLost(self, reason = connectionDone) -> None:
def connectionLost(self, reason=connectionDone) -> None:
# If the maximum size was already exceeded, there's nothing to do.
if self.deferred.called:
return
Expand Down Expand Up @@ -163,3 +168,15 @@ def read_body_with_max_size(response, max_size):

response.deliverBody(_ReadBodyWithMaxSizeProtocol(d, max_size))
return d


class SizeLimitingRequest(server.Request):
def handleContentChunk(self, data):
if self.content.tell() + len(data) > MAX_REQUEST_SIZE:
logger.info(
"Aborting connection from %s because the request exceeds maximum size",
self.client.host)
self.transport.abortConnection()
return

return super().handleContentChunk(data)
2 changes: 2 additions & 0 deletions sydent/http/httpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from sydent.http.servlets.authenticated_unbind_threepid_servlet import (
AuthenticatedUnbindThreePidServlet,
)
from sydent.http.httpcommon import SizeLimitingRequest

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -129,6 +130,7 @@ def __init__(self, sydent):
v2.putChild(b'hash_details', self.sydent.servlets.hash_details)

self.factory = Site(root)
self.factory.requestFactory = SizeLimitingRequest
self.factory.displayTracebacks = False

def setup(self):
Expand Down

0 comments on commit 89071a1

Please sign in to comment.