From 1e0bbe803ad557b095b4098ca84527775ad4f564 Mon Sep 17 00:00:00 2001 From: dennis Date: Wed, 20 Mar 2024 11:02:49 +0800 Subject: [PATCH] Implement size limits on HTTP header number, header size and body size --- conf/config.ini | 6 +++ src/Common/config.cpp | 3 ++ src/Common/config.h | 6 +++ src/Http/HttpRequestSplitter.cpp | 4 ++ src/Http/HttpRequestSplitter.h | 9 +++- src/Http/HttpSession.cpp | 72 ++++++++++++++++++++++++++++++++ src/Http/HttpSession.h | 14 ++++++- 7 files changed, 111 insertions(+), 3 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 8f566338..a470f1d5 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -225,6 +225,12 @@ charSet=utf-8 keepAliveSecond=30 #http请求体最大字节数,如果post的body太大,则不适合缓存body在内存 maxReqSize=40960 +#http header请求最大个数,大于0才校验 +maxReqHeaderNumber=0 +#http header请求最大字节数,大于0才校验 +maxReqHeaderSize=0 +#http body请求最大字节数,大于0才校验 +maxReqBodySize=0 #404网页内容,用户可以自定义404网页 #notFound=404 Not Found

您访问的资源不存在!


ZLMediaKit-4.0
#http服务器监听端口 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index a34ee84f..dcc0d54e 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -164,6 +164,9 @@ namespace Http { #define HTTP_FIELD "http." const string kSendBufSize = HTTP_FIELD "sendBufSize"; const string kMaxReqSize = HTTP_FIELD "maxReqSize"; +const string kMaxReqHeaderNumber = HTTP_FIELD "maxReqHeaderNumber"; +const string kMaxReqHeaderSize = HTTP_FIELD "maxReqHeaderSize"; +const string kMaxReqBodySize = HTTP_FIELD "maxReqBodySize"; const string kKeepAliveSecond = HTTP_FIELD "keepAliveSecond"; const string kCharSet = HTTP_FIELD "charSet"; const string kRootPath = HTTP_FIELD "rootPath"; diff --git a/src/Common/config.h b/src/Common/config.h index 98ab289c..57418160 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -254,6 +254,12 @@ namespace Http { extern const std::string kSendBufSize; // http 最大请求字节数 extern const std::string kMaxReqSize; +// header最大请求个数 +extern const std::string kMaxReqHeaderNumber; +// header最大请求字节数 +extern const std::string kMaxReqHeaderSize; +// body最大请求字节数 +extern const std::string kMaxReqBodySize; // http keep-alive秒数 extern const std::string kKeepAliveSecond; // http 字符编码 diff --git a/src/Http/HttpRequestSplitter.cpp b/src/Http/HttpRequestSplitter.cpp index 32ab476f..7804d1ac 100644 --- a/src/Http/HttpRequestSplitter.cpp +++ b/src/Http/HttpRequestSplitter.cpp @@ -65,6 +65,10 @@ void HttpRequestSplitter::input(const char *data,size_t len) { _content_len = onRecvHeader(header_ptr, header_size); } + if (_content_len == 0 && _remain_data_size > 0) { + onCheckHeader(ptr,_remain_data_size); + } + if(_remain_data_size <= 0){ //没有剩余数据,清空缓存 _remain_data.clear(); diff --git a/src/Http/HttpRequestSplitter.h b/src/Http/HttpRequestSplitter.h index c4345c9f..be337ac9 100644 --- a/src/Http/HttpRequestSplitter.h +++ b/src/Http/HttpRequestSplitter.h @@ -68,7 +68,7 @@ protected: * @param data content分片或全部数据 * @param len 数据长度 */ - virtual void onRecvContent(const char *data,size_t len) {}; + virtual void onRecvContent(const char *data, size_t len) {}; /** * 判断数据中是否有包尾 @@ -78,6 +78,13 @@ protected: */ virtual const char *onSearchPacketTail(const char *data, size_t len); + /** + * 判断请求头是否有效 + * @param data 请求头数据 + * @param len 请求头长度 + */ + virtual void onCheckHeader(const char *data, size_t len) {}; + /** * 设置content len */ diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 3c34a661..7d508b49 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -26,6 +26,9 @@ namespace mediakit { HttpSession::HttpSession(const Socket::Ptr &pSock) : Session(pSock) { //设置默认参数 setMaxReqSize(0); + setMaxReqHeaderNumber(0); + setMaxReqHeaderSize(0); + setMaxReqBodySize(0); setTimeoutSec(0); } @@ -67,6 +70,8 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { CHECK(_parser.url()[0] == '/'); _origin = _parser["Origin"]; + onCheckHeader(len, _parser.getHeader().size()); + urlDecode(_parser); auto &cmd = _parser.method(); auto it = s_func_map.find(cmd); @@ -140,11 +145,26 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) { } void HttpSession::onRecvContent(const char *data, size_t len) { + if (_max_req_body_size > 0 && len > _max_req_body_size) { + WarnL << "Http body size is too huge: " << len << " > " << _max_req_body_size + << ", please set " << Http::kMaxReqBodySize << " in config.ini file."; + reset(); + throw std::out_of_range("Http body size is too huge: " + to_string(len)); + } + if (_on_recv_body && !_on_recv_body(data, len)) { _on_recv_body = nullptr; } } +void HttpSession::onCheckHeader(const char *header, size_t len) { + size_t number = 0; + if (_max_req_header_number > 0) { + number = onSearchHeaderNumber(header, len); + } + onCheckHeader(len, number); +} + void HttpSession::onRecv(const Buffer::Ptr &pBuf) { _ticker.resetTime(); input(pBuf->data(), pBuf->size()); @@ -182,6 +202,30 @@ void HttpSession::setMaxReqSize(size_t max_req_size) { setMaxCacheSize(max_req_size); } +void HttpSession::setMaxReqHeaderNumber(size_t max_req_header_number) { + if (!max_req_header_number) { + GET_CONFIG(size_t, s_max_req_header_number, Http::kMaxReqHeaderNumber); + max_req_header_number = s_max_req_header_number; + } + _max_req_header_number = max_req_header_number; +} + +void HttpSession::setMaxReqHeaderSize(size_t max_req_header_size) { + if (!max_req_header_size) { + GET_CONFIG(size_t, s_max_req_header_size, Http::kMaxReqHeaderSize); + max_req_header_size = s_max_req_header_size; + } + _max_req_header_size = max_req_header_size; +} + +void HttpSession::setMaxReqBodySize(size_t max_req_body_size) { + if (!max_req_body_size) { + GET_CONFIG(size_t, s_max_req_body_size, Http::kMaxReqBodySize); + max_req_body_size = s_max_req_body_size; + } + _max_req_body_size = max_req_body_size; +} + void HttpSession::onManager() { if (_ticker.elapsedTime() > _keep_alive_sec * 1000) { //http超时 @@ -834,4 +878,32 @@ std::shared_ptr HttpSession::getSharedPtr() { return dynamic_pointer_cast(shared_from_this()); } +ssize_t HttpSession::onSearchHeaderNumber(const char *data, size_t len) { + ssize_t number = 0; + const char *ptr = data; + const char *pos = nullptr; + while (pos = strstr(ptr, "\r\n")) { + number++; + ptr = pos + 2; + } + return number; +} + +void HttpSession::onCheckHeader(size_t len, size_t number) +{ + if (_max_req_header_size > 0 && len > _max_req_header_size) { + WarnL << "Http header size is too huge: " << len << " > " << _max_req_header_size + << ", please set " << Http::kMaxReqHeaderSize << " in config.ini file."; + reset(); + throw std::out_of_range("http header size is invalid: " + to_string(len)); + + } + if (_max_req_header_number > 0 && number > _max_req_header_number) { + WarnL << "Http header number is too huge: " << len << " > " << _max_req_header_number + << ", please set " << Http::kMaxReqHeaderNumber << " in config.ini file."; + reset(); + throw std::out_of_range("http header size is invalid: " + to_string(len)); + } +} + } /* namespace mediakit */ diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 2bc1c353..a022707d 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -49,6 +49,9 @@ public: static std::string urlDecodeComponent(const std::string &str); void setTimeoutSec(size_t second); void setMaxReqSize(size_t max_req_size); + void setMaxReqHeaderNumber(size_t max_req_header_number); + void setMaxReqHeaderSize(size_t max_req_header_size); + void setMaxReqBodySize(size_t max_req_body_size); protected: //FlvMuxer override @@ -57,8 +60,9 @@ protected: std::shared_ptr getSharedPtr() override; //HttpRequestSplitter override - ssize_t onRecvHeader(const char *data,size_t len) override; - void onRecvContent(const char *data,size_t len) override; + ssize_t onRecvHeader(const char *data, size_t len) override; + void onRecvContent(const char *data, size_t len) override; + void onCheckHeader(const char *data, size_t len) override; /** * 重载之用于处理不定长度的content @@ -126,6 +130,9 @@ private: //设置socket标志 void setSocketFlags(); + ssize_t onSearchHeaderNumber(const char *data, size_t len); + void onCheckHeader(size_t len, size_t number); + protected: MediaInfo _media_info; @@ -136,6 +143,9 @@ private: size_t _keep_alive_sec = 0; //最大http请求字节大小 size_t _max_req_size = 0; + size_t _max_req_header_number = 0; + size_t _max_req_header_size = 0; + size_t _max_req_body_size = 0; //消耗的总流量 uint64_t _total_bytes_usage = 0; // http请求中的 Origin字段