Picture of Jürgen Kreileder

Securing WordPress 2 Admin Access With SSL

A few people have asked for an updated version of my Securing WordPress Admin Access With SSL guide. So here is an updated version for 2!

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 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
      $ openssl pkcs12 -export -clcerts \
          -in blogclient.cert \
          -inkey blogclient.key \
          -out blogclient.p12

      Then import blogclient.p12 into your browser.

  • 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

      TLSv1:SSLv3:!SSLv2:!aNULL:!eNULL:!NULL:!EXP:!DES:!MEDIUM:!LOW:@STRENGTH

      in ssl.conf in order to just allow TLS v1 and SSL v3 ciphers which provide strong encryption and authentication (see ciphers(1)).

    • If you are compressing WordPress output (that is if you enabled the ‘WordPress should compress articles (gzip) if browsers ask for them’ option) then also enable mod_headers:
      $ a2enmod headers
  • Configure Apache to listen on the HTTPS port
    $ cat > /etc/apache2/conf.d/ssl.conf << EOF
    <IfModule mod_ssl.c>
    	Listen 443
    </IfModule>
    EOF
  • 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.
  • Enable the site and restart Apache
    $ a2ensite 20-blog-ssl
    $ /etc/init.d/apache2 restart
  • Remove the old WP cookies from your browser
  • Test the new setup!

February 1st, 2006: wp2-ssl.patch updated for WordPress 2.0.1

March 11st, 2006: WordPress 2.0.2 has been released, fixing some security issues. The HTTPS patch still applies fine to that version.

March 19th, 2006: Updated wp2-ssl.patch. Changes: Fix bug in list-manipulation.php, use HTTPS for ‘Login’ and ‘Register’ links, backport ‘Mark-as-Spam’ feature from trunk

May 1st, 2006: WordPress 2.0.3 has been released. Here is the updated wp2-ssl.patch.

July 29th, 2006: WordPress 2.0.4 has been released, fixing some security issues. Here is an updated version of the wp2-ssl.patch.

January 12st, 2007: wp2-ssl.patch updated for 2.0.6 and 2.0.7-RC1

January 15st, 2007: WordPress 2.0.7 has been released. The patch still applies fine to that version.

This article Jürgen Kreileder is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

32 Comments

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post. Both comments and pings are currently closed.

[…] Securing WordPress 2 Admin Access With SSL | no wow Securing WordPress 2 Admin Access With SSL (tags: wordpress acess ssl)   […]

[…] read more | digg story […]

[…] Securing WordPress 2 Admin Access With SSL | no wow Securing WordPress 2 Admin Access With SSL (tags: wordpress acess ssl) […]

[…] Geht nicht gibt’s nicht, also den Patch von Jürgen Kreileder installiert und gemäß seiner Anleitung weiter verfahren. […]

Andreas said

Danke, bin schwer begeistert!

Tim said

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.

Tim said

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.

Yes, the missing proxy and proxy_html modules are probably causing the problem you see. With the setup described in the article, previewing works fine for me.

You’re right about the login/logout links on non-HTTPS pages though, but that’s an expected consequence of using secure cookies.

Scott said

I’m using WordPress 2.0.1 and your patch. When I click the [Sign Out] link on the top, I’m redirected to the login page, but without the https. What would cause this?

Scott, I can’t reproduce that problem. Can you provide more info?

If you’re coming from an HTTPS page, you should get redirected to an HTTPS page — unless some plugin overrides wp_redirect().

Even if the PHP code issues a wrong location for the redirect you shouldn’t get to the login page over plain HTTP. The apache config should deny that request with a “403 Forbidden” error.

Scott said

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.

Scott said

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.

Scott said

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.

Scott, the new version of the patch should fix that problem.

Joerg said

If you are way too lazy for this setup (it is a bit complicated) or do not have access to mod_proxy, you could go with the following setup:

1) Open wp_config.php in your favorite editor:
After the line “require_once(ABSPATH.’wp-settings.php’);” add:

wp_cache_set("siteurl_secure", "https://www.example.com/path-for-wordpress-on-secure-server/", "options");
wp_cache_set("home", ($_SERVER["HTTPS"]?"https://":"http://").$_SERVER["SERVER_NAME"]."/your-blog-path", "options");
wp_cache_set("siteurl", get_settings("home")."/your-web-path-for-wordpress", "options");

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).

Joerg said

Hmm… since the code in the comment was screwed up I posted all this to my testing and development environment:
http://noctis.de/pl/2006/03/using-wordpress-with-ssl/

deoren said

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.

Thanks!

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.

Derrell said

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’.

Neues Widgets Plugin funktioniert nicht mit SSL Proxy…

Das neue WordPress Widgets Plugin ermöglicht es benutzern, die Sidebar des Blog per Drag´n Drop zu gestalten. An sich eine ziemlich coole Sache, allerdings bleibe ich doch lieber dabei die Sachen per Hand zu editieren. Nicht zu letzt, weil das Wi…

[…] 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.

[…] With help from here and here I have a solution that works for me. […]

NOnces have arrived in WP…

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…

Lynoure said

Thanks for your patch!

I wish a patch for securing WordPress was included in the WordPress code. Seems such a pity that such a pretty blog engine requires patching to secure.

[…] 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. […]

Haris said

Hi Juergen,

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:

http://haris.tv/2007/01/11/wordpress-ssl-plugin-secure-admin-patched-and-working

Haris

[…] Securing WordPress 2 Admin Access With SSL (tags: wordpress ssl sicherheit datenschutz) […]

[…] Another user-supplied one that comes with decent install instructions (debian-centric, again) but I didn’t try myself […]

umesh said

hi,

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?

Please help.