craftcms / element-api

Create a JSON API/Feed for your elements in Craft.
MIT License
498 stars 56 forks source link

Can element api create jsonp? #96

Closed jledfordtbc closed 4 years ago

jledfordtbc commented 5 years ago

I haven't seen anyone asking or answering this, but can you pass a callback with element api and wrap it in jsonp? Dynamic callbacks would be even better so I can hit the endpoint myserver.com/test.json?mycallback=whatever and what is returned is wrapped in a whatever callback. Is that possible?

brandonkelly commented 4 years ago

We just released Element API 2.6.0 with JSONP support via a new callback endpoint setting.

If you want that to be configurable with a callback query string param, do this:

use yii\web\BadRequestHttpException;

return [
    'test.json' => function() {
        $callback = Craft::$app->request->getQueryParam('callback');

        // Only alphanumeric and underscore characters allowed
        if ($callback && !preg_match('/^\w+$/', $callback)) {
            throw new BadRequestHttpException('Invalid callback');
        }

        return [
            'callback' => $callback ? "/**/ $callback" : null,
            // ...
        ];
    },
];
SimonEast commented 4 years ago

@brandonkelly Can you confirm that the callback is appropriately filtered and truncated to prevent attacks? For example you don’t want people doing this...

http://server/my-api-endpoint/?callback=<malicious script goes here>

Be aware that even pure alphanumeric strings can be abused via the Rosetta Flash technique, so you often need to add a /**/ comment prefix and truncate the length to help prevent this. Haven’t reviewed your code to see if it’s doing this.

Some of the security issues are explained on the JSONP Wikipedia page at https://en.m.wikipedia.org/wiki/JSONP#Security_concerns

brandonkelly commented 4 years ago

@SimonEast It’s a config setting so as far as Element API is concerned the value is trusted. If you want to set the value from user input it’s on you to ensure the value isn’t malicious. I’ve updated the example above to verify that the callback only contains alphanumeric characters and underscores, and append /**/ to the value.