HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Do something cool with CMS? Show us ...
This board is for 'Answers', and the discussion of answers... Not for questions.
Post Reply
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by martin42 »

I had this working before upgrading to the beta (0.11 Beta 1), but something broke, so I've edited some files to make it work again.

My solution goes like this:-

1. Configure your favourite web server to deny access to "/admin" for HTTP clients.  Allow everything for HTTPS clients.  Also set up a Basic Authentication password file as a pre-login step for all HTTPS connections, so that the PHP admin code is not accessible to the great unwashed.

Now just to add to the complication, I happen to use a webserver with no HTTPS functionality. This means I must use a wrapper such as Stunnel to handle SSL encryption & decryption.  So, there's no actual HTTPS as far as the web server is concerned - just an extra HTTP server instance on a different port on the loopback interface. (This has implications for URI processing later.  The diffs I'm about to give you work either way.)  The same kind of thing would apply if you used an SSL accelerator hardware box.

2. Implement these diffs, to make admin login and logout revolve around two new config.php parameters: admin_url and admin_login_url :-

Code: Select all

# diff config.php.ORIG config.php
33a34,37
> // Added by Martin:
> $config['admin_url'] = 'https://www.example.com/admin/index.php';
> $config['admin_login_url'] = 'https://www.example.com/admin/login.php';
>


# diff /admin/login.php.ORIG /admin/login.php
123c123,124
<                               redirect("index.php");
---
>                               // redirect("index.php");   Edited by Martin
>                               redirect($config["admin_url"]);


# diff logout.php.ORIG logout.php
43c43,44
< redirect("login.php");
---
> // redirect("login.php");  Edited by Martin
> redirect($config["admin_login_url"]);


# diff page.functions.php.ORIG page.functions.php
64,65c64,68
<                               $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
<                               redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
---
>                               // Edited by Martin.
>                               // $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
>                               // redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
>                               $_SESSION["redirect_url"] = $config['admin_url'] ;
>                               redirect($config["admin_login_url"]);
70,71c73,77
<                       $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
<                       redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
---
>                       // Edited by Martin.
>                       // $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
>                       // redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
>                       $_SESSION["redirect_url"] = $config['admin_url'] ;
>                       redirect($config["admin_login_url"]);
Maybe something like this could be put into a future release?  The solution given above has the advantage that it works even when CMSMS doesn't know that Admin content is served over HTTPS, due to a Stunnel loopback or an SSL accelerator.

Cheers!

- Martin.
Ted
Power Poster
Power Poster
Posts: 3329
Joined: Fri Jun 11, 2004 6:58 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by Ted »

Actually, yes, this is something that was suggested before and i totally forgot about it.  I'll put it on my TODO list.
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by martin42 »

Thanks Wishy!

Generally, do you like people to follow up Forum postings with an entry in Mantis, when it seems appropriate?  If so, is "Severity=Tweak" applicable for things like this, to indicate that they're not bugs but only suggestions?

Cheers,

- Martin.
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by martin42 »

Whoops! After more testing, it's clear that there are some more files in /admin that need editing, so I'm reworking this change.  Let's start again with those diffs..

Firstly, /config.php now contains just a one-line addition:

Code: Select all

$config['admin_url_root'] = 'https://www.example.com/admin';
Secondly, we need to change code wherever redirects happen to admin pages, so that the admin_url_root is used explicitly.  There are quite a few files in /admin where this happens. Here are the diffs of the files I've done so far.

Code: Select all

