如何使我的 TCP 监听应用程序更安全?
我正在寻求有关编写一个非常安全的 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;
}
I’m looking for advice on writing a very safe C/C++ app that will listen to a tcp port. I'd like to peek at what others are trying to do to my machine, but I’d rather not install an elaborate honeypot app if I can do what I need with a very short program. With my goal being simple and safe, I want to make sure I handle at least the following:
1) The client should not be able to send an unlimited amount of data,
2) The client(s) should not be able to overload the server with too many connections and
3) The client should not be able to keep a connection open indefinitely.
Because my current app is small and functional, a third party library doesn't seem warranted for this project. However, a small library that would simplify the app further would be interesting. Also, I expect the C++ standard library may help in simplifying my code.
Below are some key functions from my initial attempt. This code compiles on Windows, OSX and Linux.
How can I make the app safer, or simpler?
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您最好的选择是让路由器限制 IP 地址建立的连接数量,因为当您的程序获取连接信息时,系统资源已经分配。
最好的选择是有一些合理的可以发送的最大值,并且只跟踪您已读取的数据量,一旦达到最大限制,就关闭连接。
为此,您可以创建一个将启动倒计时的线程,因此当连接打开特定时间后,它可能会超时,您可以关闭连接。
还有其他更复杂的解决方案,但我认为这将是一个很好的起点。
如果您想对 (2) 执行某些操作,请在内存中保留一个哈希图,并仅增加已建立的连接数,但您可能还想跟踪时间。您希望数据结构非常快,因此您可以决定是否需要提前断开连接。
例如,如果哈希图中有两个长整型,那么您可以输入第一个连接的时间(以 unixtime 或刻度为单位),然后递增,如果在一分钟内获得 10 个连接,则开始关闭多余的连接,直到一分钟过去了。如果该元素的连接时间足够长,您相信它不会攻击您,则删除该元素。
另外,请确保验证传递给您的所有参数,并为任何字符串设置合理的最大值。
Your best options is letting the router limit how many connections an IP address makes, as, by the time your program gets the connection info there are system resources already allocated.
The best option is to have some reasonable max that can be sent, and just keep track of how much you have read, and once you reach the max limit, close the connection.
For this, you can just create a thread that will start a countdown, so when a connection has been open for a specific amount of time, it can timeout and you can just close the connection.
There are other, more complicated, solutions, but I think this would be a good starting point.
If you want to do something about (2), keep a hashmap in memory and just increment how many connections have been made, but you may also want to keep track of time. You want the data structure to be very fast, so you can decide if you need to disconnect the connection early.
For example, if you had two longs in the hashmap, then you could put the time (in unixtime or ticks) of the first connection, and increment, and if you get 10 connections in less than one minute, start to close the excess until a minute has passed. Then remove that element if it has had connections long enough that you trust it was not attacking you.
Also, make certain you validate all the parameters being passed to you, and have reasonable max values for any strings.