
1% of CMS-Powered Sites Expose Their Database Passwords (2011) - feross
http://feross.org/cmsploit/?hn=1
======
sequoia
0\. Put local config files outside of webroot or other publicly accessible
directories, include programatically.

1\. Disable swap/backup files in your editor (or write them to a different
location)[1]

2\. Git ignore or SVN ignore swap files and backup files

3\. Configure your web server to not serve such files.

Some combination of these should keep you safe. :)

[1]: in vim: noswap nobackup nowritebackup or
[http://vim.wikia.com/wiki/Remove_swap_and_backup_files_from_...](http://vim.wikia.com/wiki/Remove_swap_and_backup_files_from_your_working_directory)

~~~
astrodust
Step 0 is the only way to do it. I'm constantly stunned to see how many PHP
apps violate this rule.

~~~
trebor
You've obviously never had an amateur developer run smack-into open_basedir
issues before. The application basically gets a 404 error when including
dependencies, until they look for open_basedir configuration. A developer
really needs to know when open_basedir has restricted an include, because the
error is really vague.

And shared hosts don't generally allow files outside the webroot of a site.
When they do, they might already have open_basedir set to only allow inclusion
WITHIN the webroot.

Most packages like Wordpress are developed to run on the maximum number of
servers. It isn't built to run on the best, or smartest configured ones—but
the average, out-of-the-box Apache/PHP install.

~~~
astrodust
The PHP ecosystem has some really awful defaults which is why it's developed
such a bad name for itself. It may be that many of these are there to
accommodate misconfigured web hosts, but this behavior has a way of self-
perpetuating.

Example: PHP 5.5 will be running for the next billion years because when
mysql_query is finally put to rest in newer versions all those applications
that depend on it will fall over. Thus, a _legacy_ version of PHP will be
supported by hosts, bugs, security holes and all.

"You either die a hero, or live long enough to see yourself become the
villain..."

PHP crossed the line from hero long ago.

~~~
kysol
Actually, it comes back to the user, not so much the coder. After I left a job
a while back they hired some random as a replacement. The replacement had no
Linux experience, so the next best thing for them was to tell the owner to
move all software to a shared host (which they had had experience with
before).

Everything was written below a public root, but the shared host didn't allow
this, so the next thing you saw was: domain.com/publicroot/index.php^ when the
new developer took over. The funniest thing was that they had internal
subdomains which ended up public as well, one with full customer listings.

"Never attribute to malice that which is adequately explained by stupidity." -
Hanlon's Razor

^ fake path

------
calinet6
Lol. That's just called statistics.

I bet you 1% of _every_ web site or server in existence does something so
blatantly and unthinkably wrong that it would make any sysadmins eyes pop out
of his head.

1% is in fact extremely low in my opinion—my guess would be closer to 10-15%
of sites have some glaring security hole.

The only important thing is to not let it be _your_ site.

~~~
im3w1l
Or a site holding data of yours, which is harder.

~~~
calinet6
Exactly. This is why you use different passwords for every site.

------
nodesocket
Prevent nginx from serving files that start with a period or tilde will help.

    
    
        # prevent hidden files from being served and logged
        location ~ /\. {
          access_log off;
          log_not_found off;
          deny all;
        }
    
        # prevent tilde files from being served and logged
        location ~ ~$ {
          access_log off;
          log_not_found off;
          deny all;
        }

~~~
romaniv
This is a bit of a hack, since you don't really know how the "backup" file
will look like. It might end with tilde, but it might end with .bkp. It's
impossible to iterate through all the possibilities, just like it's impossible
to separate them from valid files.

A much cleaner solution would be to separate "callable" entry-point files
(like index.php) from "library" files into separate directories and point
nginx/Apache only to the directory with callable files.

------
antihero
How utterly stupid do you have to be to engineer software that requires you to
have configuration files in a publicly accessible folder? Even most shared
hosts now will have a public_html folder and the ability to put sensitive
stuff not inside it.

The .htaccess hacks are great but they are just patching the symptom, and one
slip up and you're back to square one.

The best way to do configuration is to have it in environment variables that
are populated in a completely separate config file for the specific instance,
so for instance a uWSGI ini file.

This is very easy with Django and uWSGI.

In your uWSGI.ini:

    
    
        env = DJANGO_SETTINGS_MODULE=yoursite.settings.production
        env = DJANGO_SECRET_KEY=herp
        env = DJANGO_DB_PASSWORD=derp
    

In yoursite/settings/production.py

    
    
        from .base import *
    

In yoursite/settings/base.py

    
    
        import os
        
        DATABASES = {
            'default': {
                ...
                'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD'),
                ...
            }
        }
    
        SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
    

