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:
Your opinion on the importance of protecting a hypothetical 3rd party site from harmful jsonp injections
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.
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.