How to secure WordPress admin directory on IIS 7.0

Recently I was told about Smashing Magazine, which turned out to be a pretty useful site. It is targeted for web developers and web designers and it contains tons of information, tools and freebies for web developers. One of the article on that site was about 10 Steps To Protect The Admin Area in WordPress. In that article step #7 described how to use web server’s built-in authentication to provide an extra protection layer for wp-admin directory, where all WordPress admin scripts are located. The article described how to do that in Apache by using .htaccess file. In this post I will explain how to protect WordPress wp-admin directory on IIS 7.0 by using IIS built-in Forms Authentication.

Prerequisites

First thing to make sure is to confirm that IIS Forms authentication and URL authorization modules are installed and enabled on your IIS server. To quickly check that, open an elevated command prompt and run the following commands:

C:\Windows\System32\inetsrv>appcmd list modules | find "FormsAuthentication"
MODULE "FormsAuthentication" ( type: System.Web.Security.FormsAuthenticationModule,preCondition: )

C:\Windows\System32\inetsrv>appcmd list modules | find "UrlAuthorization"
MODULE "UrlAuthorization" ( type: System.Web.Security.UrlAuthorizationModule, preCondition: managedHandler )
MODULE "UrlAuthorizationModule" ( native, preCondition: )

If the output from the commands look similar to above then the necessary modules are installed.

Another thing to ensure is that the WordPress site is hosted in an Application Pool with Integrated Managed Pipeline Mode. To check that run the following command from elevated command prompt (replace “DefaultAppPool” with the name of your AppPool if necessary):

C:\Windows\System32\inetsrv>appcmd list apppools | find "DefaultAppPool"
APPPOOL "DefaultAppPool" (MgdVersion: v2.0,MgdMode: Integrated,state: Started)

The output should contain MgdMode:Integrated.

Configuration steps

Download the zip file below:

IIS Forms Authentication for WordPress (4.1 KB)

Extract the two folders – App_Code and App_Data – into the root folder or your WordPress web site. For example if your WordPress files are located in folder C:\inetpub\wwwroot, then there should be two new files present at the following paths:

C:\inetpub\wwwroot\App_Code\CustomProvider.cs
C:\inetpub\wwwroot\App_Data\Users.xml

The CustomProvider.cs file contains an implementation of membership provider for IIS that uses Users.xml file as a storage for usernames and passwords. To learn more about providers refer to the Membership Providers article. The code for CustomProvider.cs was literally copied from that article.

The Users.xml file is a simple credentials storage. Its content looks like below:

<Users>
  <User>
    <UserName>your_username_here</UserName>
    <Password>your_password_here</Password>
    <EMail>user@ruslany.net</EMail>
  </User>
</Users>

Note that it is important that this file is located in App_Data directory, because this directory, as well as App_Code, is protected by IIS Request Filtering module, which prevents anyone from requesting any files from these folders.

Also it is important to pick a user name and a password different from the ones that are used for the WordPress authentication.

Also extract the Login.aspx file into the root folder of the web site, e.g.:

C:\inetpub\wwwroot\Login.aspx

Now you need to register this custom membership provider and enable Forms authentication. To do that add the following XML configuration element into the <configuration> element inside of the web.config file, located at the root folder of your WordPress site:

<system.web>
  <authentication mode="Forms" />
  <membership defaultProvider="AspNetReadOnlyXmlMembershipProvider">
    <providers>
       <add name="AspNetReadOnlyXmlMembershipProvider"
                type="ReadOnlyXmlMembershipProvider"
                description="Read-only XML membership provider"
                xmlFileName="~/App_Data/Users.xml" />
    </providers>
  </membership>
</system.web>

With the custom provider registered and configured, it is time to configure IIS to protect wp-admin folder with extra authentication that uses this custom provider. To do that add the following XML configuration fragment into the <configuration> element inside of the web.config file, located at the root folder of the WordPress site:

<!-- Deny access to wp-admin for anonymous users -->
<location path="wp-admin">
  <system.webServer>
      <security>
          <authorization>
              <add accessType="Deny" users="?" />
          </authorization>
      </security>
  </system.webServer>