I'm not sure if this is possible with WordPress or the whole shitty PHP way of
doing things - perhaps if you had a FPM pool for each site and site specific
configuration in there?

~~~
killahpriest
_How utterly stupid do you have to be to engineer software that requires you
to have configuration files in a publicly accessible folder? Even most shared
hosts now will have a public_html folder and the ability to put sensitive
stuff not inside it._

Complete untrue. You can (and are advised to) move your _wp-config.php_ above
_public_html_. The reason its not done by default is because you just can't do
this on many of the crappy hosting services, and it creates a barrier for
entry for people making their first-ever site.

~~~
antihero
Except you can on 99% of crappy hosts. I remember even Tripod allowed you to
put things outside of public_html.

~~~
dsl
But things inside public_html can't read files outside of it. I believe cPanel
is configured this way be default.

------
Yaggo
The fundamental problem with many PHP applications is that the php files are
located in document root, i.e. can be potentially served as static files. More
correct approach would be to put them outside document root (e.g.
APP_DIR/lib), and using index.php which requires them (e.g.
APP_DIR/static/index.php).

~~~
mylittlepony
That's not restricted to PHP, since it does not even depend on the language.

~~~
Yaggo
True, but other languages are not typically processed in the traditional
mod_php way where urls are directly mapped to files in your static root, thus
it's extremely rare to see e.g. python or ruby files served as static files.

~~~
mylittlepony
I don't know what you mean. You just tell the web server what you want to do,
and it will do it. Eg: "Process only files in this dir, and only accept
requests to this kind of URL. And any request to that kind URL, pass it to
this single php file".

Do software like XAMP have bad defaults and shouldn't be used for production
out of the box? Maybe.

Has that helped a lot of people to get started, and make PHP the most popular
server language for the web? Definitely.

Should we mock _all_ php devs including experts, because of designers or ninja
CEOs who tried/succeeded in editiing or making a website but made some
mistakes in the process? The answer is still no.

------
anu_gupta
The title of the linked post is _1% of CMS-Powered Sites Expose Their Database
Passwords_ \- has it changed since submission?

~~~
Torn
Could be HN mod editorialising at work

~~~
switz
Don't start assuming without evidence. I don't support the changing of titles,
but I'm not going to blame _the mods_ for something they didn't do.

~~~
switch007
Perhaps "it could be..." was merely a suggestion rather than an accusation?
(It's within the realm of possibility based on empirical evidence.)

------
josefresco
To protect the wp-config.php file using htaccess is a well known security
precaution for any WP pro.

The issue however is if you have a _copy_ of said file, in which case
protecting the main version would be useless.

Also I'd say the _blame_ if you can assign any is on Vim, Emacs, Gedit, and
Nano which would have had to crash in order to set these chain of events in
motion.

~~~
damncabbage
> _Also I'd say the blame if you can assign any is on Vim, Emacs, Gedit, and
> Nano_

... Or the guy who is just editing files directly on the server (or shovelling
everything up with FTP), instead of sane version control.

(I'm not sure anyone doing the former can be counted as "Pro", WP or
otherwise.)

~~~
sirclueless
Please don't put your database passwords into version control.
[https://github.com/search?q=path%3Awp-
config.php&ref=sim...](https://github.com/search?q=path%3Awp-
config.php&ref=simplesearch&type=Code)

(A) Database passwords are a security risk. You don't want them escaping to
other servers when you clone or share code.

(B) Wordpress is commodity software. You are unlikely to need custom
modifications to the source. "I'm on version 3.4.2" is sane enough version
control.

(C) Database passwords are ephemeral. You don't need to keep a durable record
of them. If your hard drive fails or you otherwise lose your record of them,
you can just change the password. What is needed is a good backup strategy,
not a durable password.

I would say live editing wp-config.php outside of version control is the
_best_ strategy, until you start needing to synchronize multiple webservers.
At that point you should switch to some deployment and configuration manager
like Chef or Puppet, not put passwords into version control.

~~~
damncabbage
You're right; on reflection, version control has nothing to do with it
(besides a .gitignore with wp-config-anything ignore entries). I used to use a
wp-config.php file in a shared directory on the server that gets symlinked in
by Capistrano during deployments; there's no way to accidentally create new
.swo/.bkp/.whatever files in production without both screwing up the
Capistrano recipe _and_ wp-config.php file creation.

(It seems I skipped actually writing something useful and went straight to
durr version control, sorry.)

Points A) and C) are dead on. Addressing your point B), though:

