Gatekeeper In Perspective
Published 19 years, 9 months pastSo when I said on Monday:
Got feedback? Let’s hear it?
…what I actually meant was:
Got feedback about the code or how the package works once it’s installed in WordPress? Let’s hear it.
I should have realized that otherwise, the comments would turn into an argument about comment spam, fighting it, ways the general idea could be defeated, and more. Which they did.
Look, folks, despite what some people might tell you, I’m not so arrogant as to think that I could single-handedly solve the comment spamming problem for all time. Even if I were, I very much doubt I’d be so clueless as to think that WP-Gatekeeper was that solution. And if both those things were the case, I’m pretty darned near certain I would have very explicitly made the claim of having beaten the spammers. Likely in big, boldfaced, red, capitalized, blinking letters, plus a background MIDI of “We Are The Champions”.
WP-Gatekeeper is not going to stop every possible comment spam attack, human or automated, for the rest of time. Neither is any other defense you can name, without exception. There may be measures that currently have 100% resistance to scripted attacks. They will one day fail—I can pretty much guarantee it. Even today, they are defeatable by actual humans sitting at computers and posting comment spam on every site they find. That kind of spamming is very, very rare, but it happens. I had such an incident within the last month. If I hadn’t been keeping a close eye on new comments just then, I’d likely have missed it completely.
I’m fully aware that there are ways a spambot could defeat WP-Gatekeeper. At the moment, none of them can. That will one day change, of course, assuming challenges become at all popular. Comment spam and the fighting thereof is a dance, a tennis match, an arms race. Neither side will ever win. As one side adopts a new tactic, the other side will move to counter it. The countermeasure will itself be countered. And so it goes. Eventually, either spambots or spam defenses (or the two in combination) will become so advanced that they’ll gain self-awareness, and then we’ll all be royally hosed.
I know this. You know this. Let’s move on from there, okay?
In the end, the goal is to add another arrow to the quiver at the disposal of spam fighters. Think this approach is wrongheaded, annoying, or otherwise pointless? Fine. Don’t use it. For those who want to add this kind of capability—and since I instituted it on meyerweb, I’ve had not a single piece of spam make it onto the site or hit the moderation queue, whereas in my pre-defense days, I’d get at least twenty every day—then the package is there. You can combine it with other defenses, if you like, for even more coverage. I may upgrade it in the future, depending how far I get in learning PHP, mySQL, and form handling, and what feedback I get from people who know PHP better than I do. I may not, in which case the system as it stands is effective, and probably will be for a while. Even if I do one day abandon further development, the code is out there for someone else to improve if they so choose.
In the meantime, if there’s anyone who is using WP-Gatekeeper or has looked at the code, and has feedback on the coding or the way it works for the administrator of a WP blog, please feel free to share. Also, if anyone can point me to an example of PHP code for collecting all of the HTTP_VARS that are returned by an XHTML form and then looking through them, even when the variable names aren’t necessarily known ahead of time, I’d really like to see it. Thanks.
Comments (26)
foreach ($_POST as $foo => $bar)
{
Your code here.
$foo will be the form field name.
$bar will be its value.
}
Thanks, Todd, but I need a little more explanation before I’ll be able to “get it”, especially since I’m new to both PHP and the whole “foreach blah as foo” approach. A URL to a demonstration, or a tutorial, is fine.
Basically its a loop structure that iterates through a set of something (in this case it is the pages submitted POST variables.
$_POST is an associative array(set of variables), There are two types of arrays, there are numerical which are referenced by number, and associative arrays which are referenced by name or number. The foreach function steps throught an array(either type), within each step the variable $foo will contain the form field name or ‘key’.
$bar will contain its contents or ‘value’.
Lets take this form for example.
In the first iteration the key will be ‘author’, and the value will be ‘Aaron Tate’.
Second: key will be ‘comment_post_ID’ and value will be ‘552’ (hidden fields are included obviously.)
More info on foreach (and a wealth of other knowledge) here:
http://au.php.net/manual/en/control-structures.foreach.php
The above code, given a form submission with, say, a field named “solemnity” into which someone has entered “durian,” and a field “email” into which someone has entered their email address, would loop twice. The first time, $foo would = “solemnity” and $bar would = “durian.” Next time, $foo would = “email” and $bar would = the email address.
Better?
To explain Comment #1 a little more, the “foreach” keyword is an Iterator shortcut common in some languages. It enumerates all of the elements of a datastructure in a loop. Every time through the loop, it assigns one of them to a variable:
foreach($_POST as $var){
do things with $var;
}
This works if $_POST is a 1-d array, but it’s not. It’s associative with keys, and in the case of $_POST, those keys are the field names. In PHP, arrays with keys have syntax like key => value, so instead of taking:
as $var
we have to write:
as $key => $var
Got it, gang. Thanks!
Update: Oops, I ran into another problem, which is that given a set of checkboxes, or several text inputs with the same name, only the last instance of that input passed through the value. So, for example, if I set up a “check the boxes next to the green and red squares” type challenge, it would always fail because onnly the last of the two checks would come back. My attempts to parse values to see if they’re arrays seems not to work. Any suggestions/pointers?
If i understand you correctly its a html thing not a php, checkboxes have the same name, but the value will be different…
<input name="color" type="checkbox" value="red">
<input name="color" type="checkbox" value="green">
and in php (with the code from comment 1) code, it would be
$foo = color
$bar = red/green
as for mutliple inputs, they cant have the same name, its unique like id
Also note that only checkboxes that are checked will be submitted. Unchecked boxes are ignored.
Have you tried changing the name attribute on your checkboxes from “name” to “name[]”? PHP will interpret this as an array.
Michael: Except when I try it, with both boxes checked, $bar returns just green. No red. That’s kind of the problem.
Also, when I looked through http://w3.org/TR/html4/interact/forms.html , I didn’t see any reference to text inputs having to have unique names. Since
name
, as an attribute ofinput
, can’t be unique (otherwise checkboxes and radio buttons would fail), I’m not sure text inputs are required to be unique either. Of course, if I missed an explanatory line in the text of the specification, it wouldn’t be the first time.also, you might not know that
print_r($variable);
will print out the array in the propor format so you can see what is going on.
eric, I did a quick test for you and this html
<input type="checkbox" name="checkbox[]" value="checkbo1x">
<input type="checkbox" name="checkbox[]" value="checkbox2">
<input type="submit" name="Submit" value="Submit">
produced this
Array ( [checkbox] => Array ( [0] => checkbo1x [1] => checkbox2 ) [Submit] => Submit )
The complex part of this issue is that http only sends the info for the checked boxes. That means you can’t have a bunch of value-less checkboxes like this:
<input type="checkbox" name="inputname[]" />
<input type="checkbox" name="inputname[]" />
<input type="checkbox" name="inputname[]" />
<input type="checkbox" name="inputname[]" />
and know in the end which boxes have been checked. (You would only end up knowing how many had been checked, but not which ones exactly.)
You need to either give each of them a unique value or explicitly say their location in the array within the name field like so:
<input type="checkbox" name="inputname[0]" />
<input type="checkbox" name="inputname[1]" />
<input type="checkbox" name="inputname[2]" />
<input type="checkbox" name="inputname[3]" />
but if you have a question like “check blue and red” you count the array, if its less or greater then 2 then deny it, the do a loop to make sure both values actually match one of the answers, this might prove easier or harder depending on how you are useto coding.
So much for managing community direction.
Here’s a test case: http://meyerweb.com/eric/php/formtest.html
You’ll note that it contains form elements that have defined values, some of which share names, and others of which do not. When the form is submitted, the result is that the last value in each name-group that doesn’t have brackets is read; anything before it in that group is dropped. Now, if I add the square brackets to the names in the XHTML, PHP suddenly wakes up and handles things correctly. Without them, it doesn’t.
So does anyone know how to make PHP handle HTTP form data correctly without the square-bracket hack, which should not be needed? No guesses; I said does anyone know either how to fix PHP sans hacks, or else exactly why it’s acting this way.
Obviously since scanned fingerprint identification is not an option at this point, then any method used to seperate visitors from spammers and spambots is going to be dependent upon the dedication of the spammers, no? They’ll just figure out a way around any blockade we put up.
Plus, it’s just plain silly to go to the effort of installing a biometric ID system on your server, and even sillier to expect your readers to purchase a thumb scanner (priced anywhere from $57.00 to $460.00 depending on the quality) and hook it up just to comment.
However, for little or no money per person, the problem could be solved immediately and forever. Here’s what you need:
Okay. Eric makes his comment area with one extra text input field for the name and ID number (the aformentioned “Keith Burgin – 1” corresponding to your comment.
You look at the ID number on your current label, type that in, and send your comment. Don’t look for it to be up for a couple of days, this takes a bit of time to sort out.
Once you’ve sent your comment, take the ID sticker and wrap it around the finger of your choice. Hack that baby off with the kitchen knife, seal the stub with the propane torch, seal the finger in the Ziploc bag, slide that in the padded envelope and mail it off to Eric.
Once Eric gets the finger, he simply matches the ID tag to your comment and approves the comment. Simple, no?
Spammers are not dedicated enough to use this system – I can guarantee that. Of course it will cut down on the number of legitimate comments, since an individual will only be able to do this ten times. Of course, you could always use toes, but let’s be a little sympathetic to Eric, people. Who wants to get a toe in the mail? I mean, really…
Any takers?
It’s documented behaviour, though as you correctly say, it shouldn’t be necessary.
Just posting this here having passed it to Eric by IM, for the sake of anyone who’s looking here for the answer.
Ok let’s go back to basics – what does your browser send to meyerweb.com when you click submit on that form. In the form tag, you’ve said use method “post”, so the browser is going to create a http post request that looks something like this: (many fields omitted for brevity, and defaults used)
checker=cb02
checker=cb04
checker=cb05
checker2[]=cb02
checker2[]=cb04
checker2[]=cb05
This is what PHP gets. Now php has to decide how to take this post data and convert it into php variables. It does this in a pretty straightforward manner, and will execute PHP code something like this:
$_POST[‘checker’] = ‘cb02’
$_POST[‘checker’] = ‘cb04’
$_POST[‘checker’] = ‘cb05’
$_POST[‘checker2’][] = ‘cb02’
$_POST[‘checker2’][] = ‘cb04’
$_POST[‘checker2’][] = ‘cb05’
Now do you see why you need those []? Without them PHP assigns each checked value from ‘checker’ to $checker, each value overwriting the previous. When you add the [], php assumes you want an array that stores them all.
I hope this explains more clearly what’s going on. The details may not be exactly correct, but I hope you get the gist of it.
Eric, as explain by Hadley above (very well i might add) these are needed, I just dont see the big deal? the html still validates, are there accessability issues? or just a personal annoyance?
There are two reasons I wanted the real information, and wasn’t satisfied to settle for just knowing that something worked without any information on why.
First, as you say, personal annoyance; HTML forms have long passed multiple-value results, and every other scripting language I’ve ever used has been able to deal with that or at least make it possible to deal with it. In fact, they would, absent any special handling, generally return a value something like ‘cb02:cb04:cb05’ (using some kind of separator). PHP appears to be either too dumb to understand that, or too “smart” to let it pass through untouched. The latter approach, that of trying to be smarter than the user, being what Microsoft does all the time.
Second, if I’m going to hack, I want to know exactly why the hack is necessary. I don’t use CSS hacks I don’t understand, and I avoid using CSS hacks whenever possible. The same goes for any other hack in any other language. I will avoid hacks whenever I can, and only use them when I understand them.
Via IM, I got a pointer from JH: http://us4.php.net/manual/en/faq.html.php#faq.html.arrays . That lead me to http://us4.php.net/manual/en/language.variables.external.php . In the comments on the latter page, carl_steinhilber said (back in 2002):
He was wrong about the square-bracket workaround being non-compliant; it’s acceptable to throw brackets into
name
values. Other than that, I agree with him 110%.Regardless, that closes that case: if I want to use checkboxes or have the same name applied to multiple text boxes, I’m going to have to use the square brackets. Now—for anyone who is using WP-Gatekeeper or has looked at the code, and has feedback on the coding or the way it works for the administrator of a WP blog, I’d like to hear from you.
Trackback ::
randomfire
Form elements as PHP variables
A discussion on how the requirement for brackets for form elements as a PHP array is actually quite useful….
Trackback ::
bookmarks
Eric’s Archived Thoughts: WP-Gatekeeper
Eric’s Archived Thoughts: WP-Gatekeeper (more, dl & instructions)…
Using the
name="blah[]"
‘trick’, why not use thecount()
function?You iterate through the $_POST variables:
foreach ($_POST as $foo => $bar)
{
if (count($bar) > 1) {
//it's an array
} else {
//it's not an array
}
}
Here’s an example I just whipped up to make sure I was right. I ran it as a script from the command line, but the ideas are the same:
$monkies[0] = 'Bobo';
$monkies[1] = 'George';
$animals['dog'] = 'Rex';
$animals['cow'] = 'Bessie';
$animals['monkey'] = $monkies;
$animals['cat'] = 'Spot';
echo 'I have the following pets:'."n";
foreach ($animals as $theKey => $theValue) {
if (count($theValue) > 1) {
for ($i=0;$i<count ($theValue);$i++) {
echo 'A ' . $theKey . ' named ' . $theValue[$i] . "n";
}
} else {
echo 'A ' . $theKey . ' named ' . $theValue . "n";
}
}
But yes, it would be easier if PHP just put the checkboxes as delimited text.
Personally Eric i don’t think people appreciate all you do, even if they don’t like the idea they should get over themselves and enjoy that someone is doing something
Eric, perhaps my earlier e-mail should have also been a post – THANKS! Nothing more needs to be said.
Eric,
There is another way, potentially, if you’ll pardon my limited (but now growing) understanding of PHP: why not set, perhaps, 7 checkboxes, each with a designated color. Only 4 would have the proper colors. Have the user select only the green and the blue. Set the array with PHP — then implode the array using the
implode()
command to a single string and send that string to the handling script. Let PHP then parse the string. Right string: post; wrong string: send them to another site (maybe even their own IP).Just a thought…