# diff lib/page.functions.php.ORIG lib/page.functions.php
64,65c64,65
<                               $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
<                               redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
---
>                               $_SESSION["redirect_url"] = $config['admin_url_root']."/index.php" ;
>                               redirect($config["admin_url_root"]."/login.php");
70,71c70,71
<                       $_SESSION["redirect_url"] = $_SERVER["REQUEST_URI"];
<                       redirect($config["root_url"]."/".$config['admin_dir']."/login.php");
---
>                       $_SESSION["redirect_url"] = $config['admin_url_root']."/index.php" ;
>                       redirect($config["admin_url_root"]."/login.php");
#
# diff admin/login.php.ORIG admin/login.php
123c123
<                               redirect("index.php");
---
>                               redirect($config["admin_url_root"]."/index.php");
#
# diff admin/logout.php.ORIG admin/logout.php
43c43
< redirect("login.php");
---
> redirect($config["admin_url_root"]."/login.php");
#
# diff admin/deletetemplate.php.ORIG admin/deletetemplate.php
80c80
<       redirect("listtemplates.php");
---
>       redirect($config["admin_url_root"]."/listtemplates.php");
84c84
<       redirect("listtemplates.php?message=".lang('errortemplateinuse'));
---
>       redirect($config["admin_url_root"]."/listtemplates.php?message=".lang('errortemplateinuse'));

I'll post the remaining diffs when I've done them.
Ted
Power Poster
Power Poster
Posts: 3329
Joined: Fri Jun 11, 2004 6:58 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by Ted »

There's going to be some issues with the module API as well, since right now it only adds the admin dir name to moduleinterface.php to generate certain links and redirects.
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by martin42 »

OK, I'll grep for 'admin' more widely ;-)

Thinking aloud... I guess two further security lockdowns would be:-

1. Make all the files/dirs read-only and owned by root, except for /uploads (read/write to the HTTPS user) and /tmp (read/write to both HTTP and HTTPS for session management).  BTW, is session management actually necessary for read-only users? Can I disable it?

2. Use different config.php files for HTTP and HTTPS (mirror the site directory contents using symlinks) so that the HTTP site accesses the DB with read-only privileges, except for the session management table(s).

That way, if a security hole ever emerges in CMSMS, the DB content ought to be untouchable, and only the /uploads directory can be defaced, at least without an OS- or DB-privilege escalation exploit.

BTW, is there an easy way to lock down PHP to prevent the possibility of arbitrary OS commands being executed if someone ever manages to attack a PHP page script?  I don't want to break any required funtionality, and chroot-ing seems a bit extreme.  I do have a PHP basedir in effect, but I guess basedir applies only to file access from within PHP, not from a system() call for example.

Thanks in advance for any lockdown tips ;-)
User avatar
sjg
Power Poster
Power Poster
Posts: 310
Joined: Thu Jan 27, 2005 5:11 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by sjg »

Here are a few ways to lock down PHP... it's not an exhaustive list, and probably none of these things will protect against bugs in the PHP core (like buffer overflows, if they exist).
  • put "expose_php = Off" into your php.ini, so hackers won't have a simple scan to find which version of PHP you're running. Of course, if they have an attack on CMS Made Simple, they'll be searching for that rather than PHP. Still, every bit helps.
  • put "display_errors = Off" into your php.ini, so less information gets displayed to attackers when they're trying to do SQL or parameter injection or something.
  • Add a disable_functions list to your php.ini file. This basically allows you to list functions that cannot be used by PHP scripts. You'll have to balance which ones you list with which ones are needed by CMS Made Simple and/or other PHP scripts you're running. Do a Google search to see some typical examples.
  • I don't think CMS Made Simple will work in PHP's "Safe Mode." You can read a lot of interesting rants about the failings of Safe Mode on other sites. Once again, if you're interested, Google for it.
Then again, Googling "Lock Down PHP" or "Secure PHP" will get you more and similar recommendations.

Good Luck!
___Samuel___
Many modules available from the http://dev.cmsmadesimple.org
The CMS Made Simple Developer Cookbook is now available from Packt Publishers!
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

Restrict Admin functionality to HTTPS only

Post by martin42 »

For anyone interested, here's a summary of the changes for keep HTTPS Admin from trying to reference HTTP...

In root directory of the CMSMS install, add this line to config.php:

  $config['admin_url_root'] = 'https://www.example.com/admin';


In directory "admin", files *.php, change all occurrences of 'redirect' to use admin_url_root, e.g.

redirect($config["admin_url_root"]."/login.php");


In directory "lib", change redirect calls, and redirect_url assignments, in file page.functions.php :-

                              $_SESSION["redirect_url"] = $config['admin_url_root']."/index.php" ;