Yes, it is commodity software. But most seem to use at least _some_ plugins
and themes, and making changes to those without a rollback is a nightmare. You
can make backups before changing things, sure, but version control has that
already built in.

(Sibling posts to yours point out that Wordpress looks in both the docroot and
one level up for wp-config.php - if I ever manage a Wordpress site again I'll
be sure to move it out.)

------
cdmckay
Yii places all config information (and any files that shouldn't be accessed by
a visitor) in a folder called "protected" that is behind a "deny from all"
.htaccess file. WordPress should consider something like that.

------
ck2
While this is bad of course, you also would have to allow network access to
your mysql from remote ips.

Which if allowed is even more stupid.

If you run mysql only locally then do _skip-networking_ and if you have to
have networking restrict the ips it's allowed from.

If they can use the mysql password locally, well then you have far bigger
problems with security than mysql and exposed php configuration files.

~~~
jrnkntl
You can always try to append "/phpMyAdmin" to the root url, you'd be surprised
how often that works.

------
nikcub
The first time I read a vulnerability report about text editor backup or swap
files being served as pain text was probably in the mid-to-late-90s

Some things never change.

------
eksith
For the majority of users that use PHP: A lot of hosts now allow editing of
php.ini which isn't accessible to the web (or else it's time to switch hosts).

You can store the login credentials there :
<http://www.php.net/manual/en/pdo.construct.php>

This is another reason why you should move away from mysql(i) to PDO.

Or if that doesn't work, try storing in the .htaccess or Apache conf
environment variables : <http://stackoverflow.com/a/2583857>

------
brokentone
This is another great reason to "keep config files out of web root." WordPress
supports this, although it's not by default, and poorly documented, and even
suggested it's not beneficial.

