301 redirect med Asp.Net

Om du flyttar en webbsida till en ny adress är det bra om den gamla fortfarande fungerar och skickar vidare till den rätta. Det mest kända sättet att göra detta är genom HTML meta-refresh. Men precis som med att använda Javascript har denna metod blivit associerad med spam. Sökmotorerna gillar det inte och det kan få din webbplats att bli borttagen från sökmotorernas register.

Det bästa sättet är att använda en 301 redirect som berättar för webbläsare och sökmotor-spindlar att "sidan är borttagen, här är den nya adressen".

Läs mer på Aspcode.net som visar ett exempel på hur man lägger till det i global.asax.

protected void Application_BeginRequest(Object sender, EventArgs e)
{
string sOldPath = HttpContext.Current.Request.Path.ToLower();
string sPage = "http://www.aspcode.net/articles/" + sOldPath;
Response.Clear();
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location",sPage);
Response.End();
}

Eller som man kan göra på en enskild sida:

private void Page_Load(object sender, System.EventArgs e)
{
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", "http://www.thenewdomain.something/newpage");
}

Steven Hargrove har också skrivit en jättebra artikel som förklarar 301 redirect och hur man använder det på andra serverspråk.

By Jesper Lind

Felsök: Could not find schema ...NetConfiguration/v2.0:configuration

Fick detta felet i VS intellisense, och det tyder på någon misskonfiguration i Visual Studio.

Could not find schema information for the element 'http://schemas.microsoft.com/.NetConfiguration/v2.0:configuration'.

Som en snabb fix bytte jag ut:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

Mot:

<configuration>
<!-- xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"-->

Man kan läsa mer om det här och här.

By Jesper Lind

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;

By Jesper Lind

Klass-fil som genererar lösenord

Här visar jag ett exempel på en bra klass för att generera lösenord. Lösenorden blir 10 tecken långa och skapas av endast tecknen a-z, A-Z, 0-9.

Det är inte jag som utspungligen skrev den men har använt den så länge att jag glömt bort vart den kommer från. Om någon känner igen koden, hör gärna av er så ska jag skriva en referens till skaparen.

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Text;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// Summary description for PasswordGenerator
/// </summary>


public class PasswordGenerator : Page
{
private char[] characterArray;
private Int32 passwordLength = 10;
Random randNum = new Random();

public PasswordGenerator()
{
characterArray = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
}

private char GetRandomCharacter()
{
return this.characterArray[(int)((this.characterArray.GetUpperBound(0) + 1) * randNum.NextDouble())];
}

public string Generate()
{
StringBuilder sb = new StringBuilder();
sb.Capacity = passwordLength;
for (int count = 0; count <= passwordLength - 1; count++)
{
sb.Append(GetRandomCharacter());
}
if ((sb != null))
{
return sb.ToString();
}
return string.Empty;
}
}

By Jesper Lind

Felsök: Request.Form-värdet som identifierats från klienten ... kan vara skadligt

Detta är ett fel som man kan få om man skriver in html-kod i en textruta och postar det med Asp.Net. Det behöver inte ens vara html. .NET-Ramverket känner av ifall man har skrivit större-än eller mindre-än tecknet ("<...>").

Hela felmeddelandet:

Beskrivning: Vid verifiering av begäran identifierades ett indatavärde från klienten som kan vara skadligt. Behandlingen av begäran har avslutats. Det här värdet kan indikera ett försök att kompromettera säkerheten för tillämpningsprogrammet, till exempel en skriptattack över webbplatsen. Du kan inaktivera verifiering av begäran genom att ange validateRequest=false i Page-direktivet eller -konfigurationsavsnittet. Du bör emellertid låta tillämpningsprogrammet kontrollera alla indata i det här fallet.

Undantagsinformation: System.Web.HttpRequestValidationException: Request.Form-värdet som identifierats från klienten (ctl00$mainContentPH$fvFaq$tbFaqBody_sv-SE="...the link &lt;a href="http://www....") kan vara skadligt.

Botemedel
För att stänga av valideringen kan man göra på två sätt.

1. Stänga av i konfigurationen

<system.web>
<pages ValidateRequest="false"/>
</system.web>

2. Stänga av på en enskild sida

<%@ Page ValidateRequest="false" ... %>

En varning för injektion
Valideringen finns där av ett skäl, tänk på att om den stängs av så kan besökarna t ex posta javascript som ser ut så här. Och det är inte ofta man vill...

<script>alert('I am posting some dangerous code')</script>

Mer info finns på Brian Cryer's hemsida.

By Jesper Lind

Felsök: Procedure ... expects parameter ... which was not supplied

Hade en del problem med att luska ut vad detta felmeddelandet berodde på.

Procedure <procedure name> expects parameter <parameter name>, which was not supplied : ADO.NET Error

Till slut hittade jag lösningen på Data Mining Developers.

Exempel av problemet

string ColumnName = null;
//error will be thrown if the variabe ColumnName is null
if(ColumnName ==null)
ColumnName=string.Empty;
SqlParameter param = new SqlParameter("@ColumnName", SqlDbType.NVarChar, 100);
param.Value = ColumnName;
cmd.Parameters.Add(param);
cmd.Execute();

By Jesper Lind

Formatera texter med RegEx

Funktionen för att visa källkod på min sida är rätt simpel och highlightar inte några nyckelord. Den visar bara koden i en grå ruta med ram runt. Min har dessutom vart lite buggig och om man hade flera rutor i samma inlägg som formaterades inte alla rätt.

Nu har jag kodat om det och gjort en funktion som använder Regular Expressions. Tänkte dela med mig av denna här. Koden applicerar HtmlEncode på allt som står mellan [codeblock][/codeblock] för att taggarna ska visas som vanlig text.

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;

public partial class example_RegEx_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string strInput = "This is a simple code-example that will be formatted with a gray background using RegEx.[codeblock]<html><body>Hello world!</body></html>[/codeblock]";
lblOutput.Text = FormatCodeBlock(strInput);
}

public string FormatCodeBlock (string strInput)
{
string strPattern = @"\[codeblock\]([\w\W]*?)\[/codeblock\]";
string strReplace = @"<div style=""background:#efefef;"">$1</div>";

string strResult;

foreach (Match m in Regex.Matches(strInput, strPattern))
{
string strMatch = m.ToString();
strInput = strInput.Replace(strMatch, Server.HtmlEncode(strMatch));
}

strResult = Regex.Replace(strInput, strPattern, strReplace);
return strResult;
}
}

By Jesper Lind