Предотвращение атаки через каталог в приложении Poco Server - PullRequest
0 голосов
/ 16 февраля 2019

У меня есть проект приложения сервера Poco.Это серверное приложение работает как веб-сервер.Теперь я хочу защитить его от атак через каталоги и ищу лучший способ защитить файлы, которые обслуживает сервер, изнутри wwwRoot.Структура

project
|-src
| |-main.cpp
|-CMakeLists.txt
|-conanfile.txt
|-build
  |-...

Например, когда я использую build, поскольку wwwRoot, conanfile.txt находится за пределами wwwRoot, но с помощью localhost:8080/../conanfile.txt я могу открыть его.Я знаю, что могу искать путь по точечным сегментам, но я слышал, что это не совсем безопасно, поскольку существуют такие хаки, как закодированные пути.Поскольку Poco - это фреймворк для серверных приложений, я предполагаю, что уже есть такая функция для проверки того, находится ли filePath внутри wwwRoot, но я не могу ее найти.

src/main.cpp содержит:

#include <Poco/Net/HTTPServer.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Util/ServerApplication.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Path.h>
#include <string>

class FileHandler: public Poco::Net::HTTPRequestHandler {
public:
    explicit FileHandler(const Poco::Path &wwwRoot);
protected:
    void handleRequest(Poco::Net::HTTPServerRequest &request,
                       Poco::Net::HTTPServerResponse &response) override;
private:
    Poco::Path wwwRoot;
};

FileHandler::FileHandler(const Poco::Path &aWwwRoot) : wwwRoot(aWwwRoot) {}

void FileHandler::handleRequest(Poco::Net::HTTPServerRequest &request,
                   Poco::Net::HTTPServerResponse &response) {
    Poco::Util::Application &app = Poco::Util::Application::instance();
    app.logger().information("FileHandler Request from " + request.clientAddress().toString() + ": " + request.getURI());


    Poco::Path path(wwwRoot.absolute(), request.getURI());
    std::string filePath(path.toString());
    app.logger().information(filePath);
    response.sendFile(filePath, "text/html");
}

class HTTPRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory {
public:
    explicit HTTPRequestHandlerFactory(const Poco::Path &wwwRoot);
protected:
    Poco::Net::HTTPRequestHandler* createRequestHandler(
            const Poco::Net::HTTPServerRequest& request) override;
private:
    Poco::Path wwwRoot;
};

HTTPRequestHandlerFactory::HTTPRequestHandlerFactory(const Poco::Path &aWwwRoot) : wwwRoot(aWwwRoot) {}

Poco::Net::HTTPRequestHandler* HTTPRequestHandlerFactory::createRequestHandler(
        const Poco::Net::HTTPServerRequest &) {
    return new FileHandler(wwwRoot);
}

class ServerApplication : public Poco::Util::ServerApplication {
protected:
    void initialize(Application& self) override;
    int main(const std::vector<std::string> &args) override;
};

void ServerApplication::initialize(Application& self) {
    loadConfiguration();
    Poco::Util::ServerApplication::initialize(self);
}

int ServerApplication::main(const std::vector<std::string> &) {

    Poco::Net::ServerSocket svs(8080);
    Poco::Net::HTTPServer srv(new HTTPRequestHandlerFactory(Poco::Path(".").absolute()),
                              svs, new Poco::Net::HTTPServerParams);
    srv.start();
    waitForTerminationRequest();
    srv.stop();
    return Application::EXIT_OK;
}

int main(int argc, char **argv) {
  ServerApplication app;
  return app.run(argc, argv);
}

CMakeLists.txt содержит:

cmake_minimum_required (VERSION 3.5.1)
project (Sandbox)

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Debug)
endif()

set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_CXX_STANDARD 14)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)

add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE CONAN_PKG::Poco)

conanfile.txt содержит:

[requires]
Poco/1.9.0@pocoproject/stable

[generators]
cmake

В build проект построен

1 Ответ

0 голосов
/ 16 февраля 2019

Вам нужно будет декодировать путь запроса и шаг за шагом строить локальный путь и проверять сегменты пути "..".Вот фрагмент, который делает это, а также обрабатывает некоторые другие вещи, которые вы должны позаботиться при обслуживании файлов.Вам нужно написать метод mapContentType() или сделать что-то эквивалентное.

Poco::URI uri(request.getURI());
std::string decodedPath = uri.getPath();

Poco::Path requestPath(decodedPath, Poco::Path::PATH_UNIX);
Poco::Path localPath(wwwRoot.absolute());
localPath.makeDirectory();

bool valid = true;
for (int i = 0; valid && i < requestPath.depth(); i++)
{
    if (requestPath[i] != "..")
        localPath.pushDirectory(requestPath[i]);
    else
        valid = false;
}
if (valid)
{
    localPath.setFileName(requestPath.getFileName());
    Poco::File requestedFile(localPath.toString());
    if (requestedFile.exists())
    {
        std::string contentType = mapContentType(localPath.getExtension());

        if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD)
        {
            response.set("Last-Modified", Poco::DateTimeFormatter::format(dateTime, Poco::DateTimeFormat::HTTP_FORMAT));
            response.setContentLength64(requestedFile.getSize());
            response.setContentType(contentType);
            response.send();
        }
        else if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
        {
            response.sendFile(localPath.toString(), contentType);
        }
        else
        {
            response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_METHOD_NOT_ALLOWED);
            response.send();
        }
    }
    else
    {
        response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
        response.send();
    }
}
else
{
    response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
    response.send();
}
...