c# - Require encryption of asp.net MVC action parameters -


i have been working on solution allow encrypt , decrypt data in url asp.net mvc project. goal allow encrypt route data and/or querystring data without developer having special in controller or action decrypt , bind data model(s). i've achieved writing own ivalueprovider. value provider searches incoming routedata , query string keys prefix indicates encrypted value. if begin prefix, decrypts them , provide value them. here of in case you're interested tl;dr (right testing i'm using base 64 encoding, not encryption):

public class encrypteddatavalueprovider : ivalueprovider {     string _encryptionprefix = "_enc!";     dictionary<string, string> _values;      public encrypteddatavalueprovider(controllercontext controllercontext)     {         _values = new dictionary<string, string>();          foreach (keyvaluepair<string, object> entry in controllercontext.routedata.values)         {             object val = entry.value;             if (val != null && val string && ((string)val).startswith(_encryptionprefix))             {                 //todo: decrypt substring                 string encryptedval = ((string)val).substring(_encryptionprefix.length);                 string decryptedval = encoding.utf8.getstring(convert.frombase64string(encryptedval));                 _values.add(entry.key, decryptedval);             }         }          //note: .net source code seems indicate accessing qs keys might trigger request validation before want to.          //may have use controllercontext.httpcontext.request.unvalidated @ keys instead. see          //querystringvalueprovider.cs, namevaluecollectionvalueprovider.cs, , unvalidatedrequestvalueswrapper.cs ("m.w.i" microsoft.web.infrastructure request validation built)         foreach (string key in controllercontext.httpcontext.request.querystring)          {             if (key.startswith(_encryptionprefix))             {                 string val = controllercontext.httpcontext.request.querystring[key];                  if (val != null)                 {                     //todo: decrypt val                     string decryptedval = encoding.utf8.getstring(convert.frombase64string(val));                     namevaluecollection decryptedqs = httputility.parsequerystring(decryptedval);                      foreach (string decryptedkey in decryptedqs)                         if (!_values.containskey(decryptedkey))                             _values.add(decryptedkey, decryptedqs[decryptedkey]);                 }             }         }     }      public bool containsprefix(string prefix)     {         return _values.containskey(prefix);     }      public valueproviderresult getvalue(string key)     {         if (_values.containskey(key))             return new valueproviderresult(_values[key], _values[key], system.globalization.cultureinfo.invariantculture);         else             return null;     } } 

this decrypts incoming encrypted (encoded) values fine. need provide way of specifying given action parameter (a.k.a. model) must encrypted. is, don't want accept unencrypted url values. 1 caveat want leave flexible possible. ideally, have attribute apply individual action method parameters, whole action method, or entire controller still have option of accepting unencrypted parameters if needed. i'm picturing this:

[requireencryption] public class mycontroller : controller {     public actionresult action1(string secret)     {     }      [requireencryption(false)]     public actionresult action2(string notsecret)     {      }      public actionresult action3(string secret, [requireencryption(false)]string notsecret)     {     } } 

well, i've achieved (except class/method-level attribute overrides parameter-level attribute), feels ugly hack. it, had create modelbinder, custommodelbinderattribute, , iauthorizationfilter filterattribute. use these replace built-in valueprovidercollection single encrypteddatavalueprovider if doesn't have decrypted value model, model remains null. both attributes use same approach had use custommodelbinderattribute parameter-level attribute , authorization filter action & controller-level attributes.

what don't 1) have have 2 different attributes , it's not quite achieving vision above, 2) modelbinder , authorization filter modifying value providers collection. seems reaching beyond intended scope. there more proper way extend mvc framework achieve this?

public class requireencryptiononallattribute : filterattribute, iauthorizationfilter {     public void onauthorization(authorizationcontext filtercontext)     {         filtercontext.controller.valueprovider = new encrypteddatavalueprovider(filtercontext.controller.controllercontext);     } }  public class requireencryption : custommodelbinderattribute {     public override imodelbinder getbinder()     {         return new encryptionrequiredbinder();     } }  /// <summary> /// binds model using encrypteddatavalueprovider. unencrypted values not bound. /// </summary> public class encryptionrequiredbinder : defaultmodelbinder {     public override object bindmodel(controllercontext controllercontext, modelbindingcontext bindingcontext)     {         bindingcontext.valueprovider = new encrypteddatavalueprovider(controllercontext);          return base.bindmodel(controllercontext, bindingcontext);     } } 


Comments