Как сделать приложение для прослушивания TCP более безопасным?
Я ищу совет по написанию очень безопасного приложения C/C++, которое будет слушать порт TCP. Я хотел бы взглянуть на то, что другие пытаются сделать с моей машиной, но я бы предпочел не устанавливать сложное приложение honeypot, если я могу делать то, что мне нужно, с очень короткой программой. Поскольку моя цель проста и безопасна, я хочу убедиться, что справляюсь по крайней мере со следующим:
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;
}
1 ответ
2) Клиенты не должны перегружать сервер слишком большим количеством соединений...
Ваши лучшие варианты - позволить маршрутизатору ограничивать количество подключений, которые устанавливает IP-адрес, поскольку к тому времени, когда ваша программа получает информацию о подключении, системные ресурсы уже распределены.
1) Клиент не должен иметь возможность отправлять неограниченное количество данных,
Лучший вариант - иметь разумный максимум, который можно отправить, и просто отслеживать, сколько вы прочитали, и как только вы достигнете максимального предела, закройте соединение.
3) Клиент не должен иметь возможность держать соединение открытым бесконечно долго.
Для этого вы можете просто создать поток, который начнет обратный отсчет, поэтому, когда соединение было открыто в течение определенного периода времени, оно может истечь, и вы можете просто закрыть соединение.
Есть и другие, более сложные решения, но я думаю, что это будет хорошей отправной точкой.
Если вы хотите что-то сделать с (2), сохраните хеш-карту в памяти и просто увеличьте количество выполненных соединений, но вы также можете отслеживать время. Вы хотите, чтобы структура данных была очень быстрой, поэтому вы можете решить, нужно ли вам отключить соединение раньше.
Например, если у вас есть два long в хэш-карте, вы можете поместить время (в unixtime или тиках) первого соединения и увеличить его, а если вы получите 10 соединений менее чем за одну минуту, начните закрывать избыток до минута прошла Затем удалите этот элемент, если у него было достаточно продолжительное соединение, чтобы вы полагали, что он не атаковал вас.
Кроме того, убедитесь, что вы проверяете все параметры, передаваемые вам, и имеете разумные максимальные значения для любых строк.