>                              redirect($config["admin_url_root"]."/login.php");

                      $_SESSION["redirect_url"] = $config['admin_url_root']."/index.php" ;
>                      redirect($config["admin_url_root"]."/login.php");


In directory "lib", change redirect() definition in file misc.functions.php to match the TCP port that your HTTPS server is listening on :-

    $schema = $_SERVER['SERVER_PORT'] == '4443' ? 'https' : 'http';

I only recently discovered this schema check in misc.functions.php. This fixes News editing from HTTPS.  Maybe some of the edits in /admin/*.php are not needed with the correct port number set up for the Scheme check.

It all seems to work so far.  Only issue is that addcssassoc.php doesn't save changes. Don't know if that was a pre-existing issue, but it's not something I use anyhow.

Now for some PHP lockdown.  (Thanks Samuel, I'll Google some more!)
wesyah234
Forum Members
Forum Members
Posts: 60
Joined: Tue Mar 07, 2006 4:30 am

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by wesyah234 »

has any progress been made on this?
martin42
Forum Members
Forum Members
Posts: 126
Joined: Sat Aug 20, 2005 11:35 pm

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by martin42 »

Funny you should ask that.  I was just wondering how much effort it would be to upgrade to the new 1.04 release, as I'm quite a long way behind now.  For what it's worth, my CMSMS website has been working pretty well with all the admin stuff confined to HTTPS,  using Basic Auth login as a wrapper round the HTTPS tree, giving as an extra layer of defence.  To be fair though, I only use a small subset of the functionality in CMSMS modules etc, so this approach might not work for other sites.

@Wishy: Is there any chance of committing the "$config['admin_url_root']" changes to SVN, if I prepare a set of diffs against 1.04?    That would make future upgrades easier.

For this stuff to be of any value however, the installer will always have to do some tricky lockdown work on his web server installation, so that he ends up with two web server instances (HTTP and authenticated HTTPS) running under different Unix User IDs with different PHP.INI files (the HTTP one being hardened).  The Unix userID for HTTP users needs only read-only RDBMS permissions, no write access to any folder, and no read access to the /admin, /lib, or /modules folders. 

You might consider an SSL wrapper to drop connections from clients without the right private SSL certificates, to prevent brute-force password guessing attacks against HTTPS Basic Auth login. Or you could use ACLs based on client IP addresses.

You might want to lose those plain-text RDBMS login credentials out of the PHP code, and use the Unix user ID to give you automatic auth to the RDBMS.  So that an arbitrary file read vulnerability wouldn't give those credentials away!

I would write a more detailed lockdown guide for all this, based on the webserver software I use, but I don't suppose anyone else runs Mathopd + PHP + CMSMS anyway.  Most people probably use Apache, or possibly IIS if they're feeling courageous!  So by publishing a lockdown guide I'd just be helping people identify any vulnerabilities in my installation ;-)

Hope that helps - though I guess it doesn't really answer your question!

PS: I do vaguely wonder whether it wouldn't be easier to combine friendly URL rewriting with the use of 'wget --mirror' so that you gain a dynamic CMS for editing, with an export function that writes out to a static website tree.  That would also have the benefit that of letting you delay exporting the CMS tree until you were happy with each batch of content changes.  But of course that way you'd lose the ability to offer any truly dynamic content: user votes, signup forms, etc etc.
wesyah234
Forum Members
Forum Members
Posts: 60
Joined: Tue Mar 07, 2006 4:30 am

Re: HOWTO: Restrict Admin functionality to HTTPS only, and add two-stage login

Post by wesyah234 »

I'm just looking for a simple solution that would allow me to have one url for the website, and a completely different url for login and subsequent admin pages. 

That way I can use:

http://mysite.com for the main site,

and:

https://mysharedhostwebserver.com/~mysite/admin/

for all my admin pages

I can go to this type of url to login, but the actual login post goes back to the non secure site, because the single configuration directive for the site url is used for the admin pages too.
Post Reply

Return to “Tips and Tricks”