JSON serializing an object with function parameter
Asked Answered
C

3

14

I have this C# object:

var obj = new {
    username = "andrey",
    callback = "function(self) { return function() {self.doSomething()} (this) }"
}

I need to JSON serialize it to pass to the browser in ajax call. I use JavascriptSerializer, but it serializes to the following JSON:

{"username":"andrey", "callback": "function(self) { return function() {self.doSomething()} (this) }"}

but what I need is:

{"username":"andrey", "callback": function(self) { return function() {self.doSomething()} (this) }}
  • no quotes around function definition.

Right now, when the JSON object gets to the browser and is created, the 'callback' parameter is not a function but a string. Any idea how to fix it, preferably on the server side?

Corri answered 21/3, 2011 at 3:29 Comment(1)
This post talks about this issue but for PHP but I guess you could do something similar in your case.Hermaphroditus
R
7

This behavior is deliberate. JSON should not include anything that is not data -- in your case an executable function. The browser will be opening up to huge security risks if data can come back from a server in JSON format that, when executed, will run arbitrary functions (that can steal info, redirect the user to a malicious site etc.)

Early implementations of JSON rely on the fact that data returned back can be simply executed via eval() to get back an object. However, people almost immediately realized that this opens up huge security risks and have been trying to handle it since. That's why, before the standardized JSON object, people stopped putting raw JSON data into eval() and used JSON parsing libraries instead.

The JSON object will always serialize an object into data only. This is by design. THe standardized JSON format has no way to represent an executable function.

Now, you can easily convert that callback on a browser into a function by passing it through to eval(). However, don't do it. You're just opening yourself up for hacking.

On the server side, modern browsers are designed to prevent this exact thing from happening -- i.e. data being sent from a browser that contains an executable function.

Ridgeling answered 21/3, 2011 at 3:41 Comment(9)
I know eval = evil, but I guess I'll have to resort to it in my case. I have to pass that js method from the server, so whatever way I can do it is fine. I doubt it brings a huge security risk as It's always my server that throws the js method to the page.Corri
In your client code, you can always pass the code string into eval("return " + code) and get back a function. However, please don't do it for your own sake. If a hacker somehow redirects your client's url to a malicious url, and it returns malicious code in that JSON, your client is royally screwed.Ridgeling
How is it different from that hacker redirecting client url's for script files and downloading malicious scripts instead? I'm not arguing, just curious - I'm not too strong on security.Corri
The hacker may not even need to redirect the client's url. For example, data en-route from your server to the client may pass through multiple layers of proxies and other filters. One of them may be compromised by a hacker. The hacker takes your data, junk your callback text string, and replaces with his own malicious code, and sends it to your client. Your client will be executing that malicious code. Now, if it is only data, at least the client will not be executing any code that you didn't write yourself; your programs will just be seeing strange data.Ridgeling
I guess I'll have to take a second look at my architecture. Thanks, I'll think about what you suggest [not to do]Corri
@Andrey, you should really rethink. There shouldn't be difficulty in avoiding sending code fragments to the client that cannot be replaced by client-side code itself. In a connected world, in anything you open up to the outside world, you always have to think about security. Don't regret it later.Ridgeling
@Andrey, in your case, if you cannot implement your functionality by passing a parameter to the client side and switch on that parameter to different functions, and must run arbitrary pieces of code sent from the server, consider dynamically adding a <script> tag which points to pieces of functions on your own server.Ridgeling
Just one usecase, which would be secure and reasonable: Serialize an Json Object for intialization of an server side generated function parameter. E.g $("#foo").DataTable(@SerializedConfig)Buehler
@StephenChung You wrote The hacker takes your data, junk your callback text string, and replaces with his own malicious code, and sends it to your client. Your client will be executing that malicious code. Won't HTTPS be enough to protect against that?Stricker
R
23

I was trying to accomplish something similar. In my case I was using MVC Razor syntax trying to generate a json object with a function passed in using the @<text> syntax.

I was able to get the desired output using the Json.net library (using JsonConvert and JRaw).

Example:

