How to delay a response in Classic ASP - asp-classic

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?

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:
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.
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.
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.

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

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.)

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>

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.

<%
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
%>

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.

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...

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 :-)

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

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
%>

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.

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)

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

Related

Best way to distinguish between Test and Production with Classic ASP

I have inherited a classic ASP application. There are various things that need tidying-up but I am forced to do things gradually (I can't do a wholesale change of every script).
There are places in the system with hard-coded urls. Some scripts have to be changed before promoting to live so that the test web root name is changed to the live web root name. I am looking at ways to avoid this (essentially working out the server programmatically).
It isn't difficult. A simple check on Request("SERVER_NAME") and this sort of thing:
appName = Request.ServerVariables("SCRIPT_NAME")
i = InStr(2, appName, "/") 'keep initial "/"
If i > 0 Then
appName = Left(appName, i)
End If
This in an "everywhere-included" script will do the trick. Then just set up a global variable to hold the full "http(s)://server/app/" or a function to MakeUrlAbsolute() and off you go.
However, this smells a bit suspect to me. Wouldn't this be better stored at application level? This suggests setting it up in Global.asa, something like:
Sub Application_OnStart()
Application("WebRoot") = ...
End Sub
But now I can't get hold of the SERVER_NAME so I'm reduced to having 2 different Global.asa files (one per environment).
Is this my only choice? Work it out on every request or hard-code in Application_OnStart?
Perhaps using Session_OnStart would be a compromise. Or is there some clever trick to access information in Application_OnStart? Perhaps people go with the hard-coding because Global.asa doesn't change often.
my method for ADO Connection looks like this.
'Servername for Application Root Drive
Select Case Left(Request.ServerVariables("PATH_TRANSLATED"), 1)
Case "c" strSrvr = "80.212.207.211" 'my remote server ip
Case Else strSrvr = "(local)" 'local Sql instance (my project on D: drive)
End Select
strConn = "Driver={SQL Server};Server="& strSrvr &";uid=DBUSER;pwd=MYPASS;database=MYDATABASE"
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open(strConn)

Debugging a database deadlock

I am trying to solve a problem with a site written in classic ASP with a SQL Server 2000 database.
Every few days the site seems to go down. There is no response from the website when you try to visit it. The loading indicator in your browser will spin round and the page just stays blank.
When I run sp_who2 after the site has gone down there's always a process that has taken up a large amount of CPU time. This process will be blocking all the other processes in the database.
I can get the site working again by killing this process.
I can't work out what's going on. When I look to see the stored procedure that this process ran before it locked up there's nothing wrong with it. The page that runs this stored procedure closes all the connection objects.
Any ideas of what could be causing this deadlock, or how I can stop it from happening?
Not sure if this is the issue, but it could be that not all recordsets and connection are always closed... When we had similar issues in the past we ended up with the following routine.. (Note that this is just a snippet showing one recordset closure, the real procedure actually goes over 15 different recordsets to see if they need to be closed..).
The modCloseObjects() prodedure is then always called at the end of the page, before a redirect, inside error handling and so one...
' subroutine will close and set the objects to Nothing. '
' Close Recordsets and then the Connection '
sub modCloseObjects()
'Close the record sets one by one '
If ucase(TypeName(oRS)) = "RECORDSET" then
if oRS.state <> adStateClosed then
oRS.close
Set oRS = Nothing
end if
end if
' if you have other recordSet objects, add them to the rourtine here: '
' Close the connection '
If ucase(TypeName(objConn)) = "CONNECTION" then
if objConn.state <> adStateClosed then
objConn.close
Set objConn = Nothing
end if
end if
end sub
If you don't have adovbs.inc , you'll need the following constant too:
Const adStateClosed = &H00000000

Change all users attribute in a domain in Active Directory recursively

I've got a little script (VBS) to change website attribute of all users :
dim objOU, objUser
objOU="OU=Users,DC=mysociety,DC=local"
Set objOU = GetObject("LDAP://" & objOU)
on error resume next
For each objUser in objOU
If objUser.Class="user" Then
Set objUser = GetObject("LDAP://" & objUser.distinguishedName)
objUser.Put "wWWHomePage", "http://mysite.mysociety.local/Person.aspx?accountname=mysociety\" & objUser.mailNickname
objUser.setInfo
if err.number <> 0 then
wscript.echo "Error processing " & objUser.givenName & ":" & err.number & ", " & err.Description
err.clear
end if
End if
Next
So my problem is that if I precise my objOU up to deepest directories it works perfectly. But it is not a recursive script and with this code it doesn't work because of subdirectories.
I am a very beginner with VBS, could you help me to make this script recursive ?
Thank you by advance and excuse me for my poor English
This task can be accomplished very efficiently using threads. In one thread, execute a search
wherein the base object is that point in the directory information tree (DIT) below which all
the entries that must be modified are stored. Use whole subtree for the search scope and a
filter that narrows the search results to just the entries that require modification. Use the
OID 1.1 for the list of requested attributes (this will cause the directory server to return
only distinguished names). Assuming the directory administrator allows this search - it may be
denied for resource reasons or security reasons or other reasons - as the search results arrive,
use another thread to construct the modifications on the distinguished names that are being
returned in the search thread. For maximum efficiency, use multiple threads to make the
modifications and use the appropriate concurrence mechanisms for your API.
see also
LDAP: Programming practices
LDAP: Search Best practices

Asp pattern page execution

I am working on an old asp project. I am new in project. My question is,
I required to execute the certain number of pages in a specific time but when I put in a loop to execute the page for certain pattern it execute only one pattern and shows time out expired problem. Though I searched in net what ever answer I get it will not fulfill my requirement. So my team lead said we have to find out some thing that can execute my page for 3 min each then it call back freshly. Is there any method is there in asp. as I am new in asp. as it possible or share some idea.
I want to execute for each pattern. Here is my simple code.
dim arrList()
dim mySQL,x,strPtrn
mySQL=""
x=0
strPtrn=""
mySQL="select distinct(pattern_no) from pattern_master where std between (dateadd(hh,8,getdate())) and (dateadd(hh,11,getdate()))"
set rstptrnmst= conn.Execute(mySQL)
do until rstptrnmst.EOF
for each ptrn in rstptrnmst.Fields
' Response.Write(x.name)
'Response.Write(" = ")
'Response.Write(x.value & "<br />")
ReDim Preserve arrList(x)
arrList(x)=ptrn.value
x=x+1
next
Response.Write("<br />")
rstptrnmst.MoveNext
loop
rstptrnmst.close
conn.close
'for each ptrn in arrList
'response.Write("<br>" & ptrn)
'next
for each ptrn in arrList
Session("pattern")=ptrn
server.Execute("processFNO.asp")
Session("pattern")=""
response.write("The first pattern " & ptrn & "<br />")
next
'Response.Redirect("processFNO.asp?fno="&arrList(0))
%>
So I want execute that page for each pattern. How can I? For each pattern it take 3 min(approx).any idea?
thanks for advance.
If your script is going to take that long to run you will need to set the script timeout option to cater for that:
<%
Server.ScriptTimeout = 180
%>
The timeout is set in seconds, with the default being 90. You should set this at the very top of your page.
More information about timeouts here: How do I increase timeout values

asp errors not displayed

I have moved an sql database from one server to a new one (detached/attached)
Now i experience some strange behavior as it does not work but NO error is displayed.
This is the code
<%
const database_dsn="PROVIDER=SQLNCLI10; SERVER=FR-2626\SQLLOP;DATABASE=Lop;Uid=admin-sql;Pwd=xxxx;"
response.write "Step 0//"
set conn=server.CreateObject("ADODB.Connection")
set RS=server.CreateObject("ADODB.Recordset")
conn.Open database_dsn
response.write "Step 1//"
req = "Select count(*) From tblArticleList"
response.write "Step 2//"
set RS = conn.Execute(req)
response.write "Step 3//"
%>
The program stops at Step 2; then nothing, no error is displayed...
I just don t know what to do..How can i get some error?
Thanks
Jonathan
Oh i partially found the answer for the error display.
In the Debug pane of the IIS directory configuration, Enable ASP debugging should NOT be checked...althought i thougth it should...
have you got your browser set to "show friendly http errors" this in conjuction with what you have already identified is the common causes I've had for not seeing an error message.
Shahkaplesh is also right that you can use Server.GetLastError() to get the last error that occurred but you shouldn't need to do this in this example.
When executing a query you don't use the "Set" command. I don't know why its not showing you anything, but your code should look more like this:
<%
const database_dsn="PROVIDER=SQLNCLI10; SERVER=FR-2626\SQLLOP;DATABASE=Lop;Uid=admin-sql;Pwd=xxxx;"
response.write("Step 0//")
set conn=server.CreateObject("ADODB.Connection")
set RS=server.CreateObject("ADODB.Recordset")
conn.Open database_dsn
response.write("Step 1//")
req = "Select count(*) From tblArticleList"
response.write("Step 2//")
RS = conn.Execute(req)
response.write("Step 3//")
%>
Yes, the parentheses on the "Response.Write" are optional. But I'm OCD like that and it makes troubleshooting a little easier.
You need to place error checking code to find out what the actual error might be.
I would suggest that you change you code like so:
<%
const database_dsn="PROVIDER=SQLNCLI10; SERVER=FR-2626\SQLLOP;DATABASE=Lop;Uid=admin- sql;Pwd=xxxx;"
'Its very important to add this line!!! '
On Error Resume Next
'Its very important to add this line!!! '
response.write "Step 0//"
set conn=server.CreateObject("ADODB.Connection")
set RS=server.CreateObject("ADODB.Recordset")
conn.Open database_dsn
if err.number<>0 then
response.write err.description
end if
response.write "Step 1//"
req = "Select count(*) From tblArticleList"
response.write "Step 2//"
set RS = conn.Execute(req)
if err.number<>0 then
response.write err.description
end if
response.write "Step 3//"
%>
And not to kick a dead horse but I'm doing something similar against an Oracle database and I've had two phantom problems I have yet to identify root cause but here's two things that made them go away.
1. Name all columns in a Query and Alias any that are calculated (sum, count, avg, etc.) So your query would become
req = "Select count(*) NumRows From tblArticleList"
2. Wrapping my query string in a call to cstr caused the result.EOF flag to be populated correctly rather than be returned with an empty or null value causing a simple DO WHILE NOT result.EOF Some Action LOOP to create an infinite loop until the web request timed out. So e.g.
response.write "Step 2//"
set RS = conn.Execute(cstr(req))
Nothing major just a couple tips if you get stuck and can't find out why. Follow the debugging advice above though, that's good info.
I think Server has a GetLastError method, which you can check to find out what error occurred while running any statement.
e.g. On error resume next
.... statement that could cause an error....
errorObject = Server.GetLastError()
For ASPError object, refer http://www.w3schools.com/asp/asp_ref_error.asp
Actually don't mean to be disagreeable, but yes you do need a Set there, as you are setting an object type and presumably wanting to use the return value at some point (if this wasn't just a test script which it looks like to me).
Also btw parentheses are not really correct there in Response.Write() as it does not return a value. They only happen to work on single parameter subs because you can put parentheses anywhere you like around expressions.
eg:
a = (b)
Response.Write ((("test"))&(1))

Resources