Forum Moderators: coopster & phranque

Message Too Old, No Replies

how to ensure perl scripts only run from server

stopping script stuffers

         

Frank_Rizzo

10:43 pm on Jul 24, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm trying to stop dodgy people from running scripts by typing directly in the URL field, or from running from other servers.

The script is called from a passed A HREF with query string:

<a href="/cgi-bin/data.pl?name=joe&location=north>joe's details</a>

What the monkeys are doing is modifying the address URL field in Explorer and chaning name=joe&location=north to name=jim&location=north, name=jim&location=south etc.

I tried inserting the following at the begining of the scripts:

***********
$domain = 'www.mydomain.com';
$check_loc = $ENV{'HTTP_REFERER'};
unless ($check_loc =~ m#$domain# ) {
print header;
print "<HTML><BODY>";
print "<P><B>WARNING</B>. Not running from authorised site.</P>";
print "</BODY></HTML>";
exit;
}
#rest of my code is here.....
#only genuine visitors get to run the code
#monkeys get the warning above
***********

It seems to work ok most of the time but I was getting genuine customers saying they were getting this warning page.

Is the script good? Is there a better one?

I'm going to change it all to PHP soon but I really need a good interim quick patch.

jdMorgan

11:03 pm on Jul 24, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Frank_Rizzo,

Using the HTTP_REFERER can be "iffy". Some ISPs, proxy servers, and user client programs (such as
Norton Internet Security) block the HTTP_REFERER, and all you'll get is "-" in your server logs.
You may have to modify your script to allow the referer to be either your domain OR blank. Also,
don't warn the hackers too thoroughly - you don't want to encourage them to fish around to figure
out ways to get by your filter. Just redirect them somewhere (like to your home page) so it looks
like your script is broken. It does feel good to say "I caught you," but it gives them an efficient
test tool...

HTH,
Jim

Frank_Rizzo

11:20 pm on Jul 24, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



jdM

I was guessing that some of the punters were using stuff like nortons to block the referer.

So how can I get around this quickly? I had a monkey hit a run of about 300 database records this evening. I've blocked his IP but he's on dynamic dial up - next time he'll be given a new IP.

If anyone knows of a way for perl to read in passed chars without displaying on the URL field I'd be grateful.

I'm not passing the vars via a form so I can't have hidden fields.

I know I gotta move to PHP but thats going to take me some time.

Your point about not warning them is a good one. I'll just produce a dummy page stuck on one record.

jdMorgan

11:52 pm on Jul 24, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Ugh...

Any other "identifying marks or features"?

Can you block by User-agent?

Some combination of Referer, User-agent, and IP address block? Limit the damage to bona-fide
visitors, but get this guy off your site for awhile to give you some time to come up with a better
approach?

Something like:
Accept all requests with non-blank referer field and referer=your site.
Block requests with blank referer and some combination of IP_address or Remote_Host and User-Agent?

Well, I'm out of my league here scripting-wise, so if nothing else, this will bump your thread...

Best,
Jim

toadhall

1:14 am on Jul 25, 2002 (gmt 0)

10+ Year Member



<snip>

... on second thought I think I'll have a coffee first.

bobriggs

1:29 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If anyone knows of a way for perl to read in passed chars without displaying on the URL field I'd be grateful.

It's the browser displaying the address bar.

I don't know how many links you would have to change, but you could use mod-rewrite.

Rewrite something like
/data/joe/north.html

to
/cgi-bin/data.pl?name=joe&location=north

Of course you'd have to change all the links, which might not be a solution for you, although it might be possible with a global search and replace on your static pages.

jatar_k

2:18 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



if this one joker is giving you trouble I would ban his IP block just until you figure out a solution. That will at least stop the immediate problem and give you a little time to work.

It will also give our perl gurus some time to stumble onto this thread. ;)

Tapolyai

2:24 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Why not just encode the two fields, maybe into a single field?

Say joeŚnorth becomes 6874726f6e7c656f6a. It's the text with an escape character and in hex backwards. Then add a CRC or hash of the code within the code at a "random" location.

That is, say joe is converted to 656f6a and north is 6874726f6e. The escape (to separate the two fields) is the 7c in-between.
North is 448630255470 in decimal and joe is 6647658. Length of North in decimal is 12, and joe is 7. Product of 12 and 7 = 54 in hex.

The whole string now:
6874726f6e7c54656f6a

Change any part, it will not match. Of course my checksum is quite terrible because there are several duplicates that would produce the same results. You could use a real hash, I just wanted to get the point across.

So, a bit of coding, and anyone changing the URL will have a hell of a good time working it out.

The length is variable, the escape location is variable, the checksum position is variable, and you can even vary the checksum method depending on the record type or the phase of the moon, etc...

toadhall

