Stop spamberichten via webformulieren

27-10-2011 | Marcel van Langen | ColdFusion | spam, webformulier, post

Het is de vloek van het internet. Spam. En ze zijn nog slim ook. Webformulieren worden automatisch gevuld en verzonden. En die krijg jij weer in je mailbox. Of in je database. Of nog erger, in je applicatie. Maar hoe maak je nu onderscheid tussen de gewenste en de ongewenste berichten? In deze post geef ik je wat tips en direct te gebruiken code.

Spam is big business. Spam is intelligent. Als ColdFusion ontwikkelaar is het onze plicht om het de spammer zo moeilijk mogelijk maken onze formulieren te misbruiken. Hiervoor zijn standaard componenten beschikbaar. CFFormProtect is de meest bekende en werkt uitstekend. Je kunt CFFormProtect downloaden bij RIAForge. Eigenlijk lost deze component al je problemen al op. Maar het is toch goed om te verkennen wat je verder of anders kunt doen.

Is het door een mens geplaatst?

Je kunt op het formulier een vraag stellen die alleen door mensen kan worden beantwoord. In het verleden werd hiervoor vaak een zogenaamd CAPTCHA image gebruikt, maar die dingen zijn akelig onvriendelijk. In het verleden heb ik eenvoudige sommetjes gebruikt om te valideren of er wel een mens achter het scherm zit. Hiervoor voeg ik de volgende code toe aan het formulier zelf:

<cfset variables.random_getal_1 = RandRange(1,7) />
<cfset variables.random_getal_2 = RandRange(1,7) />
<cfset variables.som_resultaat = variables.random_getal_1 + variables.random_getal_2 />

<cflock scope="session" timeout="10" type="exclusive">
<cfset session.formulier_som = variables.som_resultaat />
</cflock>

Dit levert een random sommetje op. In de sessie scope sla ik vervolgens het antwoord op. Die ga ik straks vergelijken met hetgeen de gebruiker als antwoord op de som heeft gegeven. Vervolgens voeg ik aan het formulier het volgende hidden field toe:

<input type="hidden" name="frm_sleutel" value="#Encrypt(variables.som_resultaat, request.passwordsleutel, "CFMX_COMPAT", "HEX")#" />

Dit is straks de tweede controle op datzelfde veld. Niet alleen ga ik straks controleren of het resultaat overeenkomt met het antwoord in de session scope, ik verstuur ook het antwoord van de som versleuteld via encryptie met het formulier mee. Ook dit ga ik straks vergelijken met het antwoord dat de gebruiker heeft opgegeven. Natuurlijk moeten we ook nog even het sommetje aan de gebruiker vragen in datzelfde formulier:

<label for="frm_sommetje">#variables.random_getal_1# + #variables.random_getal_2# =</label>
<input name="frm_sommetje" type="text" value="" class="form_text" maxlength="2" id="frm_sommetje" />

Het enige dat ik nu nog hoef te doen is het antwoord van de gebruiker bij het verwerken te vergelijken met zowel het resultaat in de session scope als met het ge-encrypte antwoord. Dit doe ik op de verwerkende pagina eenvoudig als volgt:

<cfparam name="attributes.frm_sommetje" default="" />
<cfif attributes.frm_sommetje EQ "">
geen antwoord
<cfelseif NOT IsNumeric(attributes.frm_sommetje)>
onjuist antwoord
<cfelseif Decrypt(attributes.frm_sleutel, request.passwordsleutel, "CFMX_COMPAT", "HEX") NEQ attributes.frm_sommetje>
onjuist antwoord
<cfelseif attributes.frm_sommetje NEQ session.formulier_som>
onjuist antwoord
</cfif>

De eerste drempel voor spammers is een feit.

Is het automatisch gevuld?

Wanneer spammers een formulier vullen, vullen ze over het algemeen alle velden die ze tegenkomen. Een eenvoudige extra controle op spammers is dan ook om een veld op te nemen die juist leeg moet worden gelaten. Je kunt er niet op vertrouwen dat deze aanpak alle spam zal tegenhouden, maar het draagt weer bij aan de bescherming van je webtoepassing. Hiervoor voeg je de volgende code toe aan je formulier:

<input type="hidden" name="frm_leegveld" value="" />

En in de afhandeling van het formulier voeg je de volgende validatie toe:

