301 Redirects Plugin – How to Fix the Vulnerability?

I’ve recently had one of the websites I maintain hacked through a little loophole that allowed unsafe redirects to get added to any website that has the addon installed.

The plugin is called Simple 301 Redirects Plugin, and on its own, it was mostly fine, but the killer combination was using a nifty little add-on for bulk uploading redirects, which sounds very useful if you have lots of redirects that you want to just add from a spreadsheet.

The result of these 2 plugins can be your entire website getting redirected to a 3rd party URL with a 301 – which can’t be uncached for users, so the damage will depend on how long the hack was active on the website, with more time it will get cached for more visitors.

In my particular case, I’ve managed to remove the malicious redirect, but I’ve also had to think of the underlying problem that this plugin in generall opens up, which is that you can brake the entire website, with just one pair of source -> destination URLs. So here’s the code I added to the theme:

// * attaches to the 301 redirects options field, and modifies before its saved


As you can see in the above code, my approach was to modify the added redirects, before the option is saved to the database, that way we can filter out any malicious destination URLs.


// * filters the simple 301 redirects array 
function prefix_301_filter_redirects($redirects) {

    $safe_3rd_party_redirects = [
        // * allow self domain name
        parse_url(home_url(), PHP_URL_HOST),

    // compare user request to each 301 stored in the db
    foreach ($redirects as $storedrequest => $destination) {

        $is_safe_destination = array_filter($safe_3rd_party_redirects,function ($_include) use ($destination) {
            // * if include (safe) destination is found in list of safe destinations   
            $_destination = preg_replace("(^https?://)", "", $destination);

            return strpos($_destination, $_include) === 0;

        // * checks if the redirect destination is relative (starts with /)
        $is_relative_destination = strpos($destination,'/') === 0;
        // * if not save destination unset 
        if(empty($is_safe_destination) && ! $is_relative_destination ) {
            prefix_31_filter_notice($storedrequest, $destination);

        // * prevent all pages redirect with a wildcard /* (all inner pages), * all pages with home
        if(in_array(trim($storedrequest),['/*','*'])) {
            prefix_31_filter_notice($storedrequest, $destination);

    return $redirects;


In the sample above, will fill in the missing function from the filter code snippet, and what it in simple terms do is… declare a list of safe destination domains, and only allows the redirect pair to be saved if it matches any of these safe domains.

The other part of my idea was to prevent all only wildcard redirect rules, so anything that would redirect all pages including home (/* OR *) is removed.

You’ll also notice that there is a small notice function (prefix_31_filter_notice) in the sample above, that one is not necessary but I just found it helpful to always show the redirect pair that was removed in the form of a notice up top (when the plugin settings page is submitted).


function prefix_31_filter_notice($redirect_from,$redirect_to) {
    add_action('admin_notices', function () use ($redirect_from,$redirect_to) {

        echo '<div class="notice notice-warning is-dismissible">
            <p>Removed unsafe redirect: ['.$redirect_from .'  => '.$redirect_to.']</p>



Probably not something groundbreaking in this article, but just wanted to share the solution that I came up with for this particular problem.

Leave a Reply

Your email address will not be published. Required fields are marked *