2:38 am on Jul 25, 2002 (gmt 0)

10+ Year Member



I'm not a perl guy, but here's something you might adapt (it's php):

$refer = split('\?',$HTTP_REFERER); // results in the full path sans name/value
if (!$HTTP_REFERER) {
echo 'Our security system uses $HTTP_REFERER. Please lower your shields and try again.';
} else if ($refer[0]=='http://localhost/test/httpreferer.html') {
echo "Welcome to Ricky's Hoedown!";
echo "<br>\nAnd by the way, your name's $name and you live in the $location.";
} else {
readfile("http://localhost/test/httpreferer.html"); // redirect
}

It will work with any name/value appended to the url.
Feel free to use my unique messages if you wish. I'll waive copyright. ;)

amoore

2:45 am on Jul 25, 2002 (gmt 0)

10+ Year Member



Generate your pages with a script. Make the script add a random number variable 'ticket' to the query string. Stuff that random ticket into a database with the time. Something like: /cgi-bin/data.pl?name=joe&location=north&tiket=123456987.

Make data.pl check that the ticket it gets is associated with a recent time. This only allows them to work for a little while.

If you're really concerned, only allow the ticket to be used once.

Oh yeah, crontab a script to clean out old entries from your ticket table occasionally.

Does that cut it?

toadhall

3:55 am on Jul 25, 2002 (gmt 0)

10+ Year Member



BTW, in case anyone notices... that redirect method in the code I sent isn't appropriate (that's a nice way of saying "it doesn't work").

Use:

header ("Location: [blah");...]

instead

richlowe

4:48 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



IN a guestbook routine that I downloaded, I found that it keeps a small file of IP addresses that have made entries recently. It only allows an IP to make a post to the guestbook once an hour or so. That seems to work much better than blocking referrals.

Richard Lowe

Brett_Tabke

5:19 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Administrator 10+ Year Member Top Contributors Of The Month



I think you have the best solution amoore:

>Make the script add a random number variable
>'ticket' to the query string.

Or better, add a "time" id to the form.

$ttime =time;
print qq¦ <your form here><input type=hidden name=ttime value=$ttime>¦;

Then the receiver script checks the time value and stuffs it if it is close (say, 20-30 mins).

$dif =$FORM{'ttime'};
$allowedtime =60*60*30; #30mins.
if (time -$dif > $allowedtime) ... then go error.

Frank_Rizzo

8:51 am on Jul 25, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Some good stuff here guys. And its really nice to see a bit of pulling together to sort out a problem.

I like the encryption method. I think I'll work on a version of that.

I'm trying to squeeze maxmimum speed out of the darn server though. I'm one of those speed freaks who has everything tuned to the max (man, I even overclock the toaster and coffee machine).

The timestamp idea is ok, but I dont know if it will work with this setup:

[search.html]
user enters a name to search for - has form which passes 'JOE' to search.pl
[search.pl]
scans search database all displays matching names
user clicks on link which then runs data.pl

******************************
Enter name to search: JOE
------
Results of your search:

<a href="/cgi-bin/data.pl?user=joe&location=north">joe montana</a>
<a href="/cgi-bin/data.pl?user=joey&location=south">joey tribiana</a>
<a href="/cgi-bin/data.pl?user=joebox&location=east">joe boxers</a>
<a href="/cgi-bin/data.pl?user=joe90&location=north">joe ninety</a>
******************************

See that the page with the list is generated on the fly.

I think that the monkey has either laboriously clicked on one, and then modified the URL address box to try every combination of Joe and location, else he has a script running on his own location which is accumulating the data. BTW, thousands of records in there!

Anyway, thanks again for mucking in guys.

Frank_Rizzo

10:08 am on Jul 26, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Ok, I worked out a solution for this.

First I really wanted to do some encryption similar to cgi-bin/data.pl?chunk=0EA7523FCB1

That would be really cool. But I can't do this because most of the links are generated off line by Clipper database app. Clipper doesn't know crypt(), encode() etc.

What I decided to do was to shove a simple checksum on the end of each url:

cgi-bin/data.pl?name=joe&location=north&id=123&length=138471

It is $id which the monkeys are changing to steal the data. $length (not using the obvious $checksum as that would give clues), is a function of $id

$length = (($id * key1) + key2);

in the data.pl I just check for the right checksum

if($id ne (($length - key2) / key1)) {
$id = 1;
}

What this does is to check the checksum. If it is incorrect then the $id has changed in the URL box and the monkey is always presented with database record 1.

Ok, it probably won't take long to work out the key2 and key1 fields but I'm thinking of changing the keys every half hour.

Run a script via crontab which creates random numbers for key1 and key2 and writes to a keys file. The search and data.pl then get the randomly changing keys from the keys file.