</location>
<!-- Allow access to wp-admin/css folder for anonymous users -->
<!-- this is needed in order for WordPress login page to display correctly -->
<location path="wp-admin/css">
  <system.webServer>
    <security>
      <authorization>
        <remove users="?" roles="" verbs="" />
      </authorization>
    </security>
  </system.webServer>
</location>
<!-- Allow access to wp-admin/images folder for anonymous users -->
<!-- this is needed in order for WordPress login page to display correctly -->
<location path="wp-admin/images">
  <system.webServer>
    <security>
      <authorization>
        <remove users="?" roles="" verbs="" />
      </authorization>
    </security>
  </system.webServer>
</location>

Last thing you will need to do is to configure IIS modules that are used for Forms Authentication and for URL authorization to run for all kinds of requests. Both those modules are Managed modules (that is they are implemented using .NET Framework) and by default they are configured to run only for requests that are made for ASP.NET content. In order for them to work with PHP content then need to be configured to run for all requests. This can be done by using the following configuration section. Add it inside of <configuration>/<system.webServer> element:

<modules>
    <remove name="UrlAuthorization" />
    <remove name="FormsAuthentication" />
    <remove name="DefaultAuthentication" />
    <add name="DefaultAuthentication"
               type="System.Web.Security.DefaultAuthenticationModule" preCondition="" />
    <add name="FormsAuthentication"
               type="System.Web.Security.FormsAuthenticationModule" preCondition="" />
    <add name="UrlAuthorization"
               type="System.Web.Security.UrlAuthorizationModule" preCondition="" />
</modules>

At this point the WordPress wp-admin directory is protected by IIS Forms Authentication in addition to the standard WordPress authentication. Update the users.xml file with the username and password of your choice and then try to request http://localhost/wp-admin/. The IIS Forms Authentication logon page will be shown first:

FormsAuthPage

Once you provided the user name and password as specified in users.xml file, the standard WordPress login page will be shown next. In that page you can provide your WordPress logon credentials:

WordPressAuth

Now the entire content of the wp-admin directory is protected by two different authentication mechanisms, which makes it harder for anyone to hack into it. And the nice thing about this configuration is that it can be done even if your site is hosted on a shared server. All that is necessary is to have ASP.NET and IIS URL authorization enabled on a shared server by a shared hosting provider.

Download all the necessary files as well as an example of web.config file from the link below.

IIS Forms Authentication for WordPress (4.1 KB)

Also check out how this protection actually works by trying to navigate to http://ruslany.net/wp-admin/.

118,691 views

ruslany on February 6th 2009 in PHP, WordPress

PoorFairAverageGoodExcellent (3 votes, average: 5.00 out of 5)

