3

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.

codefactor
  • 1,616
  • 2
  • 18
  • 41
  • 1
    doesn't really matter - it's the jsonp users who'll be executing the code you send back to them. theoretically you could send back `window.location.href='disgustingporn.com';` and redirect all their users. As long as you're practicing safe computing on your server, doesn't really matter what they send to you. – Marc B Jan 06 '14 at 20:02

2 Answers2

3
  • 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.

Community
  • 1
  • 1
jillro
  • 4,456
  • 2
  • 20
  • 26
  • 3
    Only receiving data the user sent themselves is no guarantee of safety, that is how reflected XSS attacks work. – Quentin Jan 06 '14 at 20:30
  • True, I was making the assumption that the question was safety from the website point of view. Edit my answer. – jillro Jan 06 '14 at 20:35
  • A fixed name is not really an option since many jsonp frameworks require a customized jsonp callback function name in order to allow asynchronous simultaneous jsonp calls. Hypothetically, though, if I did want to protect other websites from harm via my service - would you recommend validating or escaping the jsonp callback function, or perhaps both? And if so what php function would you use? – codefactor Jan 06 '14 at 22:04
  • You can validate it is a valid Javascript function name using PHP regular expressions functions. See my edit. – jillro Jan 06 '14 at 22:23
  • As noted on [Validate a JavaScript function name](http://stackoverflow.com/questions/2008279/validate-a-javascript-function-name) the accepted answer is incorrect and the regex is incomplete. (Therefore the accepted answer here is also incorrect and incomplete.) – Winnie Tong Mar 26 '14 at 00:37
  • As you may have noticed, the question was "Is it necessary to validate or escape the jsonp callback string ?", not "What is the correct regex to validate a JavaScript function name ?". I will state it more clearly in my answer. – jillro Mar 26 '14 at 00:41
  • @WinnieTong Adding the complete answer about the regex would add to much complexity to the answer as it is a complexe problem, and would make this question a duplicate. – jillro Mar 26 '14 at 00:48
  • @Guilro In a world where people blindly copy and paste solutions from stackoverflow, I feel that it is necessary to caution those considering copying and pasting the accepted solution posted here. Thanks for updating your response to indicate the code's incompleteness! – Winnie Tong Apr 02 '14 at 00:26
3

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.

2) 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
}
SilverlightFox
  • 32,436
  • 11
  • 76
  • 145
  • 1
    I thought your answer did add to the discussion and was useful, however I thought the usage of a regular expression (Guillaume Royer's answer) for javascript function names a little more robust than alpha numeric test. Thanks for the tip on the function `ctype_alnum` – codefactor Jan 07 '14 at 18:24