How to delay a response in Classic ASP
Asked Answered
H

15

9

I have a site running Classic-ASP and on the login page I would like to delay the response to a failed login attempt (by like 10 seconds) to help prevent brute force attacks on accounts.

Quick google searches show some hacks using SQL server queries that seem hack-tastic.

Is there a good way to do this in classic asp?

Henequen answered 10/2, 2010 at 14:18 Comment(0)
S
8

I am not going to answer your specific question, as many have already done so, but there are far better ways of preventing brute force attacks.

For instance:

  1. Why not lock a specific session or IP address out after say 5 (being generous here) failed login attempts? You could lock it out for say 10 minutes. You could even write a "401 Unauthorized" HTTP status and then simply end the response with Response.End.
  2. In a similar fashion, but not even linked to failed logins, you could block requests for the login page more than X times in Y seconds for a specific IP, UserAgent and other client features - ensuring kind of a 'unique' client.
  3. Ignore IP address (it is easily spoofed and can be a proxy server IP), and simply detect the automation of the login attempt. X number of failed logins within Y seconds for a specific username/email address, block it for that username for a set period of time, and end the response.

Just saying there are other options than putting unnecessary load on your server by locking some resources and waiting.

Obviously, doing this at the hardware layer - firewalls etc. would be the preferred option.

Soviet answered 10/2, 2010 at 15:4 Comment(3)
There are certainly other approaches, the problem with IP based blocking is that often users at large corporations will appear to be coming from the same IP, so an IP based block might affect more people than desired. And source IP can simply be faked, rendering this method inert.Henequen
Yes, I am aware of that Bert, proxies and all that, and it can even be spoofed. However, brute force attacks tend to also use a specific username, for which they attempt logins, so X login attempts for user 'Admin' within Y seconds, just tell them to take a hike. Regardless of IP or anything. It's the repeated login attempts for the same username in a timespan that is clearly an automated web request, that identifies this. Modified my answer to reflect some of this.Soviet
+1, Good Answer, although I'd use strong terms to disuade the delay response solution. A brute force attempt using a fresh session each time can be performed with many parallel requests hence such an attack can effectively become an unintended DOS attack.Chiaki
P
4

There is another approach, but keep in mind the aforementioned caveats about unessecarily consumed resources. Here is an approach though

Sub DelayResponse(numberOfseconds)
 Dim WshShell
 Set WshShell=Server.CreateObject("WScript.Shell")
 WshShell.Run "waitfor /T " & numberOfSecond & "SignalThatWontHappen", , True
End Sub
Pacorro answered 10/2, 2010 at 14:30 Comment(1)
Won't wait on W10 Pro IISAsturias
L
4

There is the WScript.Sleep method for general purpose VBScript, however, this won't work in the context of ASP.

There are a number of other mechanisms you can use to achieve this, however, they're all effectively "workarounds" as there's no built-in way to cleanly cause an ASP page (running VBScript) to pause itself.

See here:

How do I make my ASP page pause or 'sleep'?

To specifically answer your question of:

Is there a good way to do this in classic asp?

No. There's no good way to do this, and there's only the "hack-tastic" hacks that can be used, however they bring with them all sorts of side-effects and caveats. (See the last part of the "How do I make my ASP page pause or 'sleep'?" link for a specific memory eating, page faulting nasty side-effect.)

Lampion answered 10/2, 2010 at 14:34 Comment(0)
F
4

You can use :

<html>
<head>
    <title>Sleep</title>
</head>
<body>
    <% 

        function Sleep(seconds)
            set oShell = CreateObject("Wscript.Shell")
            cmd = "%COMSPEC% /c timeout " & seconds & " /nobreak"
            oShell.Run cmd,0,1
        End function

        Sleep(5)

        response.write("End") 
    %>
</body>
</html>
Furrow answered 27/4, 2012 at 8:19 Comment(1)
THANK YOU!!! I was having an issue with Submitting a new record and having the DIV reload with the updated data, but it was not reloading with the new data. I knew there was an issue with the data and refresh being done simultaneously, which would cause it not to load the new data. Adding your script with a sleep(1) did the trick. THANK YOU!Hoodwink
T
2

There is no simple way to do so in pure ASP.
Either SQL WAITFOR, or create simple ActiveX component in VB (or anything) that sleeps.
Note that this will increase load on the server. Sleeping requests keep memory and connections consumed for nothing.

Thanasi answered 10/2, 2010 at 14:24 Comment(0)
C
1

Other approach to avoid brute force attacks without using IP restrictions is to offer a captcha after the second fail attempt for the same user. This is the way Google do it.

This is how Google does it

