Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding bookmarklet to wordpress post

I'm trying to add a javascript bookmarklet link to a post on my WordPress site. However it isn't coming out in the post preview. When I check the link that WordPress adds to the post it has converted it to javascript:void(0). This simple example reproduces the problem.

<a href="javascript:alert('Alert!');">Search Scholar</a>

There are a few other people who've had the same problem here, here, here, and here but no-one seems to have found a solution beyond just giving their bookmarklet code for people to copy and paste and create their own bookmarklet.

The cause of this problem is that Chrome's XSS protection is stripping out the javascript from the link when submitting it via wp-admin. One "solution" is to add the line header("X-XSS-Protection: 0"); to wp-blog-header.php in the root folder. This is insecure as it switches off the XSS protection on your WordPress site but it does allow the bookmarklet code to be rendered when the page loads.

Are there any real solutions to this problem that don't involve switching off XSS protection? Is there a perhaps a plugin I can install to my WordPress to allow me to add javascript: links inside my posts?

like image 220
Jamie Bull Avatar asked Nov 07 '13 19:11

Jamie Bull


1 Answers

EDIT 2 After even more research, this is actually caused by the browser's XSS detection as mentioned by the OP (as opposed to any WordPress-specific functionality). The problem only arises when you click the Preview button in WordPress, and only on that initial page load. Apparently WordPress sends along some of the HTML in the request headers, and that triggers the XSS functionality in the browser. If you load the preview, and then you refresh the page, the XSS issue goes away, and the javascript: link is displayed as it was saved. When viewing the actual site, after publishing the page, this XSS issue is never encountered present.

EDIT After some deeper research (working with @gnarf), it turns out the actual issue comes down to the way that WordPress handles javascript: links in its preview functionality. It would seem that WordPress has some custom Javascript that runs and converts all javascript: links to javascript:void(0) links (stripping out any custom code), but only if you're previewing the page. After publishing the page, the javascript: links are rendered out properly.


Original Post (describes how to stop WordPress from stripping out javascript: links when saving a post as a non-admin user, which is what I assumed the original problem might have been)

It looks like WordPress strips out the HTML in the content_save_pre filter. Specifically, it calls the wp_kses_bad_protocol method in wp-includes\kses.php:

/**
 * Sanitize string from bad protocols.
 *
 * This function removes all non-allowed protocols from the beginning of
 * $string. It ignores whitespace and the case of the letters, and it does
 * understand HTML entities. It does its work in a while loop, so it won't be
 * fooled by a string like "javascript:javascript:alert(57)".
 *
 * @since 1.0.0
 *
 * @param string $string Content to filter bad protocols from
 * @param array $allowed_protocols Allowed protocols to keep
 * @return string Filtered content
 */
function wp_kses_bad_protocol($string, $allowed_protocols) {
    $string = wp_kses_no_null($string);
    $iterations = 0;

    do {
        $original_string = $string;
        $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
    } while ( $original_string != $string && ++$iterations < 6 );

    if ( $original_string != $string )
        return '';

    return $string;
}

The $allowed_protocols parameter is retrieved via the wp_allowed_protocols() method, which applies the kses_allowed_protocols filter to the list of protocols.

With this information, you should be able to tie into the kses_allowed_protocols filter to add javascript as a valid one (note that this, of course, would open up security issues):

add_filter( 'kses_allowed_protocols', function ($protocols) {
   $protocols[] = 'javascript';
   return $protocols;
});

One way to enhance the security of this approach would be to add a check for specific users or specific roles (by default, it looks like this filter actually isn't run on administrative accounts, so you can use javascript: links to your heart's content as an admin) prior to allowing the javascript protocol.

like image 185
cmptrgeekken Avatar answered Sep 20 '22 00:09

cmptrgeekken