Goodbye Aachen, Hello Hamburg

Hamburg, Proximity, Cycos Comments

Cycos

After more than 8 years I´ll leave my current employer Cycos by the end of this year. Starting 1/1/08 I'll work as a Senior Software Developer at Proximity (international website.)

Cycos is my first employer, I started here right after university in 1999. Originally I didn't plan to stay that long here but 5 years at maximum. However, because the work was so diversified and the working atmosphere so prolific, I stayed some years longer. E.g. I was delegated to Munich and San Jose (CA) for several month each, and they paid me the attendances and expenses for both PDC´03 and ´05 in LA. In other words, I can´t accuse myself of being inflexible or inmobil. And it was always fun working at Cycos. I´ll be contended if the working atmosphere at my new job is half as prolific as I got to know it in the past.

However, after 8 years it´s time for a change. Most of my time at Cycos I wrote proprietary software, mainly clients for computer telephony integration. In contrast, at Proximity I will leverage Microsoft products such as MOSS and BizTalk, i.e. areas I don´t have a clue about where I can gain experience. But don´t get me wrong, I´m really looking forward for the new job.

Proximity

And I´m not only changing the employer, but the town as well. I´ll move to Hamburg, which is 500 km away from Würselen/Aachen (for my American friends: that´s about 310 miles). Therefore the search of a new domicile has my highest priority at the moment. I have 18 paid leave days left, and I want to complete my relocation this year. So this blog will stay quite silent the next few weeks. But stay tuned, I´ll keep you informed.

XTOPIA 2007

Community, Conference, Xtopia Comments

Now Germany has its own Mix conference, though here Microsoft calls it Xtopia. It will cover Silverlight, WPF, the Expression suite, and much more. It will take place in Berlin from 10th to 12th of October (if you include the post-conference on Friday).

I'll go to Berlin tomorrow already, and stay until Saturday. My Hotel is the Quality Hotel & Suites Berlin City East. If you want to join me for a beer or two, drop me a line or call me at +49 (173) 285 21 81. Lars, I'm expecting your call.

All Firefox Extensions Gone after upgrading to 2.0.7

Software, Tips 'n Tricks, Firefox Comments

Firefox

Today I upgraded Firefox to 2.0.7, which fixes a flaw in the QuickTime plugin. However, after the upgrade Firefox didn't load any of the extensions I have installed

Fortunately I found this thread in the support forum. Simply delete extensions.ini, extensions.cache, and extensions.rdf from your profile folder. On its next start Firefox will scan for installed extensions and regenerate these files.

I hope this post will be indexed properly so you can find this information faster than me.

Shot myself in the foot

If you have tried to leave a comment on my site in the last two days, you may have noticed that they weren´t accepted. Here is why:

Because the rate of incoming spam decreased dramatically after I implemented the Honeypot CAPTCHA on this site, I wondered if I could really give that solution the credit. Therefore I entered a comment myself to check it. And what happened? Nothing! Instead, that small red asterisk appeared next to the comment field, indicating that nothing was entered. WTF? I did enter some text! Garbage, I admit, but at least a few characters. Then I remembered that I switched the identifiers of the regular comment text box and the honeypot box. Then I had a sneaking suspicion, and I quickly checked post.aspx. My apprehension proved true: I have forgotten to point the RequiredFieldValidor to the other identifier! So as long as you didn't fill in the hidden honeypot, you couldn't post your comment. How stupid is that? The ironic side of the story is, that only comment spammers were able to cross that barrier, just to get marked as spam instantly afterwards.

Anyway, it´s fixed by now, and your comments are welcome (again)!

Honeypot Captcha for Community Server

Comments

A few days ago Phil Haack wrote about Honeypot Captcha:

At the same time, spam bots tend to ignore CSS. For example, if you use CSS to hide a form field (especially via CSS in a separate file), they have a really hard time knowing that the field is not supposed to be visible.

To exploit this, you can create a honeypot form field that should be left blank and then use CSS to hide it from human users, but not bots. When the form is submitted, you check to make sure the value of that form field is blank.

A great idea, which didn’t occur to me before. And Phil wasn’t even the first one with that idea.