Curvaceous answered 10/2, 2010 at 20:57 Comment(0)
P
1
<%
 set shell = CreateObject("WScript.Shell")


 t1 = timer()
 sleep(5)
 t2 = timer()

 response.write "waited "& t2-t1 &" secs"


 function sleep(seconds)
    if seconds>=1 then shell.popup "pausing",seconds,"pause",64
 end function
%>
Pavlov answered 30/4, 2012 at 3:29 Comment(1)
Please learn how to format your code properly. Its kind of important here.Mirk
E
0

There is the Response.Buffer option that you can use to tell it to delay returning the response until the page has completed processing, so you could perhaps combine that with some kind of timeout in the script, but it would not be especially elegant especially as VBScript doesn't really offer you a way of asking threads to sleep so you can end up thrashing the CPU.

Maybe better to use a server-side session and javascript on the client, so the client delays the request and the server will only send the response after the expected delay is over. That should provide some server-side safeguards and be useable for users who aren't trying to mess around with your system...

Elnaelnar answered 10/2, 2010 at 14:27 Comment(0)
A
0

I'm using this:

function sleep(scs)
    Dim lo_wsh, ls_cmd
    Set lo_wsh = CreateObject( "WScript.Shell" )
    ls_cmd = "%COMSPEC% /c ping -n " & 1 + scs & " 127.0.0.1>nul"
    lo_wsh.Run ls_cmd, 0, True 
End Function

sleep(5) 'wait for 5 seconds

Bye :-)

Aubree answered 23/5, 2013 at 10:1 Comment(0)
O
0

For those using MySQL, you can do the following :

Sub SleepMysql(n)

    'Define Query
    Dim SqlStr : SqlStr = "DO SLEEP(" & n & ")"

    'Run Query
    Dim rsTemp : Set rsTemp = YourDatabaseConnection.Execute(SqlStr)

    'Release resources
    Set rsTemp = Nothing

End Sub 'SleepMysql
Oma answered 27/8, 2018 at 2:42 Comment(0)
B
0

Of all of the ideas - I liked Dercsár's answer - here's my implementation of a wait that I found works well. - requires your application to have access to a SQL server:

<%
' ASP Script to example to wait  
  
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open strConnString    ' your connection to sql server here...

Response.write("Waiting for 15 seconds...." & now() & "<BR>")  
Call subWaitTime("00:00:15")  
' Wait 15 seconds 
Response.write("ok - Done" & now() & "<BR>") 
conn.close 
set conn = nothing
    
    
 '---- Utility sub to wait 
 Sub subWaitTime(sTime)
    ' Call subWaitTime with number of Hours:Minutes:Seconds to delay
    sqlWaitTime = "WAITFOR DELAY " & fnSqlStr(sTime)
    conn.Execute(sqlWaitTime)      ' 
 End Sub 
%>
Blumenfeld answered 8/7, 2021 at 16:12 Comment(0)
D
0

I've been using Waitable Timer API in many VB applications. It puts the thread of your application to sleep for a period of time. The benefit of a Waitable timer instead of the Sleep API or simple loops or some other methods, is that your application will still be responsive to events, where Sleep will freeze your application for the set interval.

For ASP I have created a simple ActiveX which I typically include into ASP as follows:

Dim ActiveX
Set ActiveX = CreateObject("MyApp.Delay")
    ' Where "MyApp" is the name of your project
    ' and "Delay" is the name of the class, listed below
    ActiveX.Wait 10000 ' MilliSeconds
Set ActiveX = Nothing

Here's the code of the class. If you need to interrupt the delay to resume your code immediately, in case if you are using it in the loop in the Windows application, then you can set the Quit variable to True.

Option Explicit

Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Private Const WAIT_OBJECT_0& = 0
Private Const INFINITE = &HFFFF
Private Const ERROR_ALREADY_EXISTS = 183&
Private Const QS_HOTKEY& = &H80
Private Const QS_KEY& = &H1
Private Const QS_MOUSEBUTTON& = &H4
Private Const QS_MOUSEMOVE& = &H2
Private Const QS_PAINT& = &H20
Private Const QS_POSTMESSAGE& = &H8
Private Const QS_SENDMESSAGE& = &H40
Private Const QS_TIMER& = &H10
Private Const QS_MOUSE& = (QS_MOUSEMOVE Or QS_MOUSEBUTTON)
Private Const QS_INPUT& = (QS_MOUSE Or QS_KEY)
Private Const QS_ALLEVENTS& = (QS_INPUT Or QS_POSTMESSAGE Or QS_TIMER Or QS_PAINT Or QS_HOTKEY)
Private Const QS_ALLINPUT& = (QS_SENDMESSAGE Or QS_PAINT Or QS_TIMER Or QS_POSTMESSAGE Or QS_MOUSEBUTTON Or QS_MOUSEMOVE Or QS_HOTKEY Or QS_KEY)

