Stoppa spam-bots på din sajt

Jag har spenderat lite tid för att söka efter ett bra sätt, att försvåra för spam-robotar att snappa upp emailadresser på de webbplatser jag jobbar med. Hittade lite olika lösningar som verkar mer eller mindre effektiva.

Lite olika varianter
Först tänkte jag räkna upp några av de sidor som jag besökte innan jag hittade det som kändes bäst för mig. Ett sätt som jag aldrig tänkt på innan är att använda CSS för att skydda adresserna.

Hittade också ett enkelt exempel på en javascript-variant, där man skriver ut adresserna enligt "user at example.org".

Ytterligare ett exempel finns på The Code Project. som använder en BitConverter i Asp.Net för att göra om adresserna, till något som ser ut så här: 64657266406578616D706C652E636F6D. Ett Javascript avkodar sedan strängen, så att de ser ut som vanligt för användaren. Eftersom de flesta spam-bots inte kör javascript så kan de inte se att det är en emailadress.

Använda en HttpModule för att stoppa spam
Den metod som jag valde att använda hittade jag på Webpronews.com. Den gör om emailadresserna till hexadecimala tecken. Och den gör det på ett väldigt smart sätt eftersom den blandar de hexadecimala tecknen med vanliga, enligt en slumpvis algoritm. I en webbläsare ser adresserna helt vanliga ut, men spamrobotarna kommer få problem. Det fina med att använda en HttpModule är ju att ma inte behöver koda om alla sidor på webbplatsen. Modulen tar hand om detta och applicerar det över hela sajten. För att vara ännu säkrare kan man även kombinera denna teknik med en javascript-variant.

Så här lägger man till en referens till modulen i web.config:

<httpModules>
<add type="EmailSpamModule" name="EmailSpamModule" />
</httpModules>

Själva koden för modulen läggs i App_Code och ser ut så här:

#region Using

using System;
using System.IO;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;

#endregion

/// <summary>
/// Removes whitespace from the webpage.
/// </summary>
public class EmailSpamModule : IHttpModule
{

#region IHttpModule Members

void IHttpModule.Dispose()
{
// Nothing to dispose;
}

void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}

#endregion

void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (app.Request.RawUrl.Contains(".aspx"))
{
app.Response.Filter = new SpamFilter(app.Response.Filter);
}
}

#region Stream filter

private class SpamFilter : Stream
{

public SpamFilter(Stream sink)
{
_sink = sink;
}

private Stream _sink;

#region Properites

public override bool CanRead
{
get { return true; }
}

public override bool CanSeek
{
get { return true; }
}

public override bool CanWrite
{
get { return true; }
}

public override void Flush()
{
_sink.Flush();
}

public override long Length
{
get { return 0; }
}

private long _position;
public override long Position
{
get { return _position; }
set { _position = value; }
}

#endregion

#region Methods

public override int Read(byte[] buffer, int offset, int count)
{
return _sink.Read(buffer, offset, count);
}

public override long Seek(long offset, SeekOrigin origin)
{
return _sink.Seek(offset, origin);
}

public override void SetLength(long value)
{
_sink.SetLength(value);
}

public override void Close()
{
_sink.Close();
}

public override void Write(byte[] buffer, int offset, int count)
{
byte[] data = new byte[count];
Buffer.BlockCopy(buffer, offset, data, 0, count);
string html = System.Text.Encoding.Default.GetString(buffer);

html = EncodeEmails(html);

byte[] outdata = System.Text.Encoding.Default.GetBytes(html);
_sink.Write(outdata, 0, outdata.GetLength(0));
}

private static string EncodeEmails( string html)
{
foreach (Match match in _Regex.Matches(html))
{
html = html.Replace(match.Value, Encode(match.Value));
}
return html;
}

private static Regex _Regex = new Regex("(mailto:|)(\\w+[a-zA-Z0-9.-_]*)@(\\w+).(\\w+)");
private static Random _Random = new Random();

private static string Encode(string value)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < value.Length; i++)
{
if (_Random.Next(2) == 1)
sb.AppendFormat("&#{0};", Convert.ToInt32(value[i]));
else
sb.Append(value[i]);
}

return sb.ToString();
}

#endregion

}

#endregion

}

Jag hoppas att skaparen av detta kod tycker det är okej att jag publicerar den.
In english: I hope that the creator of this code approves that I republish it. If not, just contact me and I will remove it. I also found it here and here.

En prestandavarning
När jag provade på ett riktigt långt html-dokument så fick jag lite oväntade problem. Vet inte riktigt vad det beror på. På kortare sidor fungerar modulen bra.

En bete för bots
Till sist tänkte jag använda metoden för att lägga ut ett bete för spam-botsen. Följande adress har jag skapat endast för denna sidan och jag kommer inte lägga ut den någon annanstans. Får jag ett spam till den adressen så vet jag att en bot har överlistat systemet.

spammeifyoucan@codeodyssey.com

I koden bakom ser denna adress ut som följer, svårt för till och med en spam-bot att fatta.

s&#112;&#97;&#109;m&#101;if&#121;oucan@c&#111;&#100;&#101;o&#100;y&#115;&#115;ey.&#99;o&#109;