// set the property value using JRaw
var obj = new {
    username = "andrey",
    callback = new JRaw("function(self) { return function() {self.doSomething()} (this) }")
}
// and then serialize using the JsonConvert class
var jsonObj = JsonConvert.SerializeObject(obj);

That should get you the json object with the function (instead of the function in a string).

Post: How to serialize a function to json (using razor @<text>)

Richmound answered 28/5, 2013 at 20:4 Comment(0)
R
7

This behavior is deliberate. JSON should not include anything that is not data -- in your case an executable function. The browser will be opening up to huge security risks if data can come back from a server in JSON format that, when executed, will run arbitrary functions (that can steal info, redirect the user to a malicious site etc.)

Early implementations of JSON rely on the fact that data returned back can be simply executed via eval() to get back an object. However, people almost immediately realized that this opens up huge security risks and have been trying to handle it since. That's why, before the standardized JSON object, people stopped putting raw JSON data into eval() and used JSON parsing libraries instead.

The JSON object will always serialize an object into data only. This is by design. THe standardized JSON format has no way to represent an executable function.

Now, you can easily convert that callback on a browser into a function by passing it through to eval(). However, don't do it. You're just opening yourself up for hacking.

On the server side, modern browsers are designed to prevent this exact thing from happening -- i.e. data being sent from a browser that contains an executable function.

Ridgeling answered 21/3, 2011 at 3:41 Comment(9)
I know eval = evil, but I guess I'll have to resort to it in my case. I have to pass that js method from the server, so whatever way I can do it is fine. I doubt it brings a huge security risk as It's always my server that throws the js method to the page.Corri
In your client code, you can always pass the code string into eval("return " + code) and get back a function. However, please don't do it for your own sake. If a hacker somehow redirects your client's url to a malicious url, and it returns malicious code in that JSON, your client is royally screwed.Ridgeling
How is it different from that hacker redirecting client url's for script files and downloading malicious scripts instead? I'm not arguing, just curious - I'm not too strong on security.Corri
The hacker may not even need to redirect the client's url. For example, data en-route from your server to the client may pass through multiple layers of proxies and other filters. One of them may be compromised by a hacker. The hacker takes your data, junk your callback text string, and replaces with his own malicious code, and sends it to your client. Your client will be executing that malicious code. Now, if it is only data, at least the client will not be executing any code that you didn't write yourself; your programs will just be seeing strange data.Ridgeling
I guess I'll have to take a second look at my architecture. Thanks, I'll think about what you suggest [not to do]Corri
@Andrey, you should really rethink. There shouldn't be difficulty in avoiding sending code fragments to the client that cannot be replaced by client-side code itself. In a connected world, in anything you open up to the outside world, you always have to think about security. Don't regret it later.Ridgeling
@Andrey, in your case, if you cannot implement your functionality by passing a parameter to the client side and switch on that parameter to different functions, and must run arbitrary pieces of code sent from the server, consider dynamically adding a <script> tag which points to pieces of functions on your own server.Ridgeling
Just one usecase, which would be secure and reasonable: Serialize an Json Object for intialization of an server side generated function parameter. E.g $("#foo").DataTable(@SerializedConfig)Buehler
@StephenChung You wrote The hacker takes your data, junk your callback text string, and replaces with his own malicious code, and sends it to your client. Your client will be executing that malicious code. Won't HTTPS be enough to protect against that?Stricker
D
0

You can make use of the constructor of the Function object. See https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Function.

In your json you set the callback property to a string array that describes the Function constructor parameters. Then when the json data has arrived on the client you have to convert the Array to an instance of the Function object.

This way you can have the function implementation details in your back database instead of hardcoded in source code.

const json = '{"username":"andrey","callback":["self","return self.doSomething()"]}';

//Parse the json to an object
const object = JSON.parse(json);

//Convert the callback property from Array to Function
object["callback"] = new Function(...object["callback"]);

//Create a parameter for calling the Function
var self = {
    doSomething() {
        console.log("Do something called");
    }
}

//Call the function
object["callback"](self);
Dogmatics answered 21/4, 2020 at 10:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.