Private Const UNITS = 4294967296#
Private Const MAX_LONG = -2147483648#

Private Declare Function CreateWaitableTimer Lib "kernel32" Alias "CreateWaitableTimerA" (ByVal lpSemaphoreAttributes As Long, ByVal bManualReset As Long, ByVal lpName As String) As Long
Private Declare Function SetWaitableTimer Lib "kernel32" (ByVal hTimer As Long, lpDueTime As FILETIME, ByVal lPeriod As Long, ByVal pfnCompletionRoutine As Long, ByVal lpArgToCompletionRoutine As Long, ByVal fResume As Long) As Long
Private Declare Function MsgWaitForMultipleObjects Lib "user32" (ByVal nCount As Long, pHandles As Long, ByVal fWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private mlTimer As Long
Public Quit As Boolean

Private Sub Class_Terminate()
    Quit = True
    If mlTimer <> 0 Then CloseHandle mlTimer
End Sub

Public Sub Wait(MilliSeconds As Long)
    Dim dblDelay As Double, dblDelayLow As Double
    Dim lBusy As Long, lRet As Long
    Dim ft As FILETIME
    
    mlTimer = CreateWaitableTimer(0, True, App.EXEName & "Timer" & Format$(Now(), "NNSS"))
    
    If Err.LastDllError <> ERROR_ALREADY_EXISTS Then
        ft.dwLowDateTime = -1
        ft.dwHighDateTime = -1
        lRet = SetWaitableTimer(mlTimer, ft, 0, 0, 0, 0)
    End If
    
    dblDelay = CDbl(MilliSeconds) * 10000# ' Convert the Units to nanoseconds.
    
    ' By setting the high/low time to a negative number, it tells
    ' the Wait (in SetWaitableTimer) to use an offset time as
    ' opposed to a hardcoded time. If it were positive, it would
    ' try to convert the value to GMT.
    ft.dwHighDateTime = -CLng(dblDelay / UNITS) - 1
    dblDelayLow = -UNITS * (dblDelay / UNITS - Fix(CStr(dblDelay / UNITS)))
    
    If dblDelayLow < MAX_LONG Then dblDelayLow = UNITS + dblDelayLow
    
    ft.dwLowDateTime = CLng(dblDelayLow)
    lRet = SetWaitableTimer(mlTimer, ft, 0, 0, 0, False)
    
    Do
        ' QS_ALLINPUT means that MsgWaitForMultipleObjects will
        ' return every time the thread in which it is running gets
        ' a message. If you wanted to handle messages in here you could,
        ' but by calling Doevents you are letting DefWindowProc
        ' do its normal windows message handling---Like DDE, etc.
        lBusy = MsgWaitForMultipleObjects(1, mlTimer, False, INFINITE, QS_ALLINPUT&)
        DoEvents
    Loop Until lBusy = WAIT_OBJECT_0 Or Quit
    
    CloseHandle mlTimer ' Close the handles when you are done with them.
    mlTimer = 0
    
End Sub
Debunk answered 6/7, 2023 at 13:57 Comment(0)
W
-1

In response to the “delay the response” part of your question:

dim SecondsToWait : SecondsToWait = 4
dim StartTime : StartTime = Time()
Do Until
   DateDiff("s", StartTime, Time(), 0, 0) > SecondsToWait
Loop

Pure Classic ASP without SQL and WScript Shell, but for debug delay purposes only. This is a snippet for testing (very helupful), but it does not address the “good way” part of your question ;-)

This answer for the sake of completeness and for people looking for (debug) delays. Failed login attempts should not be handled like this.

Wachter answered 4/9, 2013 at 7:3 Comment(0)
S
-1

Another way you can delay asp response by using this code.

Sub MyDelay(NumberOfSeconds)
Dim DateTimeResume
DateTimeResume= DateAdd("s", NumberOfSeconds, Now())
Do Until (Now() > DateTimeResume)
Loop
End Sub

Call this function by using this code.

Call MyDelay(5)
Smithers answered 27/4, 2015 at 9:18 Comment(0)
V
-4

Just redirect to a randomly named large image that takes the desired amount of seconds to load.

Vernonvernor answered 28/1, 2015 at 15:46 Comment(1)
This seems to be very dependant of the environment (band width, etc.) which in general is not desired.Dryden

© 2022 - 2025 — McMap. All rights reserved.