codefactor
codefactor

Reputation: 1656

Is it necessary to validate or escape the jsonp callback string

I have a file called action.php that will do some action. I want to expose this as either a plain JSON or JSONP output. The user will call this using a URL like this:

action.php?jsonp=callback

In my action.php I am doing something like this

$jsonp = isset $_GET["jsonp"] ? $_GET["jsonp"] : false;
$output = execute_action();
if ($jsonp) {
   header('Content-Type: application/javascript');
   printf("%s(%s)", $jsonp, json_encode($output));
} else {
   header('Content-Type: application/json');
   echo json_encode($output);
}

But this seems unsafe to me. Should I validate or escape the jsonp callback parameter if it is passed in? If so, what situation would this protect against, and how should I do it in PHP?

Let's assume that this action.php is exposed to the internet as a service for any website to use (including my own).

Edit: For clarity my question has 2 parts:

  1. Your opinion on the importance of protecting a hypothetical 3rd party site from harmful jsonp injections

  2. Now supposing I wanted to protect 3rd party sites using my service, should I validate the jsonp parameter (i.e. maybe only allow certain characters?), or should I escape the jsonp output (if so what php function should I use?)

For me to mark the answer as accepted, I would like some more input on both of these questions.

Upvotes: 3

Views: 1642

Answers (2)

jillro
jillro

Reputation: 4569

  • Since users only receive data they have sent themselves, there is not real risk of persistent injection. However, an attacker could still create a malicious link and make a client click on it, to execute code on client machine. To avoid the possibility for an attacker to create those malicious links (that would permit for example to steal cookies from your domain), you have to escape or validate the callback parameter.

  • You have to chose if you just validate it or escape it. In my opinion, escaping does not make real sense. Usually we escape HTML entities in order to protect from attacks and to allow HTML characters like '<' or '>' to be displayed correctly. But in this case, there is no reason a honest user would send characters needing to be escaped... So validation is enough and makes more sense.

What to do ?

A safer way to proceed if you do not want to have to think about injection problem would be to set a fixed name for the function and let the user implement it. Just try to avoid name conflict.

You can also validate the callback value. It has to be a valid javascript function name. You can use PHP function preg_match() http://de3.php.net/preg_match.

$jsonp = preg_match('/^[$A-Z_][0-9A-Z_$]*$/', $_GET["jsonp"]) ? $_GET["jsonp"] : false;
$output = execute_action();
if ($jsonp) {
    header('Content-Type: application/javascript');
    printf("%s(%s)", $jsonp, json_encode($output));
} else {
    header('Content-Type: application/json');
    echo json_encode($output);
}

I am not an expert in regex, so I got this incomplete example pattern from Validate a JavaScript function name. Check there for the correct regex.

Upvotes: 3

SilverlightFox
SilverlightFox

Reputation: 33578

  1. Let's see...

Say someone uses your code normally:

action.php?jsonp=callback

This will render

callback({"a":1,"b":2,"c":3,"d":4,"e":5})

If someone tries something with more malicious intent:

action.php?jsonp=alert('foo');//

This will render

alert('foo');//({"a":1,"b":2,"c":3,"d":4,"e":5})

However, as this would be achieved by a JavaScript link on their own site (e.g. www.foo.com):

<script src="http://www.example.com/action.php?jsonp=alert('foo');//"></script>

the alert would be executed in the context of www.foo.com rather than www.example.com.

As you are returning the correct content type (Content-Type: application/javascript), this should stop someone exploiting this as an XSS flaw.

  1. But out of completeness, I would be inclined to reject any request where jsonp contains non alphanumerics. Encoding isn't really an option as it needs to be a valid JavaScript function.

This approach may be necessary for old browsers, as some old versions of IE (and newer versions in compatibility mode) execute MIME type sniffing, so if someone linked to your page (with an anchor (<a>) tag) with enough HTML in the jsonp parameter, they could trick IE into rendering the content as HTML and then the script code embedded in this HTML would then be executed in the context of your site and you would have an XSS vulnerability.

Limiting the parameter to alpha numeric characters would not be too harsh a restriction in my opinion.

To do this in PHP use the ctype_alnum function, and reject if false:

if (!ctype_alnum($jsonp)) {
    // halt
}

Upvotes: 3

Related Questions