• I recently found myself with the not so uncommon problem of needing to secure multiple WordPress installations on the same web server. Unlike standard HTTP with VirtualHosts (Apache), HTTPS requires different ports for each site. However, WordPress does not currently allow you to specify which port should be used for SSL functions, and automatically assumes the standard port 443. This is a reasonable assumption for the core WordPress code to follow, but some of us host many small sites on a single server, and still care about security.

    During my research, I came across the WordPress HTTPS plugin, which appeared to have exactly what I needed in its “Shared SSL” feature, since I needed my SSL Host to be “something other than ‘https://example.com/'”. However, after trying to set my Shared SSL Host to “https://example.com:4432”, followed by a thorough examination of the source code, I discovered that it wasn’t designed to work with port numbers at all.

    So, after a few lengthy hacking sessions figuring out how WordPress plugins and filters work, I have a patch here for WordPress HTTPS 1.9.2 that supports port numbers and works beautifully on my secondary (and now secure) WordPress installation on my personal VPS. I’d like to submit it to Mvied for consideration in the next release, but I could not find a way to do so, other than reach out through the forum here. I couldn’t find a GitHub repo, a “send message” link on his profile, and the Trac for WordPress Plugins doesn’t even list the plugin as a Component. I’m pretty lost when it comes to my contribution.

