One of the downsides of increased blog popularity is an increase in comment spam attempts. Fortunately, using a variety of tools already at hand, I’m able to identify and block these persistent buggers automagically while I sleep.
The Problem
Last week, this blog enjoyed some linkage from the technical ‘A-list’ regarding my analysis SixApart’s since modified licensing schedule for MovableType 3.0. With this recognition came a flood of comment spam attacks. Fortunately, most entries were thwarted using Jay Allen’s MT-Blacklist plug-in; however a few employing intentional typos got through.
I had pondered some sort of trap for the spammers … in fact I still am. Until then, I need a means of automatically denying them access regardless of typos without denying read access to legitimate visitors. So I began to pour through my various log files for a solution.
MT-Blacklist Logs to the Rescue
One of the options the MT-Blacklist offers is to log failed entry attempts. I have this turned on so I can quickly peruse my MovableType activity log. In fact, it was this very feature that brought to my attention the stepped-up attacks on my system. It also brought to my attention that the few successful entries shared IP addresses with many of the failed entries.
You see, along with being greedy and lazy, spammers, especially comment spammers, are persistent. I know because it took a set of 5 ips four days to finally sneak some comments onto my system … after 87 failed attempts. It was at this point I realized what I could do the temporarily thwart the spamscum:
Crontab as SQL statement that inserts records into the MovableType mt_ipbanlist table from entries in the mt_log where a denied IP address shows up more than once. I then encapsulated the SQL statement in a Perl program so I could add bells and whistles at a later date.
Yes, I realize the LEFT JOIN clause is less efficient than a NOT IN subquery, but unfortunately, I couldn’t get this more efficient syntax to work with the crufty version of MySQL on my server.
The Aforementioned Bells and Whistles
Next step is to expand this program to find the most egregious sinners determine which ones are not based in North America, and deny them using my .htaccess file. Here’s the MySQL statement I’d use to find these bums:
SELECT DISTINCT count( log_ip ), log_ip
FROM mt_log
WHERE mt_log.log_message
LIKE “MT-Blacklist comment denial%”
GROUP BY log_ip
HAVING count( log_ip ) >4
ORDER BY 1 DESC
Perhaps Jay Allen could be so kind as to add an option to the MT-Blacklist to automatically add IP addresses to the banned IP table? Or along the same lines, perhaps there could even create some sort of banned-IP list so I could share it with those using other blogging applications such as WordPress and pMachine?
That said, if you can juice it up the above code, leave a comment. I’d be interested in how you deal with it.
From time to time, it is a good thing to back up your entire site onto a different computer/server, even if your entire site is database-driven.
Take my situation two weeks ago, when my former host provider shut down the RBC site because of a false spam report. Even though no spam had actually been sent from redlandbaptist.org, my former host’s upstream provider demanded action. In response, my former shut down the site with no notification despite their existing terms of use to warn on the first time, suspend on the second, etc… After I threatened legal action, the host provider and I found a middle ground where he agreed to put the site online, and I agreed to move my sites elsewhere over the course of a weekend.
Fortunately, I was already moving sites off this host I personally found troublesome. The Redland site was the last to go. RBC was last because there are legacy portions of it that are not entirely data-driven. That is, with my other sites, I merely dumped the MySQL database off the old machine, install a fresh version of MovableType on the new site, pipe in the data, hit rebuild and viola!
While there are several PC based options for mirroring a site, I wanted something that would take files from one Linux server and move them to another. I had three choices. First, there is wget, a nice little GNU tool for offline reading and site mirroring. As Jim Roberts writes in his article “Mirroring Websites with wget“, the syntax is insanely simple:
My only problem with this approach is that much of the legacy stuff at RBC was image related and/or offline because it is seasonal. And though I employed the “-p” option, not all images made the cut, nor did any of our offline archives for obvious reasons. So another solution, at least where images are concerned, would be to use a Perl program that employs another command line download tool simply known as curl:
The above syntax converts urls like http://www.redlandbaptist.org/index.php?sid=123 into http://www.redlandbaptist.org/index.phpsid_123.html. Great for viewing, not so great as a working backup.
There was another way, one that insured I got all my files, all the correct paths and all the correct file ownerships and permissions. Unfortunately, this method required shell access, and though my former host provider was kind enough to put the site back online, I seriously doubt he would have honored any request to restore ssh access. So I cheated, I downloaded a ‘modified copy’ of the Gamma Web Shell.
‘Modified copy?’ The Gamma Web Shell allows an individual to execute shell commands directly from their browser, so you can imagine the security implications of installing such a program. So on my local PC, I first modified the password to something huge and random, I limited the commands allowed, and then I changed the code to accommodate changing the file name from WebShell.cgi to something hard to guess. Once modified, I ftp’d it to the old Redland site, and then entered the following commands:
Please be warned, if you go this route, you are putting your site at great risk. Don’t blame me if you get hacked. You have been warned. In fact, I only did it because my back was against the wall. That said, immediately after executing the above backup commands, I deleted the shell program. Then uploaded a text file full of Lorem Ipsum using the same name to make sure it couldn’t be reconstituted from the trash.
After these security precations, I FTP’s the backup file to the new host, invoked the command “tar -zxvf” and was back in business almost instantly. I also FTP’d the tar.gz backup file to my home PC and ‘burninated‘ a CD as a ‘suspenders and belt‘ precaution.
So how about you? What’s your method of mirroring and/or moving sites? Remember what I said this past August, “if you fail to plan, then you’re planning to fail.“
An interesting thing happened to me while sitting poolside here at Aqaba, Jordan. I was talking to an individual who works for one of the larger Christian marketing firms. He wants to develop a lay-ministry website for a neighborhood prayer ministry. Like many people I run into via Heal Your Church Website, he’s brilliant within his chosen vocation, and knows which end of the mouse of the computer to speak into, he’s no geek.
Its for these reasons he’s registered a domain and has subscribed to the web-development services offered by Register.com.
I mentioned that perhaps a blog-driven format might suit him better since his site is more current events oriented. Just type the post, no HTML in involved. He shrugged his shoulder and replied “well, I can do that with register.com.”
I didn’t reply. Instead, I just looked out over the beautiful blue waters of the Red Sea and thought that when I get back, I need to think of a way of describing the differences between a turnkey static website management system such as those offered by Register.com and the services one might get from a dynamic publishing system such as TypePad.
And more than explain the differences, explain why one would be better than another depending on the user’s context. Oh yes, and be able to explain it in non-techical geek terms.
How about you, un in to this much? If so, how have you explained it. Leave comments.
They say a picture is worth 1000 words.
‘Pelase’ don’t say something like this on your web church’s site
(the sign is bad enough):
Always test your new code.
Mistakes like can really detract from your site’s content …
I always get the help of a third partly before going live.
NOTE – Sorry guys, but I’m recycling an old article for new commentary. First, because it was published almost two years ago, second because I’m very tired, and though I have some geek things to say … I’m falling asleep on the keyboard.
After almost a year of people emailing me and leaving comments, I’ve finally made the time to update my Scripturizer Perl Module. The changes aren’t big, but they provide dramatic relief to a series of annoyances that have encumbered some of you from adding more Scripture references to your blogs, church websites and other electronically distributed information. I apologize for taking so long to get around to such an important issue.
Addition of Abbreviations:
I think a comment by Werner Peters (no relation) on Rob Hulson’s blog sums up the need for this addition best when he writes:
“It gets a little tedious writing out Deuteronomy every time!â€
Yeah, you’ve said a mouth full. So I’ve added various abbreviations to the new and improved version. I’ve done so a more hard-coded format than I like, but the regular expressions are complex enough. I want a novice to be able to look at the existing expressions for Books in the Bible and add or alter their own (expressions) as they need.
This was a painstaking process I automated to some degree with some Perl to split book names after the first three characters, then again at the fourth or fifth character depending on the length and vowel placement. I then went through each of the expressions and added or subtracted based upon a list of common Bible queries over at the ESV Bible. Many thanks to Stephen Smith for directing my attention to this data, it was incredibly helpful!
Finally, I took care of a small handful of typos, involving pluralization. I still need to write a “mop-up†option to weed-out typos such as “revalations†and “mathew.â€
Eliminate the addition of a space before non-white space bug.
This bug is exceptionally annoying to those who like to encapsulate their scripture references between brackets or parenthesis, such as (1 John 1:9). So annoying that back last August, Jason Rust left a comment on my blog with a fix. A fix later tried and tested and nicely documented by Joseph Markey.
Essentially, the bug was introduced when evaluating the space between a volume reference, e.g. 1 John 1:9, which in turn put a space in front of a non-volume reference such as John 1:9. The bug occurred when the character directly before the non-volume number scripture reference was anything other than a space.
What the fix does is it pushes the evaluation for the space into a variable, then pushes it in front of the hyperlink. An approach that works much better than a hard-coded space as nothing before the reference is then accurately rendered as nothing just before the hyperlink.
Eliminate the ‘c’ in the gcex modifier in the regular expression.
This regular expression mode modifier did nothing in this context, except possibily eat up CPU and sometimes throw errors in the latest version of Perl.
ESV Bible flip-flop
The great people at the ESV Bible have been so helpful and responsive in the past, I figured why not say thanks in the form of directing those who select the English Standard Version option to the ESV online Bible website.
In MovableType, that would be <$MTBodyEntry scripturize=â€ESVâ€$>
Using Scripturizer as a Perl Module, that would be print scripturize(â€Phil. 4:6-7″, “ESVâ€).
XHTML compliant hyperlinks
Ampersands are now represented as &
Usage Notes
Well, this is sorta a no-brainer. For those of you using the MovableType Scripturizer plug-in, leave the file /plugins/Scripturizer.pl alone.
Replace the file /extlib/Sermonizer/Scripture.pm with the referenced code below. That should be it, but first make a backup of the old one just in case.
Here is where I have it set-up on my PC for general purposes using ActiveState’s Perl Dev Kit: c:\perl\site\lib\Sermonizer\Scripturizer.pm.
Future Iterations
That’s pretty much it. If you can think of anything else, let me know and we’ll see about adding it.
Otherwise, I’d like to work on something else Stephen Smith of ESV sent me, a routine that fetches a snippet via their webservice. His code works but I have some MT centric things I want to tackle when slicing-n-dicing an XML stream of Matthew 5. That and I feel a full-blown ESV.pm module coming on. Well, Stephen has already created one, but I’ve got these ideas … and this compiler … well, you get the picture.
There is also a much needed death-blow to typos method subroutine that needs to be written. The question is, do I write it into the same pass, or do I run first a clean-up pass filter, then scripturize? Any thoughts would be appreciated.
Again, thanks to everyone for their input. I couldn’t have done any of this without your valuable input. Even if I could, it wouldn’t have been as much fun.
Find a bug? Have a suggestion? Leave a comment. We’ll do our best to see it gets taken care of … quickly. Get it to me in the next couple of days and I’ll try to add it as an update to this article.
Originally published on March 24, 2003 when the war in Iraq was heating up and I found direct links to popular RSS news feeds were effecting the speed in which pages loaded on a friend’s blog whom I help maintain. I’m re-posting this article for reasons that will become obvious later this week. Until then, enjoy this “Spidering Hack!-)”
Adding some syndicated news feeds is a nice way of adding some compelling content to your site.
The problem is that sometimes the news feed gets overrun during heavy news days, go offline and/or suffers a host of other connectivity issues that make YOUR site load slow because the software holds your user hostage while the feed retrieval portion of the application has to wait to timeout. You see this alot with PHPNuke and PostNuke sites.
A simple way around this problem is to use a program that periodically retrieves the feed, slices-n-dices and effectively caches it into an easy to include file on your host. Doing this achieves five goals:
user page loads are not penalized when feeds go down
failures to connect do not harm the existing include file
multiple attempts to read the feed to not penalize user
feed can be mirrored for local/private use
content can be formatted to taste
Below is a little program I wrote Thursday to grab news feeds from an AP Wire I found via Scripting.com for inclusion on a the website of a friend who makes his living in the political area.
Using the following CRONTAB syntax, the program is executed every 30 minutes: 30 * * * * /home/YOURPATH/getap.pl>/dev/null
The nice thing about this approach is that this particular feed does “get busy” from time to time and at one point on Friday went offline. My users did not notice because in most cases, I was able to get by the “busy signal” on the 2nd or 3rd attempt out of 10. In the case where the feed site went offline, my users merely viewed and older include file without interruption or delay.
Anyway, since I haven’t posted anything worthwhile in the past few days, I figured this was a good penance:
As part of my ongoing online response to some great questions, I offer the following request by Colin Kuskie, webmaster for the Sunset Presbyterian Church:
Hi Dean,
My name is Colin Kuskie, and I’m Sunset Presbyterian Church’s
volunteer webmaster/hostmaster. I’m planning on moving our website,
www.sunsetpres.org, to a CMS, and I’d like to help secure staff
and member email addresses against spam by using your obfuscator
as a macro, but I couldn’t find anywhere to download the code from
your site.
Is it publicly available, and if so, could you please give me a
pointer as to how to find it?
Thank you,
Colin
p.s. Thank you very much for your online minstry. It’s one of
the things that convinced me to go to a CMS, and then to teach
myself CSS in order to implement it. I pray that God will you
your website to enable other churches to reach out with God’s
message to the world.
How can I say no to a request like that? So I emailed Colin back with the response “… Colin, stop it, my ego is already ‘well maximized’ enough!” … Actually, that’s not what I said, but I thought it might be fun to share a little opera singer humor with you. Go ahead, ask your minister of music about their well-optimized ego … but I digress …
Actually, my first response to Colin was “Macro? For what system?.” This wasn’t out of any suspicion, but more out of curiosity, and more out of concern that my original obfuscator hack wouldn’t integrate all that well as it was built into an online form generated using CGI.PM. Colin responded that he intends to integrate the code behind the Mean Dean Anti-Spam E-Mail Obfuscator(MDA-SE-MO) into a macro for a Perl-based content manglement system (CMS) named WebGUI. A robust system I’ve played with once or twice before, but don’t let my ease-of-use fool you. This is one CMS that requires you know Perl, paths and your site map before you go shooting your foot off of with it … but I digress …
CMS sorties aside, Colin’s question reminded me of this sage advice she’d offer whenever I’d leave the house: “make sure you have on clean underwear.” Not that it would help if the last thing I ever saw was an oncoming cement truck … but I digress …
So, if I’m going to put my code ‘out there,’ I had better tidy it up a bit, and put it into a easy to deploy, use and maintain Perl module. This way, he could use it for WebGUI, and I could use it for an upcoming MovableType plug-in … unless one of you kind souls comes up with it first … but I digress …
I also figured if I as going to put it ‘out there,’ I might as well add an option that might really, truly drive spammer’s nutzo … that is to render the hyperlink as inline javascript. Yes, I know 12% of all browsers have Javascript disabled, but then again, how many people using wGet or Curl are actually interested in sending me email? That said, it is for such reasons that I have in the past advocated including a form-based email solution on your church or charity website. All the more so if you’re going to use the inline javascript option on Obfuscator … but perhaps this tangent is better left as a topic for later discussion?
Anyway, below is the code for the new perl module behind the slightly improved online demonstration of the MDA-SE-MO:
# 2 randoms, 1st for hex vs ascii, then again for ascii vs plain
sub OB_randlet {
my($l) = @_;
my($r) = &OB_round(rand());
return sprintf(“%%%02x”,ord($l)) if $r;
$r = &OB_round(rand());
return sprintf(“&#%03d;”,ord($l)) if $r;
return $l;
}
# real numbers only
sub OB_round {
my($n) = @_;
return int($n + .5 * ($n <=> 0));
}
# hex it up a bit
sub OB_hexape {
my($s) = @_;
$s =~ s/([a-zA-Z0-9:\s\@_\-.])/uc sprintf(“%%%02x”,ord($1))/eg;
return $s;
}
1;
__END__
=head1 NAME
Obfuscator – mangles email addresses to fool dumb spambots
=head1 SYNOPSIS
use Obfuscator;
print OB_encode($email, $prompt, $title, $usejs) if OB_validmail($email);
$email – required string representing an email address, remember, in Perl
it’s my $email = “foo\@bar.com” … note the slash
$prompt – optional string of the prompt you want between the .. tags
$title – optional string that is used for title argument of tag
$usejs – renders obfuscated hyperlink as a javascript link
=head1 DESCRIPTION
Obfuscator randomly converts your entries into a combination of numeric and
hexadecimal encodings, as well as a salting of some non-converted characters
that will either hide your e-mail address from spambots and/or cause their
mailers to gag because they’re not expecting all three encoding in radom.
This program also obfuscates the prompts to give spambots less key words to
search, find and filche.
=head1 IMPROVEMENTS
* n/a
=head1 BUGS
* n/a
=head1 AUTHOR
Dean Peters
=head1 EXAMPLE
use Obfuscator;
my $email = “obfuscater\@healyourchurchwebsite.org”;
print “Problems? Questions? “.
OB_encode($email, “drop me a line.”,
“Problem? Praise? Email Me”, true)
if OB_validmail($email);
=head1 COPYRIGHT and LICENSE
Copyright (c) 2002-2003
Dean Peters
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the “Software”),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The Software is not included in any commercial application or publication
without the expressed written permission of Dean Peters.
The Software is provided “as is”, without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. In no event shall
the authors or copyright holders be liable for any claim, damages or other
liability, whether in an action of contract, tort or otherwise, arising
from, out of or in connection with the Software or the use or other
dealings in the Software.
=cut
Yes, this obfuscator isn’t nearly as dastardly as the very cool and very effective online offering from HiveWare (hat tip to Mark Pilgrim) … perhaps by putting the Obfuscator.pm code ‘out there’ … someone will come up with an elegant “<noscript>” solution. I’ve already got one person who’s keen on optimizing the regular expressions … Colin, thank you.
My husband sent me this URL to improve the church website, but I feel over my head. In [point #] 7 you say. “What robot exclusion standard?” What is that?
Carolyn, as I like to say my 11th grade Sunday school class “there are no dumb questions, so thank you for asking … in doing so you help the rest of us learn something new.” Or put another way … ask and you shall receive!
Here is how it works. When Google or some other (legitimate/well-behaved) search engine visits your site, they first look for a file in your root/home directory named robots.txt. This little file contains simple, line-at-a-time instructions on what directories and files you would or would not like indexed. Here is an example of mine:
The first line says “User-agents” for ALL user agents, allow/disallow the following directories. The next two lines specify that I don’t want search engines to index anything in my /cgi-bin or /images subdirectory, mostly because it saves me bandwidth. Now I know what you’re thinking … “what the heck is a user agent?”
A user-agent is how we identify what type of software is visiting our site. What us geeks sometimes refer to as a “client application.” For example, many of my visitors are identified as using Microsoft Internet Explorer version 6, though today, because yesterday’s article found its way to Linux.org, the majority of my visitors are using the Mozilla browser. Google identifies itself as “googlebot.”
If I wanted, I could make an entry specific to Google that says I also don’t want it to visit nor index a file on my site named “googleme.html” by adding the following entry in my robots.txt file:
User-agent: googlebot
Disallow: googleme.html
Of course, there are those who abuse robots.txt. So the general rule of thumb is, if none of your web pages has a link to a private directory, then don’t list it in robots.txt. Looking at it the other way around, you should only allow/disallow subdirectories are linked on any of your web pages. For that, may I suggest reading my March 01’03 post entitled “How to block spambots, ban spybots, and tell unwanted robots to go to ….”
I could go on, but there is a MUCH better tutorial over at SearchEngineWorld. Not only does it go into greater detail on with useful real-world examples, but it also accompanied by their Robots.txt Validator … an online program that lets you check to see if your robots.txt file is kosher.
And if this doesn’t help, feel free to email me or leave a comment. I’ll just keep explaining it until everyone in the class gets it.