Securing WordPress 2 Admin Access With SSL

The situation has not changed much since WordPress 1.5: WordPress 2.0 still does not support HTTPS access to the admin area when the rest of the blog is served via normal HTTP and I still do not like logging in to my server over unencrypted connections, especially not when using public WLANs. Getting around this WordPress limitation requires quite a few steps:

The Goal

All communication involving passwords or authentication cookies should be done over HTTPS connections. wp-login.php and the wp-admin directory should only be accessible over HTTPS.
Normal reading access, as well as comments, tracebacks, and pingbacks still should go over ordinary HTTP.

The Plan

Add an HTTPS virtual host that forwards requests to the HTTP virtual host

Modify WordPress to send secure authentication cookies, so cookies never get sent over insecure connections accidentally

Require a valid certificate on HTTPS clients. That means to log in to WordPress you need both a valid certificate and a valid password. If someone manages to get your password, he still can not login because he does not have a valid certificate.

The Implementation

Note: This documentation assumes a Debian sarge installation with Apache 2. Some things, in particular Apache module related ones, will be different on other systems.
The server used throughout the instructions is example.org/192.0.34.166. The server’s DocumentRoot is /blog and WordPress resides in /blog/wp. The value of WordPress’ home option is ‘http://example.org’ and the value of its site_url option is ‘http://example.org/wp’.

Prepare the SSL certificates:

Generate your own certificate authority (CA) if you don’t have one already (I’m using the makefile from OpenSSL Certificate Authority Setup for managing mine) and import it into your browser.

Generate a certificate for the SSL server and certify it with your private CA.

Generate a certificate for your browser and certify it with your private CA. Most browsers expect a PKCS#12 file, so generate one with

Make WordPress SSL-ready:
Apply this patch to the WordPress code. It makes the following changes:

Use secure authentication cookies in wp_setcookie()

Make check_admin_referer() work with HTTPS URLs

Use HTTPS URLs for notification mails

Use HTTPS URLS for redirects to wp-login.php

Only allow XML-RPC logins from the local host (ie. the HTTPS proxy)

Add the Mark-as-Spam feature from trunk

The patch is against svn version 3825 of WordPress (ie. WordPress 2.0.3), when you apply it to a newer version, you will likely get some harmless ‘Hunk succeeded’ message. If you are getting ‘Hunk FAILED’ message, just send me note and I’ll update the patch.

Enable the necessary Apache modules:

Install mod_proxy_html. It will be used to replace absolute ‘http://example.org’ HTTP URLs in the WordPress output with ‘https://example.org’ HTTPS URLs:

$ aptitude install libapache2-mod-proxy-html

The module gets enabled automatically after installation.

Enable mod_proxy and mod_ssl

$ a2enmod proxy
$ a2enmod ssl

Debian provides sane default configurations for both modules. You might want to take a look at the configuration files (ssl.conf and proxy.conf) nevertheless.
I have changed SSLCipherSuite to

Modify the blog virtual host to limit access to wp-login.php and wp-admin to the local host. Also completely deny access to files which should never be accessed directly. Here is an example: 10-wp2-example.org

Now setup the HTTPS virtual server: 20-wp2-example.org-ssl
If you are compressing WordPress output you have to enable the RequestHeader line.

Thanks for this help. This certainly fixes most of my issues with having a secure blog admin and a non-secure regular blog. The only issues, which I’m sure you are aware of is that the preview on the “Write Post” page for WP2.0 no longer works and if you check on an insecure page if anyone is logged in (eg. to display “Login” or “Logout”) it will always indicate that there is no one logged in. I think both these issues are due to the patch causing cookies to only be sent over secure connection (if I understand correctly). Anyway, these are minor issues, since I don’t use the preview much anyway. Thanks again.

Now that I pay a bit more attention, I’m not completely using your solution. I’m simply redirecting from non-secure to secure using mod_rewrite in my apache config for the applicable urls (wp-admin, wp-login etc). I’m not using mod_proxy, so maybe that is my problem.

Okay, I’ve fixed the issue by *correctly* installing the mod_proxy_html module. Now all the requests are going through https like they should. Unfortunately, I’m getting the preview issue that Tim got. If I try to go to the main blog page using https, I’m not denied access, but I don’t get anything back from the server.

So, HTTPS access works for the admin pages but not the normal content pages?

If I remember correctly that happens if you have the gzip turned on (see the bottom of the Options/Reading page).

mod_proxy_html doesn’t work well with compressed content, so you either have to disable compression completely (I do that on this site) or install the ‘headers’ apache module and uncomment the RequestHeader line in 20-wp2-example.org-ssl (as described above). The latter solution disables compression only for HTTPS requests.

This article says that using SetOutputFilter INFLATE;proxy-html;DEFLATE should work too. But it doesn’t work on any of my systems, maybe because WordPress doesn’t compress everything.

Yes, that worked. I can either disable gzip or uncomment the headers line and the preview now works. I have another question. When viewing the page normally, my meta login link sends me to http not https. I don’t think it would get redirected unless I could actually have access to the wp-login.php file via http. Should this link have been translated to https? It is generated by the wp_loginout() function.

It doesn’t make much sense to change that links in my opinion. It’s better to remove them from the HTTP site and just show them on the HTTPS site.

