diff options
author | James Feist <james.feist@linux.intel.com> | 2020-03-28 02:28:59 +0300 |
---|---|---|
committer | James Feist <james.feist@linux.intel.com> | 2020-03-31 23:31:10 +0300 |
commit | f0af8594734a83a242d7a2af7accbd5ba45df1d9 (patch) | |
tree | 6c8bebc35e249f000a9d1ad6d431e1e87581b025 /http | |
parent | c6f4e01779afb7a6eb25be15003829b46f81ba4c (diff) | |
download | bmcweb-f0af8594734a83a242d7a2af7accbd5ba45df1d9.tar.xz |
Protect against slow read attack
Right now as long as an attacker continutes to
do a slow read, the connection will stay open forever.
Set a timeout so this can't happen.
Tested: Used slowhttptest to verify this wouldn't
happen
Change-Id: I4dbe2a18f9ccce0ba36875572ec3df6bf3be6a1e
Signed-off-by: James Feist <james.feist@linux.intel.com>
Diffstat (limited to 'http')
-rw-r--r-- | http/http_connection.h | 51 | ||||
-rw-r--r-- | http/timer_queue.h | 6 |
2 files changed, 33 insertions, 24 deletions
diff --git a/http/http_connection.h b/http/http_connection.h index 4cd1e89e28..9f6c7b6676 100644 --- a/http/http_connection.h +++ b/http/http_connection.h @@ -831,34 +831,39 @@ class Connection : public std::enable_shared_from_this< } } - void startDeadline() + void startDeadline(size_t timerIterations = 0) { + // drop all connections after 1 minute, this time limit was chosen + // arbitrarily and can be adjusted later if needed + constexpr const size_t maxReadAttempts = + (60 / detail::timerQueueTimeoutSeconds); + cancelDeadlineTimer(); - timerCancelKey = - timerQueue.add([this, self(shared_from_this()), - readCount{parser->get().body().size()}] { - // Mark timer as not active to avoid canceling it during - // Connection destructor which leads to double free issue - timerCancelKey.reset(); - if (!isAlive()) - { - return; - } + timerCancelKey = timerQueue.add([this, self(shared_from_this()), + readCount{parser->get().body().size()}, + timerIterations{timerIterations + 1}] { + // Mark timer as not active to avoid canceling it during + // Connection destructor which leads to double free issue + timerCancelKey.reset(); + if (!isAlive()) + { + return; + } - // Restart timer if read is in progress. - // With threshold can be used to drop slow connections - // to protect against slow-rate DoS attack - if (parser->get().body().size() > readCount) - { - BMCWEB_LOG_DEBUG << this - << " restart timer - read in progress"; - startDeadline(); - return; - } + // Restart timer if read is in progress. + // With threshold can be used to drop slow connections + // to protect against slow-rate DoS attack + if ((parser->get().body().size() > readCount) && + (timerIterations < maxReadAttempts)) + { + BMCWEB_LOG_DEBUG << this << " restart timer - read in progress"; + startDeadline(timerIterations); + return; + } - close(); - }); + close(); + }); BMCWEB_LOG_DEBUG << this << " timer added: " << &timerQueue << ' ' << *timerCancelKey; } diff --git a/http/timer_queue.h b/http/timer_queue.h index 7339d66aea..d7a427ea87 100644 --- a/http/timer_queue.h +++ b/http/timer_queue.h @@ -11,6 +11,9 @@ namespace crow { namespace detail { + +constexpr const size_t timerQueueTimeoutSeconds = 5; + // fast timer queue for fixed tick value. class TimerQueue { @@ -49,7 +52,8 @@ class TimerQueue // remove canceled ones immediately if (x.second) { - if (now - x.first < std::chrono::seconds(5)) + if (now - x.first < + std::chrono::seconds(timerQueueTimeoutSeconds)) { break; } |