8

How can I pass the encrypted id in ActionLink. This is what I have written in my view:

@model IEnumerable<forumAPP.tblTechnology>
@foreach (var item in Model)
{
string techName=item.TechName;
@Html.ActionLink(techName, "Details","Home", new { TopicID = item.TechID },null) // Here I would like to encrypt the TopicID
<br />
<br />
@Html.DisplayFor(modelItem => item.TechDesc)
}
live-love
  • 48,840
  • 22
  • 240
  • 204
Developer
  • 8,390
  • 41
  • 129
  • 238
  • What kind of encryption so you want to use? – Forty-Two Feb 08 '13 at 13:46
  • Any kind of encryption instead of transferring page as `http://localhost:1931/Home/Details?TopicID=1` I would like to have `http://localhost:1931/Home/Details?TopicID=Ek7vP1YwVhc=` – Developer Feb 08 '13 at 13:54

3 Answers3

20

Here are a couple of simple methods you can use to encode/decode. The encoded value is not secure, and as you can see, decoding it is trivial. If your goal is to obfuscate the id, this will work. If you need to secure it, you should take a different approach.

public string Encode( string encodeMe )
{
    byte[] encoded = System.Text.Encoding.UTF8.GetBytes( encodeMe );
    return Convert.ToBase64String( encoded );
}

public static string Decode( string decodeMe )
{
    byte[] encoded = Convert.FromBase64String( decodeMe );
    return System.Text.Encoding.UTF8.GetString( encoded );
}

So you could place these methods in your controller, and pass the encoded TechId to the view with viewBag

int techId = 1;
var encoded = Encode(id.ToString());
ViewBag.Encoded = encoded;

And then to use it in your link

@Html.ActionLink(techName, "Details","Home", new { TopicID = ViewBag.Encoded },null)

(Though, you should really consider using a view model. ViewBag, while a convienent and easy way to pass data to the view, is not considered to be best practice. Becoming comfortable with view models and strongly typed views will make your mvc life much easier in the future. Not to mention, produce cleaner and more maintainable code for those that follow you.)

Forty-Two
  • 7,535
  • 2
  • 37
  • 54
  • 3
    There's a problem with this code I think, Convert.ToBase64String may generate ( + ) sign (I maybe / too I;m not sure) those signs cause problems in URL – Dabbas Mar 16 '16 at 23:10
3

Add A Folder with two classes

Class 1 : EncryptedActionParameterAttribute

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Web;
using System.Web.Mvc;

namespace MVCInvoicClient.Extensions
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class EncryptedActionParameterAttribute : ActionFilterAttribute
    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {

            Dictionary<string, object> decryptedParameters = new Dictionary<string, object>();
            if (HttpContext.Current.Request.QueryString.Get("q") != null)
            {
                string encryptedQueryString = HttpContext.Current.Request.QueryString.Get("q");
                string decrptedString = Decrypt(encryptedQueryString.ToString());
                string[] paramsArrs = decrptedString.Split('?');

                for (int i = 0; i < paramsArrs.Length; i++)
                {
                    string[] paramArr = paramsArrs[i].Split('=');
                    decryptedParameters.Add(paramArr[0], Convert.ToInt32(paramArr[1]));
                }
            }
            for (int i = 0; i < decryptedParameters.Count; i++)
            {
                filterContext.ActionParameters[decryptedParameters.Keys.ElementAt(i)] = decryptedParameters.Values.ElementAt(i);
            }
            base.OnActionExecuting(filterContext);

        }

        private string Decrypt(string encryptedText)
        {

                string key = "jdsg432387#";
                byte[] DecryptKey = { };
                byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
                byte[] inputByte = new byte[encryptedText.Length];

                DecryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                inputByte = Convert.FromBase64String(encryptedText);
                MemoryStream ms = new MemoryStream();
                CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(DecryptKey, IV), CryptoStreamMode.Write);
                cs.Write(inputByte, 0, inputByte.Length);
                cs.FlushFinalBlock();
                System.Text.Encoding encoding = System.Text.Encoding.UTF8;
                return encoding.GetString(ms.ToArray());
        }

    }
}