The reason why using wp_loginout() on the HTTP site doesn’t make sense is that wp_loginout() doesn’t know whether you’re logged in or not because the authentication cookies only get sent over secure HTTPS connections. That means when accesing the HTTP site, you’re always logged out, ie. you’ll always get the ‘Login’ link but never a ‘Logout’ link. When viewing the site over HTTPS the links will work like expected.

Anyhow, it doesn’t hurt. I’ll upload a new patch which always generates https links for wp_loginout() and wp_register() later today.

I’ve noticed something else strange with the https solution. When I attempt to delete a link, I get the normal “are you sure” message box, then I get a message box with the following:

Microsoft Internet Explorer

[OK]

After I click ok, I still see the processing Data… text at the bottom of the list. The link does not dissapear from the list, but if I hit refresh, it is gone. It appears as if the AJAX code to remove the line is failing, but I’m not quite sure.

The reason I want to get the login links to work from the main site is because I’m host several blogs for friends and would like to make the secure login as transparent as possible. Thanks for your help so far.

This way the settings cache is populated on every call with your values – depending on wheter you hit the site with http or https. The Hostname will be set automatically.

Now, to force logins with SSL, open wp-login.php and search for “case ‘login’:”. A few lines down in the file you’ll find the HTML-head. Right before the add the line:
“>

Once you entered the SSL-site, you will continue to browse it via SSL and vice versa.

Disclaimer: I’ve only modified my wordpress recently, but I did not see any problems arise. But beware: the cookies set on the secure site (if any) would be transmitted via plain HTML unless the hostname for HTTPS is different from the one used for HTTP (which is the case in my setup).

Thank you for your contributions, and especially for making it available under the GPL license.

Have you contacted the WordPress developers regarding what your patch does? In the control panel (trunk perhaps) they could add an option to secure the administration area only via SSL which would enable your changes.

I’m sure a bit more work than that would be needed, but it would be a great feature to add.

No, I haven’t submitted the patch. I doubt it would get accepted, it depends on too many external things. (I’ve submitted a few fixes for bugs I found while working on the patch though.)

IMO WordPress needs proper built-in HTTPS support. Unfortunately the developers seems to a have different take on that. There’s on old feature request which didn’t get much attention. The link to the secure-admin , which was added today, looks at least a bit promising.

Yes, thanks for posting your notes. They were quite helpful. A question for you…

Your patch disables the XML-RPC interface to WordPress. While this is certainly desirable if you’re using XML-RPC over HTTP, there’s no reason you can’t configure your XML-RPC client to access WordPress’ XML-RPC over TLS/SSL. I’ve tested it with both MarsEdit’s and ecto2 and it works fine. Am I missing something or were you just trying to protect against someone inadvertantly using HTTP when they wouldn’t realize they were doing that?

Also, I have WordPress working with lighttpd over HTTP and HTTPS under OS X. If anyone’s struggling with this particular combination, give me shout at ‘ddp at electric-loft dot org’.

[…] Jürgen Kreileder has a great blog entry on how to hack WordPress to use secure administration pages. Alas, it does more than I want (comment spam management) and my web server doesn’t run the mod_proxy required to complete the URL output rewriting. I had to do something a bit more… sinister (read: hackish). […]

Derrell, the disabled XML-RPC interface is a historic thing. I don’t use it for my blog and – because you can’t make xmlrpc.php HTTPS-only (it’s required for trackbacks) – I just disabled. And yes, it keeps people from inadvertently logging in via plain HTTP.

I’ll enable it again (with a check for SSL) in the next version of the patch.

WordPress 2.03 is a critical security release. It eliminates the HTTP Referrer check and replaces it with a nonce system. What is a referrer check? Well, it is an attempt to confirm that an administrative action is being taken by an administrator in…

[…] This is a test post, please ignore. I’m using it to hack on comment support. As background, WordPress doesn’t really support SSL/TLS (HTTPS) out of the box. I’m using Jürgen Kreileder’s fine SSL patches but since I’m running lighttpd instead of Apache and because I want to use ecto over XMLRPC, it’s not quite the same and I need additional hacks. I have Comment registration working over HTTPS but I haven’t managed to get the actual Comment code working yet… Stay tuned. You’ll be able to vent back soon. Share and Enjoy:These icons link to social bookmarking sites where readers can share and discover new web pages. […]

The secure-admin plugin by wordpress.org also does a great job of implementing SSL within WordPress. I had problems making it work at first and had to debug it to find it was a code error. I’ve made the fixed file available here:

I am using WORDPRESS 2.7.1 with CAS authentication.
Now I am trying to publish a document on WordPress using XML-RPC but it shows error.
javax.faces.el.EvaluationException: Exception while invoking expression #{org_alfresco_module_blogIntegration_BlogActionListener.executeScript}
caused by:
org.alfresco.module.blogIntegration.BlogIntegrationRuntimeException: Failed to execute blog action ‘metaWeblog.editPost’ @ url ‘http:///wordpress/xmlrpc.php’
caused by:
marquee.xmlrpc.XmlRpcException: The XML-RPC response could not be parsed: org.xml.sax.SAXParseException: The element type “address” must be terminated by the matching end-tag “”.

I want to know whether XMl-RPC request on wordpress is allowed after applying CAS authentication on WORDPRESS?