[http://codex.wordpress.org/Editing_wp-
config.php#Configure_D...](http://codex.wordpress.org/Editing_wp-
config.php#Configure_Database_Settings)
[http://codex.wordpress.org/Hardening_WordPress#Securing_wp-c...](http://codex.wordpress.org/Hardening_WordPress#Securing_wp-
config.php)

------
mobweb
My small-ish personal website gets hammered everyday by bots trying to access
obscure config files and installer scripts from various CMS I have never even
heard about.

I think the chance of successfully obtaining DB credentials using this method
is fairly high even without those "backup files" mentioned in the link.

I'd assume there are many people out there who can set up a simple CMS
themself but ignore/don't understand how to properly remove the various
"installer" and "configurator" scripts once they're done...

------
itry

      "If the text editor crashes or the SSH connection drops during editing"
    

What about the time of editing? During that time, the temp files exist. Are
they readably? I would guess so. So even if you just edit them and have to
crash, you are vurnurable.

------
MattBearman
This is why everything except a font accessor (index.php) should be stored in
a directory behind the publicly accessible web root directory.

It's standard practice for most web frameworks, and yet it seems the most
popular CMSs don't bother.

~~~
analog
It would make the CMSs harder to deploy, security gets traded off for
convenience.

~~~
IgorPartola
Apache could make this very easy. The default htaccess file could set this up.

------
program
As a side note a lot of Wordpress sites do expose the wp-config-sample.php
file. See for example the official Yahoo! Blog, The Wall Street Journal, Sony
Blog, etc...:

<http://ycorpblog.com/wp-config-sample.php>

<http://blogs.wsj.com/law/wp-config-sample.php>

<http://blog.us.playstation.com/wp-config-sample.php>

...

------
tolos
> Thus, 230 / 216391 = 0.11% of all websites are vulnerable.

> Latest stats say that about 13.8% of the top 10,000 websites run CMSs. If we
> just focus on CMS-powered websites, then the percentage of vulnerable sites
> is much higher:

> Thus, 230 / (216391 * 0.138) = 0.77% of websites running a CMS are
> vulnerable.

I don't think those numbers mean what you think they mean.

13.8% out of 10,000 doesn't say much about the top 216,391. And perhaps 0 out
of 230 of the vulnerable websites use CMS, however unlikely that is.

------
jamesbrennan
When I'm working with CodeIgniter (which ExpressionEngine is based off), I
always rewrite access to subfolders of the project to the index.php file,
because that's the only file that there needs to be access to for the site to
function properly. This keeps anyone from accessing configuration (or any
other) files.

    
    
      RewriteCond %{REQUEST_URI} ^somefolder.*
      RewriteRule ^(.*)$ /index.php?/$1 [L]

------
pioul
Quite an old issue (2011), did anybody run the script to see if as many sites
still publicly expose these files?

~~~
jorde
It would also be interesting to know if any of the big CMS vendors
(Automattic, Drupal etc) has done anything to prevent this in the newer
releases.

------
xguru
<Files ~ "(^#.*#|~|\\.sw[op])$"> Order allow,deny Deny from all </Files>

found this in comment, it's useful to me

------
jlgaddis
This is not anything new and has been known for ages.

Assuming one follows the "defense-in-depth" approach, however, this is easily
mitigated and becomes a non-issue.

FWIW, I could give you the username and password that my instances of
WordPress use to connect to MySQL and you wouldn't be able to do anything with
them.

------
belorn
Interesting security concern. It might be added that if one has several backup
files, and has MultiviewsMatch enabled, the files aren't actually readable but
get run instead. This is because multiple backups get numbered after a dot,
while single backups and crashed files do not.

------
thejosh
Wow.

I have never thought about this, as I'm super paranoid about database
credentials stored in any sort of config file (where it's wp-config.php,
settings.php or any other language with frontfacing credentials).

Great writeup ferross!

------
mmorey
To find VIM swp files you can run this command on your public_html directory:

    
    
        find . -name "*.swp"
    

You can find and remove them with:

    
    
        find . -name "*.swp" -exec rm -f \{\} \;

~~~
mauvehaus
A solution using xargs instead of exec:

find . -name "*.swp" | xargs rm

I believe find -exec forks and execs once for every file found, xargs may only
fork and exec once, provided that all of the files find finds fit in a single
command line. Hopefully not an issue in this case, but it can greatly speed up
a deletion when you have a lot of files to find and delete.

[EDIT] Doesn't properly handle spaces in filenames, see the comments below for
other slick solutions.

~~~
emillon
Technically, your solution does not work with files whose name contain a space
or newline. A working solution is either to do '-print0' in find and '-0' in
xargs, or to just use '-delete' instead of the -exec. It is also possible to
use '+' instead of ';' with '-exec' to say "fork as few times as possible", ie
pack the largest list of arguments to rm you can. But '-delete', when
supported, won't even fork/exec.

~~~
mauvehaus
Are these GNU find extensions? I spend a lot of time on solaris, and I don't
remember seeing a delete option. Thanks for the tips though!

~~~
emillon
You're right, -delete is not standard, but I think that + and -print0 are.

------
larrys
Would also note that if php for some reason is turned off the server will send
the actual config file wp-config.php as well as well as all those other files
that are mentioned by the OP.

------
easternmonk
Just do a google search for "filetype:sql @gmail.com" and you will find
thousands of `users` table dumps.

I dont know how useful it is for passwords but very useful for email spamming!

------
javipas
The author helps with an Apache rule:

<Files ~ "(^#.*#|~|\\.sw[op])$"> Order allow,deny Deny from all </Files>

What would be the equivalent of this for Nginx?

~~~
chrisguitarguy

        location ~ "(^#.*#|~|\.sw[op])$" {
            return 401;
        }
    

Or something along those lines.

Nodesocket's answer is good as well:
<http://news.ycombinator.com/item?id=5164017>

~~~
antihero
Not sure if it's actually that helpful but might be nicer to serve up a 404,
in the interest of opacity.

Simply giving the hacker less information (though not just depending on this)
is a useful form of security. If you give them a 401, then they at least know
that the file exists.

------
wyck
OP please fix your title, the article states "1% of CMS-Powered Sites" NOT "1%
of WordPress Sites".

Also you can move wp-config.php outside the root.

------
level09
how much % of these 1% websites have their database server allowing remote
connections ?

does the DB password mean anything without allowing remote connections ?
(let's forget shared hosts for a moment)

~~~
dspillett
Assuming there are no other bugs in the CMS in question, and no other apps are
installed with access to the same server, and the admins implement good
authentication policies, there is no danger from this.

But...

It is rare there is only one app on a server, even a non-shared one, so this
information could be used in conjunction with this information in order to
cause bother. It is not uncommon to see something like phpMyAdmin installed in
a standard location on a given domain and not locked down well - for sites
where this is the case this bug is very serious.

It is scary how many people out there calling themselves sysadmins use the
same password for everything to do with a given service, or even for
_everything_ \- for them this is a bigger problem as revealing the DB password
also reveals the credentials needed to access other things (perhaps an SSH
account with privileged access either directly or via sudo).

~~~
stcredzero
_> It is scary how many people out there calling themselves sysadmins use the
same password for everything to do with a given service, or even for
everything_

That should immediately mark them as a Dunning-Krueger victim/hack and you
should get someone else. If you're not using something equivalent to a
password vault or something equivalent, you're not as security savvy as you
think you are.

