| ksdafh 2006-05-18, 7:13 pm |
| Hi, I have to admit - I am using Borland C++ Builder and have posted this
message in the Borland group with no replies - Maybe one of you could have a
look at this quickly to give me some feedback...
Questions are quite short but the post is long due to code - please read on.
I have an ISAPI application that exposes some SOAP web-services. I want to
defnd against brute-force programatic attacks on the server such that: If a
large number of requests come in in less than a predefined period of time
from a single IP - to simply drop the requests and stop responding to that
IP (at least temporarily). While continuing to service requests from
others. I think I have a fair underastanding of the threading issues in
ISAPI apps and am prepared to handle them - but this problem is a bit of a
beast.
So far I have the folowing concept:
1) Declare a *global* threadlist:
TThreadList * pIncomingRequests = NULL;
2) When a web-module gets constructed ensure the global thread list is
created:
__fastcall TMyWM::TMyWM(TComponent* Owner)
: TWebModule(Owner)
{
if (! pIncomingRequests)
{
pIncomingRequests = new TThreadList();
}
}
3) Define a structure to hold info on requesters to be added to the global
thread list:
class TIncomingRequester : public TObject
{
public:
AnsiString FIP; // IP address
DWORD FNumRequests; // number of times request recieved from this IP
TDateTime FFirstRequestTime;
TDateTime FLastRequestTime;
__fastcall TIncomingRequester(const AnsiString AnIP, const TDateTime
ADateTime);
};
4) And some constants to define maximum requests per period:
const int MAXREQUESTSPERQUANTA = 100;
const int REQUESTQUANTA = 5; // 5minutes
5) Before a request gets dispatched - decide whether or not to bother
responding by checking the requester against the list:
void __fastcall TMyWM::WebModuleBeforeDispatch(TObject *Sender,
TWebRequest *Request, TWebResponse *Response, bool &Handled)
{
TList * lockedList = pIncomingRequests->LockList();
try
{
RemoveOldRequests(lockedList);
TIncomingRequester* pIR = NULL;
for (int i = 0; i < lockedList->Count; ++i)
{
pIR = (TIncomingRequester*)lockedList->Items[i];
if (pIR->FIP == Request->RemoteAddr)
{
break;
}
}
if (pIR)
{
pIR->FNumRequests++;
pIR->FLastRequestTime = Now();
}
else
{
pIR = new TIncomingRequester(Request->RemoteAddr, Now());
lockedList->Add(pIR);
}
Handled = IgnoreRequester(pIR);
}
__finally
{
pIncomingRequests->UnlockList();
}
}
//---------------------------------------------------------------------------
bool __fastcall TMyWM::IgnoreRequester(TIncomingRequeste
r * pIR)
{
if (!pIR)
return true;
else
return ( (pIR->FNumRequests > MAXREQUESTSPERQUANTA)
&& (MinutesBetween(pIR->FLastRequestTime,
pIR->FFirstRequestTime) < REQUESTQUANTA));
}
So far so good.... I think... First question is - do you see any problem
with what I have so far? How would you do it better?
One main thing I am concerned about is memory. I need to (at some point)
destroy the thread list. In the destructor I have:
__fastcall TMyWM::~TMyWM()
{
if (pIncomingRequests) // and i am the last web-module ! ???
{
TList * lockedList = pIncomingRequests->LockList();
for (int i = 0 ; i < lockedList->Count; ++i)
{
delete (TIncomingRequester*)lockedList->Items[i];
}
delete pIncomingRequests;
pIncomingRequests = NULL;
}
}
However - as you can see - I don't know how to determine if indeed *this*
web-module is the *last* web-module to be destroyed because the dll is being
unloaded. This is important because if a web-module is destroyed in an
ISAPI it does not necessarily mean that there are no other web-modules
(either inactive or active) in the thread pool - in fact there could be many
that are still working with the list. If the rest of my methodology is OK,
how do I solve this problem?
Thank You.
M.
|