18 Responses to “How to secure WordPress admin directory on IIS 7.0”

  1. Gravatar Imageruslany responded on 18 Feb 2009 at 7:17 pm #

    Update: if you use URL rewriter to enable pretty permalinks in WordPress, then it may cause some javascript errors on login.aspx page. To prevent those errors, modify the rewrite rule for WordPress permalinks as below:

    <rule name="Wordpress" patternSyntax="Wildcard">
    <match url="*" />
    <conditions>
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
    <add input="{URL}" negate="true" pattern="*.axd" />
    </conditions>
    <action type="Rewrite" url="index.php" />
    </rule>

  2. Gravatar ImageDoYouKnow.IN responded on 15 Sep 2009 at 3:51 am #

    How to secure non-Admin Directory?

  3. Gravatar ImageChris responded on 17 Oct 2009 at 3:07 pm #

    Interesting idea but I have some issues/questions:

    Why wouldn’t you use the built-in Basic Authentication for this? Using the IIS GUI you can configure the wp-admin folder for basic authentication and leverage the built in Windows accounts instead of keeping them in a file on your computer.

    You don’t mention anything about encrypting the user xml file. This should be done to prevent accidential exposure from a number of possibilities.

    Also, you don’t mention anything about using SSL, having not one, but two forms based authentication interchanges over http instead of https. In IIS 7.0/7.5 you can use the built-in URL Rewriter to switch all calls to the wp-admin and wp-login path over to https…

    You also don’t mention anything about using different account/passwords for WordPress and for your FBA… I would guess that many users would simply use the same credentials for both, making it pointless to have multiple credential challenges.

    Over all it’s an interesting idea but I think you might want to spend some more time polishing off the recommendation.

  4. Gravatar Imageruslany responded on 17 Oct 2009 at 4:07 pm #

    @Chris: Thanks for your comment – these are very valid questions/issues. Let me try to answer them one by one:

    1. The reason I choose not to use built-in IIS basic authentication is because it relies on the built-in Windows accounts, which makes this option not feasible in a shared hosting environment. Plus with basic authentication the base64 encoded password is sent on every request, while with FBA the password is sent on a login request only, while all the other requests use session cookie. So basic auth may be actually less secure because the password is exposed on the wire for longer time.
    2. You could encrypt the xml file with the password if you want to, but since it is located in App_Data folder it is already protected by IIS Request Filtering module and hence will not be exposed to the site visitors.
    3. Using SSL is the best option of course, but again it is not always possible in a shared hosting environment. If you used SSL then it would not even be necessary to use this extra protection with FBA.
    4. Yes, it is very recommended to use different user name and password for the WordPress and FBA. In fact that’s what I use on my site. I will update the article to make it clear.

    Overall, this kind of protection is not super strong, but it is better than nothing, especially if somebody uses shared hosting environment for their WordPress blog.

  5. Gravatar Imageavn responded on 30 Nov 2009 at 12:02 pm #

    Hi,
    nice and useful.
    Now I followed your instructions and get a secure wp-admin. I have a issue. If I tried to upload a file through new-media.php in flash mode it asking me a login in a window after showing progress on file upload. when giving credentials give me the famous webconfig uncustomized error. In normal mode is working fine.also direct from server is working fine in both modes.
    I suppose that this is due to flash uploader not sending the cookie to server.
    Any ideea?

  6. Gravatar Imageavn responded on 30 Nov 2009 at 12:47 pm #

    update
    this behavior is only in Firefox and not in IE (this is why on the server is working fine as I used the IE) it seems it is a bug/feature:) in how the flash in browser treat cookies needed by IIS.
    Anyway the issue still remain…

  7. Gravatar ImageJohn responded on 08 Dec 2009 at 9:43 am #

    Hello,
    I am confused about the location element. For some reason, the following does not work for me (I am using this technique to secure a PhpMyAdmin site):
    <location path="3_2_4">
    <system.webServer>
    <security>
    <authorization>
    <add accessType="Deny" users="?" />
    </authorization>
    </security>
    </system.webServer>
    </location>

    But the following does work:
    <location path="3_2_4">
    <system.web>
    <authorization>
    <deny users="?" />
    </authorization>
    </system.web>
    </location>

    I thought that in integrated pipeline mode, that the system.webServer namespace was the one that was in charge of securing all files (php,jpg etc), but in my case only system.web works. Can you clarify please? Sorry for the double post, I forgot to encode the first one. Much appreciated.
    John

  8. Gravatar Imageruslany responded on 08 Dec 2009 at 1:14 pm #

    @ John, a possible reason why this does not work is if you do not have IIS URL Authorization module installed and enabled. Open the Server Manager, go to Roles node and then find the Role Services list. In that list check if URL Authorization is installed.

  9. Gravatar ImageJohn responded on 09 Dec 2009 at 8:09 am #

    Ruslany,
    You were right, I did not have URL authorization enabled, and I got confused because I was using VS development server alongside IIS7 (http://www.4guysfromrolla.com/articles/122408-1.aspx cleared this up for me) and the behavior was different for each. So, I am beginning to understand now, but I have a follow up question. Does plugging the asp.net modules into the pipeline (i.e. removing and then adding FormsAuthenticationModule) also enable authentication on static content through the system.web authentication rule? If so, what is the advantage to using the system.webServer namespace? Thanks again.
    John

  10. Gravatar ImageJoey responded on 07 Sep 2010 at 11:55 am #

    Unfortunately I don’t see forms authentication as a checkable module in IIS? I was able to add URL authorization module though from the Add Role Services Section in IIS.

    Am I missing something?

  11. Gravatar ImageJoey responded on 07 Sep 2010 at 12:36 pm #

    Sorry, Using Server 2008 Enterprise Edition.

  12. Gravatar Imageruslany responded on 07 Sep 2010 at 5:09 pm #

    Hi Joey,

    The Forms Authentication should be already installed when you install IIS 7.

  13. Gravatar ImageJoey responded on 14 Sep 2010 at 1:15 pm #

    Yeah i had to add the module in IIS. got it sorted.

    Now i have the following errors.

    Server ErrorInternet Information Services 7.0
    Error Summary
    HTTP Error 500.19 – Internal Server Error
    The requested page cannot be accessed because the related configuration data for the page is invalid. Detailed Error Information
    Module IIS Web Core
    Notification BeginRequest
    Handler Not yet determined
    Error Code 0×80070021
    Config Error This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault=”Deny”), or set explicitly by a location tag with overrideMode=”Deny” or the legacy allowOverride=”false”.
    Config File \\?\P:\inetpub\wwwroot\web.config
    Requested URL http://magazine.lynn.edu:80/
    Physical Path P:\inetpub\wwwroot
    Logon Method Not yet determined
    Logon User Not yet determined
    Config Source
    10:
    11:
    12:
    Links and More InformationThis error occurs when there is a problem reading the configuration file for the Web server or Web application. In some cases, the event logs may contain more information about what caused this error.
    View more information »

    Here is a copy of my web.config file. something is most likely done incorrectly.



  14. Gravatar ImageJoey responded on 15 Sep 2010 at 5:58 am #

    Any idea how to resolve this error? HTTP Error 500.19 – Internal Server Error

    I have read up on it and it says that there are possibly two lines where is added.

    In the last cnfig i sent i didnt see that. I am hping you read this soon. I have to have this configured by friday.
    thanks for your help in advance.

  15. Gravatar Imageruslany responded on 15 Sep 2010 at 9:26 am #

    Hi Joey,

    This error is because some IIS section is locked for modifications on the site level. This can be changed by modifying the file C:\windows\system32\inetsrv\config\applicationHost.config or by using IIS Manager as described here.

  16. Gravatar ImageJ. james johnson responded on 15 Dec 2010 at 3:48 pm #

    This would transmit the password in clear text though…

  17. Gravatar ImageBrent responded on 24 Feb 2011 at 9:38 am #

    I had been using instructions similar to this to allow Forms Authentication to work as a single-signon for Drupal. The weirdly everything worked with integrated pipeline for authenticated users, but anonymous user POST requests received 500.0 errors while GET worked fine.

    After some redirection from a member of groups.drupal.org (http://groups.drupal.org/node/122299) I reverted to “Classic” mode with a wildcard script mapping and this worked. My question is, why? I don’t understand why “Integrated Pipeline” mode did not work for anonymous POSTs.

    My web.config under “Integrated Pipeline” included these modules:

    [XML has been removed. Only encoded XML is allowed]

    and Anonymous POSTs were explicitly allowed in the web.config and worked for ASP.Net, Classic ASP, and HTML in the same Application.

    What don’t I understand here? Thanks for your insights, I have everything working… I just don’t understand why it doesn’t work in integrated mode.

  18. Gravatar ImageJustin Bartlett responded on 22 Apr 2011 at 10:43 am #

    Hi there, I’m interested in having a shared login between a linux hosted wordpress site and an asp.net based cms – Is there any way to have asp.net read the mysql database containing wordpress login info and use that to authenticate users? Or a cookie solution? Any thoughts on sharing login / pass between asp.net and wordpress or pointing me in the right direction would be so very much appreciated.

Trackback URI | Comments RSS

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

XML Markup: If You want to add XML code to the comment please XML encode it first, otherwise the code will not show up.

Recently Published Articles