Adding a honeypot to Episerver Forms
I'm aware this is not the first blog post around adding a honeypot to Episerver Forms (see this one by Allan), but the approach is slightly different and based on an old forum question. The main difference is that this implementation adds a custom ActorsExecutingService
allowing us to return a success message even when the honeypot is triggered.
Essentially it works just like any other form component, which you can add from the UI (if you're interested in the component image, you can get it from here):
This adds a hidden checkbox to the form which if checked stops the form being processed but still shows the user a success message. This relies on the fact that spam bots generally fill out all fields in a form whist ignoring the CSS, so checks a checkbox that isn't even visible to users.
It's really pretty easy to achieve this, so let's keep it simple.
First, add the component—it doesn't need any additional properties:
[ImageUrl("~/Resources/Images/honeypotelementblock.svg")]
[ContentType(
DisplayName = "Honeypot",
GroupName = ConstantsFormsUI.FormElementGroup,
Description = "A simple honeypot which should reduce spam sign-ups.",
GUID = "{34ADB085-C6CF-4D5B-93C6-369EE3436F11}")]
public class HoneypotElementBlock : ElementBlockBase {}
Second, add a view for it (by default this should be at ~/Views/Shared/ElementBlocks/HoneypotElementBlock.cshtml
):
@model HoneypotElementBlock
<div style="display:none !important" data-f-element-name="@Model.FormElement.ElementName" data-f-type="choice" id="@Model.FormElement.Guid">
<fieldset>
<label>
<input type="checkbox" name="@Model.FormElement.ElementName" tabindex="-1" autocomplete="off" value="1" data-f-datainput="">
</label>
</fieldset>
</div>
Thirdly, add a custom ActorsExecutingService
. This adds a HasValidHoneypot
method which indicates whether the form has a trigged honeypot, if it does we don't bother to process the form:
public class HoneypotActorsExecutingService : ActorsExecutingService
{
private readonly FormBusinessService _formBusinessService;
public HoneypotActorsExecutingService(FormBusinessService formBusinessService)
{
_formBusinessService = formBusinessService;
}
public override IEnumerable<object> ExecuteActors(Submission submission, FormContainerBlock formContainer,
FormIdentity formIden, HttpRequestBase request, HttpResponseBase response, bool isFormFinalizedSubmission)
{
if (!HasValidHoneypot(submission, formIden))
{
return new object[0];
}
return base.ExecuteActors(submission, formContainer, formIden, request, response,
isFormFinalizedSubmission);
}
/// <summary>
/// Indicates whether the form has a completed honeypot element.
/// </summary>
/// <param name="submission">The submission.</param>
/// <param name="formIdentity">The form identity.</param>
/// <returns><c>true</c> if the form has a valid honeypot element, otherwise <c>false</c></returns>
private bool HasValidHoneypot(Submission submission, FormIdentity formIdentity)
{
var honeypot = _formBusinessService.GetAllInnerFormElementBlocks(formIdentity.GetFormBlock())
.OfType<HoneypotElementBlock>().FirstOrDefault();
// The form doesn't contain a honeypot element, return true
if (honeypot == null)
{
return true;
}
var elementName = (honeypot as IContent)?.ContentLink.GetElementName();
// If for some reason we can't resolve an element name, let's assume
// the submission is valid
if (string.IsNullOrEmpty(elementName))
{
return true;
}
if (!submission.Data.ContainsKey(elementName))
{
return true;
}
var value = submission.Data[elementName];
return string.IsNullOrEmpty(value?.ToString());
}
}
Finally, we need to tell Episerver to use our new HoneypotActorsExecutingService
via an initalization module:
[InitializableModule]
public class DependencyResolverInitialization : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.ConfigurationComplete += (o, e) =>
{
context.Services.AddSingleton<ActorsExecutingService, HoneypotActorsExecutingService>();
};
}
public void Initialize(InitializationEngine context) { }
public void Uninitialize(InitializationEngine context) { }
}
Now you can sit back and watch your spam-free form submissions roll in. 👍
Comments
There are zero comments 😢