<cfif attributes.frm_leegveld NEQ "">
waarschijnlijk spam
</cfif>

Wanneer is het gepost?

Spammers maken gebruik van intelligente tools om webformulieren dynamisch te vullen. Dit gaat automatisch. Maar vult een persoon een formulier in, dan zal hij tijd nodig hebben om het formulier in te vullen. Dat kunnen we gebruiken voor een extra controle. We gaan namelijk de tijd tussen het opvragen van het formulier en het verwerken van het formulier met elkaar vergelijken. Dit doen we door aan het formulier de volgende code toe te voegen:

<input type="hidden" name="frm_timestamp" value="#now()#" readonly="yes" />

Met het formulier wordt nu een timestamp meegestuurd van het moment dat het formulier werd opgevraagd. Dit gaan we in de verwerkende pagina gebruiken:

<cfparam name="attributes.frm_timestamp" default="" />
<cfif trim(attributes.frm_timestamp) EQ "">
niet via formulier
<cfelseif NOT IsDate(attributes.frm_timestamp)>
niet via formulier
<cfelseif DateDiff("s", attributes.frm_timestamp, now()) LTE 2>
te snelle invoer
<cfelseif DateDiff("d", attributes.frm_timestamp, now()) GTE 1>
te langzame invoer
</cfif>

Ik kijk eerst of de timestamp wel aanwezig is. Zo niet, dan zijn de gegevens niet via het formulier verzonden. Is de timestamp wel aanwezig (en is het een geldige datum), dan kijk ik of er minder dan 2 seconden tussen de timestamp en nu zit. Is dat het geval, dan zal het formulier naar alle waarschijnlijkheid automatisch verzonden zijn. Vervolgens kijk ik of er meer dan 1 dag tussen de timestamp en nu zit. Ook dan zal het gaan om automatische verzending. Je kunt deze tijden natuurlijk naar eigen inzicht aanpassen.

Waar komt het verzoek vandaan?

Laat ik beginnen met te zeggen dat deze laatste aanpassing geen betrouwbare controle is. Ik gebruik namelijk de CGI scope om te controleren waar de post vandaan komt. En deze CGI scope is een onbetrouwbare partner, omdat deze soms niet beschikbaar is, bijvoorbeeld door bepaalde instellingen van firewalls en privacybevorderende software aan de kant van de gebruiker. Niettemin kan het in extreme gevallen een mogelijkheid zijn om hierop terug te vallen. In dat geval voeg je eenvoudigweg de volgende controle toe aan de verwerkende pagina:

<cfif FindNoCase("uwdomeinnaam.nl",cgi.http_referer) EQ 0>
onjuiste herkomst
</cfif>

Vervang hierbij natuurlijk uwdomeinnaam.nl door je eigen domein. Dit stukje code controleert de CGI variabele http_referer op de aanwezigheid van het domein. Is deze daarin niet beschikbaar, dan is het waarschijnlijk dat het hier spam betreft.

Tenslotte

Dit artikel probeert niet volledig te zijn, maar zet je hopelijk wel op het spoor van de voor jou best werkende oplossing. Je kunt nog veel meer doen. Denk bijvoorbeeld aan het scannen van de inhoud van berichten, bijvoorbeeld op [/url] (voor zover je deze niet toestaat natuurlijk). Of stuur twee variabelen als hidden field mee. Pas een regel toe op de tweede variabele (bijvoorbeeld via een Replace actie), waardoor beide variabelen verschillend zijn bij het versturen. Door de regel op de verwerkende pagina weer omgekeerd toe te passen kun je controleren of de velden weer identiek zijn. Er zijn zoveel mogelijkheden. Veel plezier in het gevecht tegen spam!

Over Morloff

Morloff ontwerpt en realiseert succesvolle webtoepassingen op het gebied van procesoptimalisatie, eCommerce en rapportage door de inzet van moderne webtechnieken, sociale media en zoekmachine optimalisatie.

over ons | diensten | portfolio | contact | route

naar website IJsmonster.nl naar website ICT intermediairs naar website marcelvanlangen.com

Twitter

Morloff

Cuneraweg 169a
3911 RH Rhenen

M +31 (0)6 - 515 96 189
W www.morloff.nl
E contactformulier

Morloff op Twitter Morloff op Facebook Morloff op LinkedIn Morloff via RSS