0

我正在寻找关于编写一个非常安全的 C/C++ 应用程序来监听 tcp 端口的建议。我想看看其他人试图对我的机器做什么,但如果我可以用一个非常短的程序来做我需要的事情,我宁愿不安装一个精心制作的蜜罐应用程序。我的目标是简单和安全,我想确保我至少处理以下内容:
1)客户端不应该能够发送无限量的数据,
2)客户端不应该能够超载服务器有太多的连接和
3) 客户端不能无限期地保持连接打开。
由于我当前的应用程序很小且功能强大,因此该项目似乎不需要第三方库。但是,一个可以进一步简化应用程序的小型库会很有趣。另外,我希望 C++ 标准库可能有助于简化我的代码。

以下是我最初尝试的一些关键功能。此代码可在 Windows、OSX 和 Linux 上编译。

如何使应用程序更安全或更简单?

void SafeClose(SOCKET s)
{
    if (s == INVALID_SOCKET)
        return;
    shutdown(s, SHUT_RDWR);
    closesocket(s);
}

void ProcessConnection(SOCKET sock, sockaddr_in *sockAddr)
{
    time_t time1;
    char szTime[TIME_BUF_SIZE];
    char readBuf[READ_BUF_SIZE];

    time(&time1);
    strftime(szTime, TIME_BUF_SIZE-1, "%Y/%m/%d %H:%M:%S", localtime(&time1)); 
    printf("%s - %s\n", szTime, inet_ntoa(sockAddr->sin_addr));
    usleep(1000000);       // Wait 1 second for client to send something
    int actualReadCount = recv(sock, readBuf, READ_BUF_SIZE-1, 0); 

    if (actualReadCount < 0 || actualReadCount >= READ_BUF_SIZE){
        actualReadCount = 0;
        strcpy(readBuf, "(Nothing)");
    } else {
        CleanString(readBuf, actualReadCount); // Replace non-printable characters
        readBuf[actualReadCount] = 0;
    }

    printf("%s\n\n", readBuf);
    SafeClose(sock);
}


int main(int argc, char* argv[])
{
    int             port            = 80;
    char            cmd[CMD_BUF_SIZE]   = {0};
    sockaddr_in     newSockAddr;
    sockaddr_in     localSockAddr;

    if(argc < 2){
        printf("Usage: safelisten PORT\n\n");
        return error_code;
    }

    error_code++;
    port = atoi(argv[1]); 
    BuildCleanAsciiMap(); // Used to replace non-printable characters

    localSockAddr.sin_family        = AF_INET;              
    localSockAddr.sin_addr.s_addr   = INADDR_ANY;           
    localSockAddr.sin_port          = htons(port);          
    sizeNewSockAddr                 = sizeof newSockAddr;

    CHK( listenSocket = socket(AF_INET, SOCK_STREAM, 0));
    CHK( setsockopt(listenSocket, SOL_SOCKET,  SO_REUSEADDR, (char *)&on, sizeof(on)));
    CHK( bind(listenSocket, (sockaddr*)&localSockAddr, sizeof localSockAddr));
    CHK( listen(listenSocket, BACKLOG));
    CHK( SetNonBlocking(listenSocket));
    CHK( SetNonBlocking(0));        // Set STDIN to nonblocking for linux    
    printf ("Listening on port: %d\nEnter q to quit.\n\n", port);

    while(strcmp(cmd, "q")) // While the user has not entered q ...
    {
        newSocket = accept(listenSocket, (sockaddr*)&newSockAddr, &sizeNewSockAddr);
        ReadCmd(cmd, CMD_BUF_SIZE);

        if(newSocket == INVALID_SOCKET) { 
            // accept() would have blocked, thus we wait and try again
            usleep(10000);
            continue;   
        }

        // Set to nonblocking because we don't want the client to dictate how 
        // long we are connected.
        CHK( SetNonBlocking(newSocket)); 
        ProcessConnection(newSocket, &newSockAddr);
    }

    SafeClose(listenSocket);
    return 0;
}
4

1 回答 1

1

2)客户端不应该能够通过太多连接使服务器超载......

您最好的选择是让路由器限制 IP 地址建立的连接数,因为当您的程序获取连接信息时,系统资源已经分配。

1)客户端不应该能够发送无限量的数据,

最好的选择是有一些可以发送的合理最大值,并跟踪您已阅读的内容,一旦达到最大限制,请关闭连接。

3) 客户端不应无限期地保持连接打开。

为此,您只需创建一个将开始倒计时的线程,因此当连接打开特定时间量时,它可能会超时,您可以关闭连接。

还有其他更复杂的解决方案,但我认为这将是一个很好的起点。

如果您想对 (2) 做点什么,请在内存中保留一个 hashmap 并增加已建立的连接数,但您可能还想跟踪时间。您希望数据结构非常快,因此您可以决定是否需要提前断开连接。

例如,如果您在 hashmap 中有两个 long,那么您可以输入第一个连接的时间(以 unixtime 或 ticks 为单位)并递增,如果您在不到一分钟的时间内获得 10 个连接,则开始关闭多余的连接,直到一分钟过去了。如果该元素的连接时间足够长以至于您相信它没有攻击您,请删除该元素。

此外,确保您验证所有传递给您的参数,并为任何字符串设置合理的最大值。

于 2011-10-02T19:34:09.360 回答