403 XMLRPC Error In WordPress Hosted On Windows IIS (Remember Your Security Changes)

This is more of a note for myself.  Hopefully it will end up in a search engine and I’ll be able to find my own solution when this happens again.

Yesterday, I went to post something to one of my blogs that I self-host on a Windows server.  You’re on one of those blogs right now, go figure.  And when I did, I got a very common error: 403 Forbidden when accessing the xmlrpc.php file.  But, I had just successfully posted two days ago!  What could have changed?

I connected to the server and updated the permissions on the folder, since that is usually what 403 means.  That didn’t fix it.  I did an IISRESET.  That didn’t fix it.  I did a full reboot.  That didn’t fix it.  I kept searching for information.

When you search for WordPress and xmlrpc, you get a crap-ton of results that warn of the dangers of leaving this file exposed.  It’s a gateway for hackers!!!!!!  It’s so hackable!!!!!  Turn it off!!!!  Disable any remote capabilities to WordPress!!!!  These warning are geared toward users who use and reuse weak passwords.  Of course there is authentication on anything xmlrpc does, so if you’re getting hacked, it’s your fault.  Granted, there’s no protection from a hacker brute-forcing your xmlrpc URL until it finally hits something, but again, strong, unique passwords, people!

With that little rant out of the way, I do recognize the high sensitivity of this file.  It would not be unlike me to take some extra precautions on it.  And that’s exactly what I had done, and what I had forgotten I’d done.

I set up an IP address restriction on just that file, so only my home PC could access it.  That makes sense since I only post using Live Writer from home.  However, my IP address from my ISP changed in the last couple days and I didn’t notice.  This is how I fixed it.  If you instead want to use this info to secure your own WP site (and forget about it until your IP changes), go ahead.

In IIS, I navigated to the xmlrpc.php file in the content view

Annotation 2020-04-19 111349

After highlighting the file, I right-clicked and chose Switch to Features View.  You can see the specific file name is in the title.  I chose IP Address Restrictions

Annotation 2020-04-19 111727

And in here is where I added/changed my home IP address.  You can find yours at https://www.whatismyip.com.

Annotation 2020-04-19 112011

And as you would expect, I am able to post to blogs again, as evidenced by this post right here.

TFS 2012 Fails To Create New Team Project

Today, I went to add a new team project to TFS.  I did the usual steps by giving it a name and choosing the template type (not really relevant to me since I’m a solo programmer).  Visual Studio worked for a bit, then failed with the following error in the log file:

Event Description: TF30162: Task "LinkTypes" from Group "WorkItemTracking" failed
Exception Type: Microsoft.TeamFoundation.Client.PcwException
Exception Message: Page not found.

The Internet had plenty of advice to resolve TF30162 errors, but nothing useful for the specific LinkTypes error.  The only suggestion was to reload the project templates.  I was not able to do that because in TFS 2012, those templates are locked and unable to be uploaded and overwritten.

So I attempted a Repair install of TFS 2012.  That had no effect

I then made sure I was on the latest version of TFS, so I installed Update 4.  My scrum project template went from 2.0 to 2.2, but I still got the exact same error.

I then tried to create a new Project Collection and create a team project in that.  Still, no change.

Then I did a repair install on Visual Studio 2015.  This took a very long time and required two restarts.  And after that, no improvement.

So far, I’ve eliminated the TFS binaries, the TFS database, and the VS binaries.  There’s only one element left – IIS.  And that one seems to have potential because of the PageNotFound error.

Poking around the IIS log files, there’s entries in there like this:

2017-01-09 14:27:23 ::1 GET /tfs/_apis/connectionData connectOptions=IncludeServices&lastChangeId=-1&lastChangeId64=-1 8080 MicrosoftAccount\myaccount@domain.com ::1 VSServices/14.98.25331.0+(devenv.exe+,Pro,+SKU:31) – 404 0 0 1719

Looking at IIS log files prior to when my problem happened,  this 404 error has happened before, too.  but there was also other calls to the service like:

2016-03-25 12:43:46 ::1 OPTIONS /tfs/defaultcollection/_apis/ – 8080 MicrosoftAccount\myaccount@domain.com ::1 Team+Foundation+(devenv.exe,+14.0.24712.0,+Pro,+SKU:31)+VSServices/14.0.24712.0+(devenv.exe+,Pro,+SKU:31) – 404 0 0 8

Two suspects come up in Internet searches: Git and IIS plugins.  Disabling some of the plugins in C:\Program Files\Microsoft Team Foundation Server 11.0\Application Tier\Web Services\bin\Plugins made no difference.

At this point, I suspect an incompatibility between VS2015 and TFS 2012, so I upgrade my TFS install to 2015.  What else could it be?  A call is being made to a REST service that doesn’t exist, or is different enough that the inbound parameters don’t match up.  This TFS upgrade requires SQLExpress to be upgraded as well.  The rabbit hole keeps going deeper and deeper.

After that entire install and restart, I was finally able to create a new Team Project.  The new default project template is “Agile”, not that I care much.

So, the short solution for my error: Upgrade TFS from 2012 to 2015.  It must’ve become incompatible after a VS update somewhere along the way.

Staying On The Correct Path

In a current task where I had to consolidate the structure of a website and organize the files better, I ran across this interesting quirk.  When developing, we work in a virtual directory on our local machine, while on a real server, it runs in the root directory.  This poses problems whenever we want to use relative paths to images, style sheets, etc.

The key to fixing this is the VirtualPathUtility, which has the logic in it to determine the root of the web application and return a proper path.  I thought it would be easy to just use this object right in the markup like:

<head runat="server">
    <title></title>
    <link id="cssLinkOutside" href='<%=System.Web.VirtualPathUtility.ToAbsolute("~/StyleSheet.css")%>' rel="stylesheet" type="text/css" />
</head>

That didn’t work.  The rendered HTML turned out to be:

<link id="cssLinkOutside" href="&lt;%=System.Web.VirtualPathUtility.ToAbsolute(&quot;~/StyleSheet.css&quot;)%>" rel="stylesheet" type="text/css" />

Pretty literal.  The cause of this odd behavior is that the HEAD tag is set to runat=”server”.  If that piece is taken out, the embedded code works well.  So if you need the HEAD to be a server control – like when using themes – then what?  You can add code in the code-behind to do an attributes.add() on the LINK tag.  But having to add code in two places for such a simple need is just too much, especially when I was looking at updating a couple dozen pages.

The workaround is kind of surprising: Wrap the tag that contains dynamic code in a placeholder.

<head runat="server">
    <title></title>
    <asp:PlaceHolder runat="server" id="holder">
        <link id="cssLinkInside" href='<%=System.Web.VirtualPathUtility.ToAbsolute("~/StyleSheet.css")%>' rel="stylesheet" type="text/css" />
    </asp:PlaceHolder>
</head>

This renders the tag properly:

<link id="cssLinkInside" href='/WebTestbed/StyleSheet.css' rel="stylesheet" type="text/css" />