I added a “regular” captcha control to my site some time ago, but removed it again after a couple of days. Why burden the innocent commentor? He’s not the problem. The comment spammers are.

So I took the idea of the honeypot and leveraged Community Server’s spam filtering capabilities.

First, I replaced the original Community Server’s WeblogPostCommentForm with my own version by simply copying it to my own assembly. (If you build CS from the SDK, you can also just edit the existing one.)

  1. Added the private member

    private TextBox HoneyPot;
    
  2. Added the property HoneyPotTextBoxId, which references the actual TextBox in the form:

    public string HoneyPotTextBoxId
    {
        get { return ((string)ViewState["HoneyPotTextBoxId"]) ?? string.Empty; }
        set { ViewState["HoneyPotTextBoxId"] = value; }
    }
    
  3. Added following lines to the method AttachChildControls:

    HoneyPot = WeblogControlUtility.Instance().FindControl(this, HoneyPotTextBoxId) as TextBox;
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("\n<script type=\"text/javascript\">");
    sb.AppendLine("document.getElementById(\"" + HoneyPot.ClientID + "\").style.display = \"none\";");
    sb.AppendLine("</script>");
    CSControlUtility.Instance().RegisterStartupScript(HoneyPot, typeof (TextBox), "honeypot", sb.ToString(), false);
    

    You see that the honeypot field is hidden dynamically via JavaScript. No CSS is giving a hint to the spammer that the control will not be visible. Instead, as soon as the page is loaded, the textbox will be hidden programmatically.

  4. Added following lines in Submit_Click right before WeblogPosts.Add is called:

    if (!string.IsNullOrEmpty(HoneyPot.Text)) { 
        EventLogs.Info("Spammer entered \"" + HoneyPot.Text + "\" in the honey pot", "Spam Rules", 0); 
        c.SetExtendedAttribute("GotchaInMyHoneyPot", "trapped"); 
    }
    

    So whenever the honeypot textbox is filled in, the comment gets an extended attribute named GotchaInMyHoneyPot.

Now this new form must be used on the actual page. There’s only one page (per theme), which allows visitors to leave comments, which is post.aspx. Unfortunately, this must be done for every theme.

So open post.aspx and replace the original WeblogPostCommentForm with our new one, add the HoneyPotTextBoxId attribute to the form, and add the textbox. Don’t forget to use the same id for the textbox (this example uses tbBody, which shouldn’t ring a bell for the spammer.) Here’s the modified form declaration from the PaperClip theme:

<tfr:HoneyPotWeblogPostCommentForm runat="server" ValidationGroup="CreateCommentForm" 
    MessageTextBoxId="tbComment" 
    NameTextBoxId="tbName" 
    RememberCheckboxId="chkRemember" 
    SubjectTextBoxId="tbTitle" 
    SubmitButtonId="btnSubmit" 
    UrlTextBoxId="tbUrl" 
    ControlIdsToHideFromRegisteredUsers="RememberWrapper" 
    HoneyPotTextBoxId="tbBody" 
    > 
    <SuccessActions> 
        <CSControl:GoToModifiedUrlAction runat="server" QueryStringModification="CommentPosted=true" TargetLocationModification="commentmessage" /> 
    </SuccessActions> 
    <FormTemplate> 
        <fieldset id="commentform"> 
        <legend><CSControl:ResourceControl runat="server" ResourceName="Weblog_CommentForm_WhatDoYouThink" id="rc_think"/></legend> 
            <p /> 
            <div><CSControl:FormLabel runat="server" ResourceName="Title" LabelForId="tbTitle" /> <em>(<CSControl:ResourceControl runat="server" ResourceName="Required"/>)</em><asp:RequiredFieldValidator runat="server" ErrorMessage="*" ControlToValidate="tbTitle" ValidationGroup="CreateCommentForm" /></div> 
            <div><asp:TextBox id="tbTitle" runat="server" CssClass="smallbox" ValidationGroup="CreateCommentForm" /></div> 
            <p /> 
            <div id="NameTitle" runat="server"><CSControl:FormLabel LabelForId="tbName" runat="server" ResourceName="Weblog_CommentForm_Name" /> <em>(<CSControl:ResourceControl runat="server" ResourceName="Required" />)</em><asp:RequiredFieldValidator runat="server" ErrorMessage="*" ControlToValidate="tbName" ValidationGroup="CreateCommentForm" /></div> 
            <div id="NameDesc" runat="server"><asp:TextBox id="tbName" runat="server" CssClass="smallbox" ValidationGroup="CreateCommentForm" /></div> 
            <p /> 
            <div><CSControl:FormLabel runat="server" LabelForId="tbUrl" ResourceName="Weblog_CommentForm_YourUrl" /> <em>(<CSControl:ResourceControl runat="server" ResourceName="Optional" /></em>)</div> 
            <div><asp:TextBox id="tbUrl" runat="server" CssClass="smallbox" ValidationGroup="CreateCommentForm" /></div> 
            <%-- the honeybot textbox --%> 
            <asp:TextBox id="tbBody" runat="server" CssClass="smallbox" ValidationGroup="CreateCommentForm" /> 
            <p /> 
            <div><CSControl:FormLabel LabelForId="tbComment" runat="server" ResourceName="Weblog_CommentForm_Comments" /> <em>(<CSControl:ResourceControl runat="server" ResourceName="Required" />)</em><asp:RequiredFieldValidator runat="server" ErrorMessage="*" ControlToValidate="tbComment" ValidationGroup="CreateCommentForm" /></div> 
            <div><asp:TextBox id="tbComment" runat="server" Rows="5" Columns="25" TextMode="MultiLine" ValidationGroup="CreateCommentForm" /></div> 
            <asp:PlaceHolder runat="server" id="RememberWrapper"> 
                <p /> 
                <div><asp:CheckBox id="chkRemember" runat="server" Text="Remember Me?" ValidationGroup="CreateCommentForm"></asp:CheckBox></div> 
            </asp:PlaceHolder> 
            <p /> 
            <asp:Button id="btnSubmit" runat="server" Text="Submit" ValidationGroup="CreateCommentForm"></asp:Button> 
        </fieldset> 
    </FormTemplate> 
</tfr:HoneyPotWeblogPostCommentForm>

But that’s only the first half. Now whenever the honeypot field is filled with some text the comment will have an extended attribute GotchaInMyHoneyPot.

The second step is to give the comment “spam points”, which is done by a CS spam rule. Keyvan Nayyeri published a complete tutorial how to write your own spam rules, so I won’t get into details. Here’s the complete code for the new rule:

public class HoneyPotRule : BlogSpamRule { 
    private static readonly Guid _ruleId = new Guid("57E216D4-D100-468d-BB37-1B7A0A103CEF"); 
    private const int _defaultPoints = 10; 

    public override ArrayList GetAvailableSettings() { 
        ArrayList list = new ArrayList(); 
        list.Add(new RuleSetting(_ruleId, "points", "Points", _defaultPoints.ToString())); 
        return list; 
    } 
    
    public override int CalculateSpamScore(WeblogPost weblogPost, CSPostEventArgs e) { 
        if (weblogPost.BlogPostType == BlogPostType.Comment) { 
            if (!String.IsNullOrEmpty(weblogPost.GetExtendedAttribute("GotchaInMyHoneyPot"))) { 
                EventLogs.Info("A spammer fell for the honey pot", "Spam Rules", 0); 
                return Globals.SafeInt(GetSettingValue("points"), _defaultPoints); 
            } 
        } 

        return base.CalculateSpamScore(weblogPost, e); 
    } 

    public override string Name { 
        get { return "HoneyPot"; } 
    } 

    public override string Description { 
        get { return "HoneyPot description"; } 
    } 

    public override Guid RuleID { 
        get { return _ruleId; } 
    } 
}

I’m running this solution for a couple of days now on my site, and it works pretty well. It’s amazing how many spam bots fill each and every textbox they can find. But I admit that a lot of steps are involved in my solution, there’s lot of programming required. However, I decided against publishing an all-inclusive package, because I’d like to add this solution to next release of either the CSMVP CSModules or Community Server Stuff. So either follow my instructions above, wait for an official release, or leverage Phil’s honeypot control which is part of the Subkismet project (not released yet too, but you can already get the sources.)