Hidden Field Encryption in MVC - How to decrypt more than one property on the server side

2

Hello I'm developing a mvc 5 web application and in some cases I need to insert in the view fields @ Html.HiddenFor, only I do not want to show the value to the user if he views the html code, so fine I I was able to encrypt the values that go to the @Html.HiddenFor controls using the class developed by "Adam Tuliper" [credits to it for that!], but the fact is that I can not decorate my Controller side Action method with more than 1 call the extension ValidateAntiModelInjection ("property1") understood?

Follow the class developed by "Adam Tuliper" very useful!

    public class ValidateAntiModelInjection : ActionFilterAttribute
    {
        /// <summary>
        /// The name of the property we are generating a hash for.
        /// </summary>
        private readonly string _propertyName;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName">The name of the property from the form to validate against the hidden encrypted form version.</param>
        public ValidateAntiModelInjection(string propertyName)
        {
            _propertyName = propertyName;
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentException("O valor propertyName deve ser uma string não vazia.");
            }
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //The hidden form field that contains our hash - for ex. CustomerId is rendered as a hidden input  id="_CustomerIdToken"
            string encryptedPropertyName = string.Format("_{0}Token", _propertyName);

            //grab the token
            string hashToken = filterContext.HttpContext.Request.Form[encryptedPropertyName];

            //The encrypted form data MUST be there. We do not allow empty strings otherwise this could give
            //an attack vector in our filter as a means to bypass checks by simply passing in an empty validation token.
            if (string.IsNullOrEmpty(hashToken))
            {
                throw new MissingFieldException(string.Format("O campo de formulário oculto nomeado valor {0} estava faltando. Isto é criado pelos métodos Html.AntiModelInjection. Verifique se o nome usado em seu [ValidateAntiModelInjectionAttribute (\"!AQUI!\")] Corresponde ao nome do campo utilizado no método Html.AntiModelInjection. Se este atributo é utilizado em um método de controlador que se entende por HttpGet, então o valor forma que ainda não existe. Este atributo é para ser utilizado em métodos do controlador acessados via HttpPost.", encryptedPropertyName));
            }


            //Get the plain text value
            string formValue = filterContext.HttpContext.Request.Form[_propertyName];

            //Plain text must be available to compare.
            if (string.IsNullOrEmpty(formValue))
            {
                throw new MissingFieldException(string.Format("O valor de {0} estava faltando. Se este atributo é utilizado em um método de controlador que se entende por HttpGet, então o valor forma que ainda não existe. Este atributo é para ser utilizado em métodos do controlador acessados via HttpPost.", _propertyName));
            }

            //Now hash the 'plain text' version so we can compare to the hash originally created by Html.AntiModelInjectionFor
            string hashedFormValue = FormsAuthentication.HashPasswordForStoringInConfigFile(formValue, "SHA1");

            //And compare
            if (string.Compare(hashedFormValue, hashToken, false, CultureInfo.InvariantCulture) != 0)
            {
                throw new HttpAntiModelInjectionException(string.Format("Validação de segurança falhou para {0}. É possível que os dados foram alterados como o valor original utilizado para criar o campo de formulário não coincide com o valor da propriedade corrente para este campo.", _propertyName));
            }

            filterContext.HttpContext.Trace.Write("(Logging Filter)Action Executing: " +
                filterContext.ActionDescriptor.ActionName);

            base.OnActionExecuting(filterContext);
        }  
    }

In short, I'm not sure how to change the Extender Builder to accept more Propertys, not just one.

I can not use

[ValidateAntiModelInjection("property1")]
[ValidateAntiModelInjection("property2")]
public ActionResult (MinhaModelDTO Model) 
{...}

because of the repeated extension error! but I could use it like this:

[ValidateAntiModelInjection("property1","property2")]
public ActionResult (MinhaModelDTO Model) 
{...}

Or something like that, do not you think? Could someone lead the way to change the class?

    
asked by anonymous 22.09.2015 / 22:42

1 answer

0

I'll put the code for the second form:

[ValidateAntiModelInjection("property1","property2")]
public ActionResult (MinhaModelDTO Model) 
{...}

It looks like this:

public class ValidateAntiModelInjection : ActionFilterAttribute
{
    /// <summary>
    /// The name of the property we are generating a hash for.
    /// </summary>
    private readonly string[] _properties;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="propertyName">The name of the property from the form to validate against the hidden encrypted form version.</param>
    public ValidateAntiModelInjection(params string[] properties)
    {
        foreach (var property in properties {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentException("O valor propertyName deve ser uma string não vazia.");
            }
        }

        _properties = properties;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //The hidden form field that contains our hash - for ex. CustomerId is rendered as a hidden input  id="_CustomerIdToken"
        foreach (var _propertyName in _properties) {
            string encryptedPropertyName = string.Format("_{0}Token", _propertyName);

            //grab the token
            string hashToken = filterContext.HttpContext.Request.Form[encryptedPropertyName];

            //The encrypted form data MUST be there. We do not allow empty strings otherwise this could give
            //an attack vector in our filter as a means to bypass checks by simply passing in an empty validation token.
            if (string.IsNullOrEmpty(hashToken))
            {
                throw new MissingFieldException(string.Format("O campo de formulário oculto nomeado valor {0} estava faltando. Isto é criado pelos métodos Html.AntiModelInjection. Verifique se o nome usado em seu [ValidateAntiModelInjectionAttribute (\"!AQUI!\")] Corresponde ao nome do campo utilizado no método Html.AntiModelInjection. Se este atributo é utilizado em um método de controlador que se entende por HttpGet, então o valor forma que ainda não existe. Este atributo é para ser utilizado em métodos do controlador acessados via HttpPost.", encryptedPropertyName));
            }


            //Get the plain text value
            string formValue = filterContext.HttpContext.Request.Form[_propertyName];

            //Plain text must be available to compare.
            if (string.IsNullOrEmpty(formValue))
            {
                throw new MissingFieldException(string.Format("O valor de {0} estava faltando. Se este atributo é utilizado em um método de controlador que se entende por HttpGet, então o valor forma que ainda não existe. Este atributo é para ser utilizado em métodos do controlador acessados via HttpPost.", _propertyName));
            }

            //Now hash the 'plain text' version so we can compare to the hash originally created by Html.AntiModelInjectionFor
            string hashedFormValue = FormsAuthentication.HashPasswordForStoringInConfigFile(formValue, "SHA1");

            //And compare
            if (string.Compare(hashedFormValue, hashToken, false, CultureInfo.InvariantCulture) != 0)
            {
                throw new HttpAntiModelInjectionException(string.Format("Validação de segurança falhou para {0}. É possível que os dados foram alterados como o valor original utilizado para criar o campo de formulário não coincide com o valor da propriedade corrente para este campo.", _propertyName));
            }

            filterContext.HttpContext.Trace.Write("(Logging Filter)Action Executing: " +
            filterContext.ActionDescriptor.ActionName);

            base.OnActionExecuting(filterContext);
        }  
    }
}
    
22.09.2015 / 23:10