WFFM and secure Fields

Created: 19 Oct 2016, last update: 24 Jun 2017

Web Forms For Marketers and secure Fields

How secure is WFFM for sensitive data like passwords, credit cards or personal data? Where is my data? It depends on the exact version. For this Article we use several versions to compare. Mostly is based on Sitecore 8.1 update-3 with the matching WFFM version. In order to prevent data leaks, know where the WFFM data is.

Locations where the Data may be unencrypted stored and accessible:

  • MongoDB Forms collection
  • MongoDB Interactions collection
  • Event Queue (Core database)
  • Submit Queue (Data folder)

MongoDB Forms collection
Since Sitecore 7.5 default the data is stored in the analytics MongoDB in the Forms collection. This data is accessible from the CMS, Forms reports. Depend on the version it is also possible to use a SQL database. Depend on the version there is a “Save” item field on each form field. On form fields types like the creditcard is this default off, and it is not stored. But pay attention on Sitecore 7.5 in this version all fields are stored there is not a “save” item field. In older versions for Sitecore 6 the value for secure fields like creditcard and password are marked with “secure:<schidden></schidden>” For Sitecore 7.5 you can create your own AnalyticsDataProvider and filter on the <schidden> to avoid storage of secure fields. Ask me if you need this.

WFFM secure fieldThe save Field on a WFFM Field Item.

MongoDB Interactions collection
If on the WFFM form the Form Dropout Tracking is enabled the Field values and also values that fail on validation are stored in the Interaction. This data is accessible through dropout rapportage when the Save Field is not enabled. Same as for the Forms collection on older versions the behavior is different and a possible security issue.

Event Queue
On scaled environments where the content delivery servers have no access to the master database. You need something to transfer the form field values to the CMS server for processing the remote save actions on the CMS server. Since Sitecore Web Forms for Marketers 8.0 rev.150224 (Update-2) The Sitecore EventQueue is used, on older versions the "remoteWfmService" web-service is used.

In the Event Queue all fields are store for example the default creditcard field data is stored as plain text: There is also some deprecated logic somethings like secure:<schidden>4111111111111111<\/schidden> for MVC or “secure:VISA:<schidden>4111111111111111<\/schidden>” Webforms. This data is accessible for the Database administrators (DBAs) and possible also for Sitecore admins through the Sql Shell tool /sitecore/admin/SqlShell.aspx and possible available in Database backups. There is a Task that run default every 4 hour and remove all records older than 1 day.

Submit Queue
In the Data folder there is a folder “Submit Queue” this is used as the MongoDB is offline. When Sitecore is running normal this is empty, but when the MongoDB is unavailable this folder is used as Queue. So far I see the default password and creditcard field are not stored as plain text. But my own implementation of a secure field, see below is in plain text. Maybe some work to do there.

Create your own secure form-fields
Depend of your solution is on MVC or ASP.NET Web Forms you need at least the corresponding variant. You need to return a ControlResult:

ControlResult(string fieldId, string fieldName, object value, string parameters, bool secure = false)

The ControlResult has a boolean named secure, set it true and your form-field is secure, means not stored in the MongoDB.
Before Sitecore 8 there was not a secure setting, for older version you need to format the value like this:
Sitecore.StringExtensions.StringExtensions.FormatWith("secure:<schidden>{0}</schidden>", (object)this.textbox.Text)

Code sample
See this example of a Secure Single lined text.

MVC

using Sitecore.Forms.Mvc.Validators;
using System.ComponentModel.DataAnnotations;
using Sitecore.WFFM.Abstractions.Actions; using Sitecore.Forms.Mvc.ViewModels.Fields; namespace jbl.WFFMMVCFields {     public class SecureSingleLineTextField : SingleLineTextField     {         [DynamicStringLength("MinLength", "MaxLength", ErrorMessage = "The {0} field must be a string with a minimum length of {1} and a maximum length of {2}.")]         [DataType(DataType.Text)]         public override string Value { get; set; }         public override void Initialize()         {             if (this.MaxLength != 0)                 return;             this.MaxLength = 256;         }         public override ControlResult GetResult()         {             return new ControlResult(this.FieldItemId, this.Title, (object)this.Value, (string)null, true);         }         public override void SetValueFromQuery(string valueFromQuery)         {         }     } }

Web Forms

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.StringExtensions;
using Sitecore.Form.Core.Attributes;
using Sitecore.Form.Core.Visual;
using Sitecore.Form.Web.UI.Controls;
using Sitecore.WFFM.Abstractions.Actions;

namespace jbl.Controls
{
    [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
    public class SecureSingleLineText : RegexInputControl
    {
        private static readonly string baseCssClassName = "scfSingleLineTextBorder";

        [VisualCategory("Validation")]
        [VisualProperty("Maximum Length:", 2000)]
        [DefaultValue(256)]
        public int MaxLength
        {
            get
            {
                return this.textbox.MaxLength;
            }
            set
            {
                this.textbox.MaxLength = value;
            }
        }

        [VisualProperty("Minimum Length:", 1000)]
        [DefaultValue(0)]
        [VisualCategory("Validation")]
        public int MinLength { get; set; }

        [DefaultValue("scfSingleLineTextBorder")]
        [VisualProperty("CSS Class:", 600)]
        [VisualFieldType(typeof(CssClassField))]
        public new string CssClass
        {
            get
            {
                return base.CssClass;
            }
            set
            {
                base.CssClass = value;
            }
        }

        public SecureSingleLineText(HtmlTextWriterTag tag)
            : base(tag)
        {
            this.MaxLength = 256;
            this.MinLength = 0;
            this.CssClass = SecureSingleLineText.baseCssClassName;
        }

        public SecureSingleLineText()
            : this(HtmlTextWriterTag.Div)
        {
        }

        public override ControlResult Result
        {
            get
            {
                return new ControlResult(this.Title, (object)this.textbox.Text, StringExtensions.FormatWith("secure:{0}", (object)this.textbox.Text))
                {
                  Secure = true
                };
            }
        }
        protected override void OnInit(EventArgs e)
        {
            this.textbox.CssClass = "scfSingleLineTextBox";
            this.help.CssClass = "scfSingleLineTextUsefulInfo";
            this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
            this.title.CssClass = "scfSingleLineTextLabel";
            this.textbox.TextMode = TextBoxMode.SingleLine;
            this.Controls.AddAt(0, (Control)this.generalPanel);
            this.Controls.AddAt(0, (Control)this.title);
            this.generalPanel.Controls.AddAt(0, (Control)this.help);
            this.generalPanel.Controls.AddAt(0, (Control)this.textbox);
        }
    }
}

And Create a Sitecore Item for the Field Type, copy the Single Line text item and adjust, fill in the correct Assembly, Class and MVC type.