Viewing 15 replies - 1 through 15 (of 19 total)
  • Plugin Author Mike Ems

    (@mvied)

    Hey doingweb,

    It’s true, this is really the only place to get in touch with me. If you’d like you can toss the source in a pastebin and I’ll take a look at it.

    Thanks for contributing. 🙂

    Mike

    Plugin Author Mike Ems

    (@mvied)

    Hey doingweb,

    I went ahead and edited the plugin to accept a port in the Shared SSL Host field. In 1.9.2, line 740 would be changed to this:
    $_POST[$key] = 'https://' . $url['host'] . ((@$url['port']) ? ':' . $url['port'] : '') . @$url['path'];

    That should pretty much do it. I’d still like to see the modifications you made in case I overlooked something.

    Thanks,
    Mike

    Thread Starter doingweb

    (@doingweb)

    Hey! Thanks for the prompt reply! Here’s the pastebin for the patch: http://pastebin.com/bs3GRaYp.

    I made almost the same change on line 740, except I used isset(), though an error suppression is probably a better choice for brevity and consistency. This change makes it so that the URL including port number can be saved, but it doesn’t solve all of the problems associated with port numbers in the URL.

    I found that, while the URL was rewritten correctly in many places, there were several places where the port number was added twice. That is, “src” and “href” attributes would contain URLs with “https://example.com:4432:4432” in them, which would break functionality. This indicated to me that the “https://example.com” portion was being replaced twice with my Shared SSL Host URL, and that there was some code that ignored the port number and only looked for a plain base URL to replace.

    So most of the other changes I made were to the regular expressions in the process() and replace_http_url() functions that deal with recognizing and capturing the URL to replace.

    I don’t think I have to tell you, but please feel totally free to make necessary changes for correctness and style if you choose to use it. Though I’d call myself a decent PHP/RegEx coder, this is my first experience with the “dirty” side of WordPress, and I didn’t read any of the developer docs or much of the core code. I very well could’ve completely missed something.

    Thanks!

    Plugin Author Mike Ems

    (@mvied)

    Hey doingweb,

    Could you do me a favor and download the development version and see how it works? I’ve made quite a few changes to the regular expressions for the next version (they’re much more generic). For example: preg_match_all('/\<(a|form)[^>]+[\'"]((http|https):\/\/[^\'"]+)[\'"][^>]*>/im', $buffer, $matches);

    The only regex that’s not completely generic is the one in the replace_http_url method. I’ve changed it to preg_match_all('/(http|https):\/\/[\/-\w\.,#?=\+&%;:\d]+/im', $string, $url); which I think should be sufficient to make it work with a port number.

    If you want, download the development version here and replace your current wordpress-https.php with the one in the zip archive and let me know how it works.

    Thanks,
    Mike

    Thread Starter doingweb

    (@doingweb)

    This version (1.9.3-dev) does not fully work, and still exhibits the doubled port number bug.

    I believe the problem is in the replace_http_url() function, where we have the lines (651, 652):

    $string = str_replace($this->replace_http($this->http_url), $this->https_url, $string);
    $string = str_replace($this->http_url, $this->https_url, $string);

    Keep in mind that sometimes, the input string ($string) contains a URL that already contains the port number.

    For reference, $this->http_url is defined as (line 100):

    $this->http_url = 'http://' . parse_url(get_option('home'), PHP_URL_HOST);

    And $this->https_url is our Shared SSL Host (port and all).

    The apparent goal of these replacements is to replace the https and http versions of the site’s hostname with the Shared SSL Host name and the https scheme in the URL contained in $string. However, when port numbers are involved, that part of the replacement target is left out, and only the hostname and scheme are replaced, resulting in duplicated port numbers.

    As an example, replace_http_url() receives “https://example.com:4432/wp-admin/&#8221; as $string at some point of the page load. $this->http_url is logically “http://example.com&#8221;. But when the first replacement is issued, it replaces “https://example.com&#8221; with “https://example.com:4432&#8221;, and the function returns “https://example.com:4432:4432&#8221;.

    It looks to me like the solution would be to capture the complete base URL (that is, up until the path) and just do one replacement on that, no matter what it is, with $this->https_url (and please correct me if I’m wrong/looking at it too narrowly). I’ve created a patch for the changes to get it working again: http://pastebin.com/UKDVv4Bs.

    Just after I got it working, I noticed a mixed content warning. It looks like URLs in the inline Javascript are getting returned to http (with the port number). I’m investigating.

    Thread Starter doingweb

    (@doingweb)

    For some reason, the Shared SSL Host is showing on the settings page as HTTP, even though I definitely saved it as HTTPS, and it even reflects so in the database. The HTML in the settings() function suggests that it should be using the correct HTTPS version…

    It looks like this is the same problem as with the improper inline Javascript. Still investigating.

    Thread Starter doingweb

    (@doingweb)

    Okay, I figured out what it was.

    What was happening was the first section of process() (“Fix any occurrence of the HTTPS version of the regular domain when using Shared SSL”) was taking those instances where the URL was already switched, and then replacing the HTTPS with HTTP. This resulted in some URLs like “http://example.com:4432&#8221;, which never got corrected by later code.

    In my previous patch for 1.9.2, I’d made similar changes to a similar section, which in that version was at the end of process().

    I’ve updated the latest pastebin (http://pastebin.com/UKDVv4Bs), and I am now free of any mixed content warnings.

    Plugin Author Mike Ems

    (@mvied)

    Hey doingweb,

    Gotcha, that makes sense. I’ll get an update into the development version today and update the topic when I do, then you can give it a test run.

    Thanks for your help,
    Mike

    Thread Starter doingweb

    (@doingweb)

    Sounds great! Thanks!

    Plugin Author Mike Ems

    (@mvied)

    Hey doingweb,

    Sorry it’s taken me a while to get back to you. I was doing some heavy development on the plugin. I think I’ve got the plugin working with a custom port, but have no real-world server to test it with. If you would, try downloading the new development version and let me know if it works.

    Thanks,
    Mike

    Thread Starter doingweb

    (@doingweb)

    Sure thing.

    Thread Starter doingweb

    (@doingweb)

    Hey, good to hear from you again!

    I just loaded it up, and it looks like it’s definitely not working. The port number only shows up in 5/21 instances in the page source where the HTTPS URL is supposed to be. This is also strange, because in the previous version, there were 33 HTTPS URLs… I’ll dig a little deeper later today.

    I’d also appreciate an acknowledgement for my work on this feature in the changelog, if you wouldn’t mind. Just my name (“doingweb” or “Chris Antes”) would be sufficient, which could link to my (as yet incomplete) blog or Twitter, where applicable.

    Thread Starter doingweb

    (@doingweb)

    All of the link, script, and img URLs did not get the port number. URLs that appear in inline script were also not given the port number.

    It looks like many of those 33 URLs from the previous version shouldn’t have even been replaced (they were intended to be off-site), so 21 is the right number.

    Thread Starter doingweb

    (@doingweb)

    Those URLs that weren’t given the port number were not run through replace_http_url(), which would have done the job. The second section of process(), labeled “Fix the regular stuff”, goes through each of these tags (see for loop lines 532-573). However, there is a lot of complicated and seemingly redundant logic in there. For instance, three separate branches end in the single line:

    $buffer = str_replace($html, str_replace($url, $this->replace_http_url($url), $html), $buffer);

    The inline script problem was previously solved by the first section of process() (“Fix any occurrence of the HTTPS version of the regular domain when using different SSL Host”). However, this now checks if the SSL host is different, which it isn’t, since the port is now considered separate.

    Thread Starter doingweb

    (@doingweb)

    Okay, I have a small patch here that clears up all of my problems. The source now shows all HTTPS URLs with ports (with no doubling), including all tags and inline script, and I have no mixed content warnings.

    I changed the condition for when to replace the HTTPS version of the regular domain from being only if the SSL Host is different to if the SSL Host is different or an SSL Port is set. I also corrected what I believed to be a typo on a later line in that condition, switching the call to replace_https_url() with replace_http_url().

Viewing 15 replies - 1 through 15 (of 19 total)
  • The topic ‘SSL Administration over Non-Standard Ports’ is closed to new replies.