Class 2 : MyExtensions

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace MVCInvoicClient.Extensions
{
    public static class MyExtensions
    {
        public static MvcHtmlString EncodedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
        {
            string queryString = string.Empty;
            string htmlAttributesString = string.Empty;
            if (routeValues != null)
            {
                RouteValueDictionary d = new RouteValueDictionary(routeValues);
                for (int i = 0; i < d.Keys.Count; i++)
                {
                    if (i > 0)
                    {
                        queryString += "?";
                    }
                    queryString += d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i);
                }
            }

            if (htmlAttributes != null)
            {
                RouteValueDictionary d = new RouteValueDictionary(htmlAttributes);
                for (int i = 0; i < d.Keys.Count; i++)
                {
                    htmlAttributesString += " " + d.Keys.ElementAt(i) + "=" + d.Values.ElementAt(i);
                }
            }

            //<a href="/Answer?questionId=14">What is Entity Framework??</a>
            StringBuilder ancor = new StringBuilder();
            ancor.Append("<a ");
            if (htmlAttributesString != string.Empty)
            {
                ancor.Append(htmlAttributesString);
            }
            ancor.Append(" href='");
            if (controllerName != string.Empty)
            {
                ancor.Append("/" + controllerName);
            }

            if (actionName != "Index")
            {
                ancor.Append("/" + actionName);
            }
            if (queryString != string.Empty)
            {
                ancor.Append("?q=" + Encrypt(queryString));
            }
            ancor.Append("'");
            ancor.Append(">");
            ancor.Append(linkText);
            ancor.Append("</a>");
            return new MvcHtmlString(ancor.ToString());
        }

        private static string Encrypt(string plainText)
        {
            string key = "jdsg432387#";
            byte[] EncryptKey = { };
            byte[] IV = { 55, 34, 87, 64, 87, 195, 54, 21 };
            EncryptKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByte = Encoding.UTF8.GetBytes(plainText);
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, des.CreateEncryptor(EncryptKey, IV), CryptoStreamMode.Write);
            cStream.Write(inputByte, 0, inputByte.Length);
            cStream.FlushFinalBlock();
            return Convert.ToBase64String(mStream.ToArray());
        }               
    }
}

Controller

Add this line above the controller class Example for your Index
 [EncryptedActionParameter]

In your View

 @Html.EncodedActionLink("Download Invoice", "FileDownload","DataFiles", new { id = item.DataFilesID }, null)

add a using statement

@using MVCInvoicClient.Extensions
danopz
  • 3,310
  • 5
  • 31
  • 42
wesley7
  • 101
  • 3
  • You should really use `HttpUtility.ParseQueryString` ([documentation](https://msdn.microsoft.com/en-us/library/ms150046.aspx)) instead of parsing it manually. – Quentin S. Sep 13 '16 at 08:45
  • In the attribute I would recommend changing the following make it work for any parameter type instead of just int: after string[] paramsArrs = decrptedString.Split('?'); put the following line: var paramInfos = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.GetParameters(); and in the for loop i would do this: var paramArr = paramArrs[i].Split('='); var paramInfo = paramInfos.First(x => x.Name == paramArr[0]); var paramType = paramInfo.ParameterType; decryptedParameters.Add(paramArr[0], Convert.ChangeType(paramArr[1], paramType)); – Michael Rentmeister Apr 17 '17 at 16:14
0

I came across this while looking for a secure method of doing this. In case someone else wishes to do this securely, you can use the MvcSerializer (I found it in the MVC futures 3 project, I am unsure whether it is included in MVC 4). For example:

(new MvcSerializer()).Serialize(<Your data here>, SerializationMode.EncryptedAndSigned)

And then to reverse the process...

(new MvcSerializer()).Deserialize(<Serialized data here>, SerializationMode.EncryptedAndSigned)

This is great because without any extra effort it encrypts and signs the data. The futures project also includes some attributes to make this happen automatically during model binding.

major-mann
  • 2,602
  • 22
  • 37