I'm writing a custom ValueProvider that will find encrypted values in the request and decrypt them so they can be used during Model Binding phase.
I seem to have most of the process done, I can decrypt the values and registered the value provider with the framework.
The decrypted string is actually a json string, a simple parsing to Dictionary<string,string> will allow me to provide values for the first level.
Now, that's the hardest thing I've found online, but what if I have a nested object?? how would I go for creating a value provider for it??
P.S: still need to have the ContainsPrefix function to work properly.
Have a cool signature? hit me up and I might use it.
Jul 12, 2017 10:17 PM
(This post was last modified: Jul 12, 2017 10:34 PM by ShadowCloudGH.)
(Jul 12, 2017 06:39 AM)severos Wrote: I'm writing a custom ValueProvider that will find encrypted values in the request and decrypt them so they can be used during Model Binding phase.
I seem to have most of the process done, I can decrypt the values and registered the value provider with the framework.
The decrypted string is actually a json string, a simple parsing to Dictionary<string,string> will allow me to provide values for the first level.
Now, that's the hardest thing I've found online, but what if I have a nested object?? how would I go for creating a value provider for it??
P.S: still need to have the ContainsPrefix function to work properly.
I might be oversimplifying the issue here, but can't you just serialize the object to JSON? That's what an AJAX request would most likely do anyway?
Have a look at this for some background:
http://haacked.com/archive/2011/06/30/wh...nder.aspx/
EDIT:
I just read some of your other posts and with the context for this question, might I suggest a slightly different approach? Create a custom actionFilter to do the decryption for you :
PHP Code
<?php public class CustomFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { // This method is executed before calling the action // and here you have access to the route data: var foo = filterContext.RouteData.Values["foo"]; // TODO: use the routeData to decrypt the values before it's passed to the controller base.OnActionExecuting(filterContext); } }
And if you don't want to decorate all your controller actions with this action filter register it as a global in your global.asax's Application_Start event
like this;
GlobalFilters.Filters.Add(new CustomFilterAttribute ()); // ADDED
RegisterGlobalFilters(GlobalFilters.Filters);
This was mostly taken from this as a source >>
https://stackoverflow.com/questions/4104...-is-called
(Jul 12, 2017 10:17 PM)ShadowCloudGH Wrote: I might be oversimplifying the issue here, but can't you just serialize the object to JSON? That's what an AJAX request would most likely do anyway?
Have a look at this for some background:
http://haacked.com/archive/2011/06/30/wh...nder.aspx/
Actually what you mean is deserialize, I'm reading the request not writing it.
Problem is that I'm writing middleware, so I have no idea what's the output type required, I could deserialize JSON to dictionary as I said earlier, but then what about nested objects? consider the following:
Code {"field1":["value1","value2"], "field2":{"inner1":1, "inner2":2}}
how would I write a value provider for that???? I have a strong feeling that I'm missing some key stuff related to ContainsPrefix function and how model binder deals with nested objects.
Have a cool signature? hit me up and I might use it.
(Jul 12, 2017 10:48 PM)severos Wrote: Actually what you mean is deserialize, I'm reading the request not writing it.
Problem is that I'm writing middleware, so I have no idea what's the output type required, I could deserialize JSON to dictionary as I said earlier, but then what about nested objects? consider the following:
Code {"field1":["value1","value2"], "field2":{"inner1":1, "inner2":2}}
how would I write a value provider for that???? I have a strong feeling that I'm missing some key stuff related to ContainsPrefix function and how model binder deals with nested objects.
Have you seen the updated answer?
This will be the decrypted result from the actionFilter
"field2":{"inner1":1, "inner2":2}
So just deserialize these into JSON? I presume that once you have field2 you'll know what you actually expected in terms of incoming values?
Jul 12, 2017 10:52 PM
(This post was last modified: Jul 12, 2017 10:55 PM by severos.)
Update:
Nope, using action filters won't work, you're assuming I have the value of Foo and want to decrypt it, in my case I have an encrypted object in the body, so the context doesn't even know there's something called Foo, and it'll be too late for ValueProvider or ModelBinder to kick in during ActionFilter.
(Jul 12, 2017 10:52 PM)ShadowCloudGH Wrote: Have you seen the updated answer?
This will be the decrypted result from the actionFilter
"field2":{"inner1":1, "inner2":2}
So just deserialize these into JSON? I presume that once you have field2 you'll know what you actually expected in terms of incoming values?
Just added an update.
And no, just because I know there's something called field2 doesn't mean I know what's inside, I'm writing middleware to be consumed by others, so I have no idea what's the types they're going to use, and I don't want to limit them.
Have a cool signature? hit me up and I might use it.
(Jul 12, 2017 10:52 PM)severos Wrote: Update:
Nope, using action filters won't work, you're assuming I have the value of Foo and want to decrypt it, in my case I have an encrypted object in the body, so the context doesn't even know there's something called Foo, and it'll be too late for ValueProvider or ModelBinder to kick in during ActionFilter.
Just added an update.
And no, just because I know there's something called field2 doesn't mean I know what's inside, I'm writing middleware to be consumed by others, so I have no idea what's the types they're going to use, and I don't want to limit them.
Uhm... Foo was really just the layman's example, there's no reason you can't loop through all the values in the RouteValueDictionary?
The only requirement from the consuming code would be to expect a JSON string rather than an object? Apart from that you don't need to limit the objects or the levels of nesting?
(Jul 12, 2017 11:07 PM)ShadowCloudGH Wrote: Uhm... Foo was really just the layman's example, there's no reason you can't loop through all the values in the RouteValueDictionary?
The only requirement from the consuming code would be to expect a JSON string rather than an object? Apart from that you don't need to limit the objects or the levels of nesting?
Not really, you can loop through RouteValueDictionary only if it had the data, but in my case, it has no idea what the hell is that string in body is because it's encrypted. And the one doing decryption is the ValueProvider.
I've spent quite few days looking for other work arounds, the only thing that I can see it working is if I can alter the request body before it gets read by the MVC framework, but I believe rewriting the stream will break the whole request, and I'm not really sure it'll allow me to do it.
Have a cool signature? hit me up and I might use it.
Jul 13, 2017 12:52 AM
(This post was last modified: Jul 13, 2017 12:52 AM by ShadowCloudGH.)
(Jul 12, 2017 11:13 PM)severos Wrote: Not really, you can loop through RouteValueDictionary only if it had the data, but in my case, it has no idea what the hell is that string in body is because it's encrypted. And the one doing decryption is the ValueProvider.
I've spent quite few days looking for other work arounds, the only thing that I can see it working is if I can alter the request body before it gets read by the MVC framework, but I believe rewriting the stream will break the whole request, and I'm not really sure it'll allow me to do it.
I'm not following...
1. (Encrypted)Request received by Managed Pipeline (IIS stuff before your code)
2. The encrypted request is received by the ActionFilter
3. In the actionfilter loop through the RouteValueDictionary and decrypt all values.
4. Serialize all decrypted values to JSON (To deal with nested objects)
5. Controllers now have decrypted JSON
6. YAY
If this still doesn't work for you, then creating a managed handler and injecting into your managed pipeline might be what you are looking for. This will allow you to decrypt earlier in the lifecycle, but won't solve your nested object issue.
1- Pipeline isn't IIS, I'm using .Net Core, but that's not really relevant much here.
2- The encrypted part is the body of request not the route values, also it's fully encrypted object - the whole thing is encrypted not just the values of fields - and it's encrypted as a whole not every part alone.
3- The whole process should be invisible to controllers, developers will still want to write it as they used to:
PHP Code
<?php class MyController : Controller { public string SomeAction(SomeClassHere s) { //s was actually encrypted in the body, but value provider decrypted it and passed the value to model binder. } }
Have a cool signature? hit me up and I might use it.
DO NOT DEAL WITH THIS MEMBER
Jul 13, 2017 07:41 AM
(This post was last modified: Jul 13, 2017 07:57 AM by Ninja.)
(Jul 12, 2017 10:52 PM)severos Wrote: so the context doesn't even know there's something called Foo
Let it know then.
Buffer the JSON string with a type name or something to let it know what it is. Or, if you use public/private key encryption you can just decrypt it using the private key and get the clean JSON.
EDIT: If you are inheriting from like a defaut value provider can't you just decrypt the JSON and then call the base method?
(Jul 13, 2017 07:41 AM)Ninja Wrote: Let it know then.
Buffer the JSON string with a type name or something to let it know what it is. Or, if you use public/private key encryption you can just decrypt it using the private key and get the clean JSON.
EDIT: If you are inheriting from like a defaut value provider can't you just decrypt the JSON and then call the base method?
Wow... that didn't come to my mind, I'll see if there's a default value provider and inherit it.
Have a cool signature? hit me up and I might use it.
DO NOT DEAL WITH THIS MEMBER
Jul 13, 2017 08:13 PM
(This post was last modified: Jul 13, 2017 08:13 PM by Ninja.)
(Jul 13, 2017 11:57 AM)severos Wrote: Wow... that didn't come to my mind, I'll see if there's a default value provider and inherit it.
Long problem solving frustrations can make you blind to many obvious solutions. So did you manage to get it to work? I am pretty interested in the solution.
Well, there doesn't seem to be a default value provider, however there are couple providers (Form, JQuery form, Query string, Route) but no idea which one should I use, also I'm not sure they'll work as intended since they have their own way of accessing the data through actionContext. I'll be taking a look at their sources to see what they have, might inspire me for the solution.
Have a cool signature? hit me up and I might use it.
Jul 14, 2017 08:48 AM
(This post was last modified: Jul 14, 2017 08:49 AM by Snoweyes.)
(Jul 13, 2017 10:09 PM)severos Wrote: Well, there doesn't seem to be a default value provider, however there are couple providers (Form, JQuery form, Query string, Route) but no idea which one should I use, also I'm not sure they'll work as intended since they have their own way of accessing the data through actionContext. I'll be taking a look at their sources to see what they have, might inspire me for the solution.
Can't you just create a modelbinder which reads the data from your source (for example the body of the request).
In this example i read the body and convert it to a string, but you can ofcourse do anything you want.
Example model binder:
PHP Code
<?php public class MyCustomModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { var request = bindingContext.HttpContext.Request; var data = new StreamReader(request.Body).ReadToEnd();
bindingContext.Result = ModelBindingResult.Success(data);
return Task.CompletedTask; } }
Usage:
PHP Code
<?php [HttpPost] public IEnumerable<string> ReadData([ModelBinder(BinderType = typeof(MyCustomModelBinder))] string data) { return Enumerable.Empty<string>(); }
Keep in mind the above code example is made with .NET Core.
DO NOT DEAL WITH THIS MEMBER
Jul 14, 2017 08:53 AM
(This post was last modified: Jul 14, 2017 08:53 AM by Ninja.)
(Jul 14, 2017 08:48 AM)Snoweyes Wrote: Can't you just create a modelbinder which reads the data from your source (for example the body of the request).
In this example i read the body and convert it to a string, but you can ofcourse do anything you want.
Example model binder:
PHP Code
<?php public class MyCustomModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { var request = bindingContext.HttpContext.Request; var data = new StreamReader(request.Body).ReadToEnd();
bindingContext.Result = ModelBindingResult.Success(data);
return Task.CompletedTask; } }
Usage:
PHP Code
<?php [HttpPost] public IEnumerable<string> ReadData([ModelBinder(BinderType = typeof(MyCustomModelBinder))] string data) { return Enumerable.Empty<string>(); }
Keep in mind the above code example is made with .NET Core.
He wants everything done under the hood for him, so the developer still use the controllers as normal, while under the hood the encryption and stuff is handled.
|