Published on MacDevCenter (
 See this if you're having trouble printing code examples

Web Apps with Tiger: Security and MySQL

by Kevin Hemenway
Your Life in Web Apps

In our "Getting Started" part of this series, I was a little too fast and a little too slow. Like a confused lover, I went too fast because I didn't want to bore you with protracted explanations and foreplay about topics we may both already know. On the other hand, it was a little too slow because we really didn't accomplish much besides enabling PHP and verifying it worked.

In this, our second part of "Web Apps with Tiger", we'll be focusing on protection. We'll replace the default PHP configuration with a more secure version, and explain some of the differences. Finally, we'll install MySQL, our database server, and run through its own security tweaks. In the end, we'll be satisfied, but certainly desiring more.

Now Is the Time for Caring

In the last article, we finished with an index.php file containing the contents below. When saved into /Library/WebServer/Documents/ and accessed from, we saw a boatload, nay, multiple boatloads, of configuration options relating to PHP. Load up that file again, as we'll be exploring some of what we see.

<?php phpinfo(); ?>

The first line of interest is Configure Command. This contains the exact configure command (duh...) Apple used to build the PHP module for Tiger. Since most additional features and extensions of PHP are enabled during this configure process, we can get a good idea at what "extras" we're getting by examining this line: Apple has seen fit to enable command-line usage, XML parsing, EXIF information for images, FTP support, and so on. For those in the know, you'll be particularly distraught to see no image creation or modification capabilities, such as GD. This is the same as it has always been, and I'll address it in the future as necessary.

Next is the Configure File (php.ini) Path, which alludes to a file named /etc/php.ini where we could fiddle with our PHP settings. This file doesn't actually exist, meaning that PHP starts up with all its default configuration. This default config is represented by what we do have: /etc/php.ini-default. We could, if we wanted, rename that to php.ini and make changes there. Instead, however, we'll be using an entirely different source.

Switching to a More Secure php.ini

Open the /etc/php.ini-default in your favorite editor and read the first few lines:

; This is the default settings file for new PHP installations.
; By default, PHP installs itself with a configuration suitable
; for development purposes, and *NOT* for production purposes.
; For several security-oriented considerations that should be taken
; before going online with your site, please consult php.ini-recommended
; and

Even with this admonishment, very few people actually switch to something stronger: inevitably, they code, they code, they code, they release, phew! All done, time for vacation, forget all about security. Before we get serious with any sort of PHP application, we're going to switch to the "recommended" version of the php.ini. It won't be an oversight.

Annoyingly enough, this configuration doesn't exist on our hard drive, and is quite difficult to find just layin' around in a Google result. Instead, we'll have to download a brand new source installation of PHP, just to grab the one file we're interested in: php.ini-recommended. Extract the archive and move this file into its proper place:

morbus@:~/Desktop > tar xzf php-4.4.0.tar.gz
morbus@:~/Desktop > sudo mv php-4.4.0/php.ini-recommended /etc/php.ini
morbus@:~/Desktop > ls -l /etc/php.ini
 -rw-r--r--   1 morbus  staff  39480 Apr 28 09:14 /etc/php.ini

With that done, delete the archive and restart Apache:

morbus@:~/Desktop > rm -rf php-4.4.0*
morbus@:~/Desktop > sudo apachectl restart
 /usr/sbin/apachectl restart: httpd restarted

Reload the index.php page; unless you've been incredibly astute or have been watching one single variable, you probably won't notice anything has changed. For an explanation of what this process did, along with reasons why, open the /etc/php.ini in an editor and read through the intro regarding register_globals, display_errors, log_errors, etc. Of chief and immediate interest is that PHP errors will no longer be sent to the browser: you'll have to check your error_log whenever PHP code doesn't seem to work correctly. I'll throw in another tailDash recommendation: it allows you to simply F12 your way to log viewing.

While we're in here, we may as well make two other small changes that I prefer, but which are certainly not required. The first relates to the memory_limit setting, which measures how much memory a PHP script can consume. The default is 8M--I usually set this anywhere from 16M to 64M, depending on the types of scripts I plan to be running, for how long, and the capabilities of the hardware itself. 24M is a decent enough value to start with. Generally speaking, if a single script is exceeding a 24M-memory limit, something is usually amiss.

I also like to turn Off the allow_url_fopen feature, though this is more cautious paranoia than any tried-and-true trick. This setting allows the PHP fopen function to load remote documents stored on other web servers, and has been implicated in a number of break-ins with poorly designed applications. Turn it Off for now, and if you ever need to use it in the future, you'll probably be smart enough to make a more informed decision.

If you made either of the above changes, restart Apache again.

Your Life in Web Apps

Essential Reading

Your Life in Web Apps
By Giles Turnbull

Have you dreamed of a simpler life where web apps and a browser meet all of your computing needs? All you need is a network connection. In this PDF Giles Turnbull introduces you to a day of web apps-only, then he surveys the best and most innovative web apps from the current crop available right now. He also addresses practicality, security issues, and backup strategies for living the web app life. Is it really possible? This PDF will help you decide.

Read Online--Safari
Search this book on Safari:

Code Fragments only

Installing MySQL, the Database Server

Most popular web apps require some sort of database back end, and there are two major open source players: MySQL ("My Ess-Que-Ell") and PostgreSQL ("Postgres Que-Ell"). Which one is "best" is as embroiled a flame war as any other. Some applications work only with MySQL, some only PostgreSQL, some both, some in between. The ones we'll be exploring throughout this series are generally tested and developed with MySQL, and the popular web acronym "LAMP," or "Linux + Apache + MySQL + PHP/Perl/Python," asserts MySQL's popularity.

At the time of this writing, the recommended version of MySQL is 4.1.13. Head on over to their download pages, scroll nearly all the way to the bottom, and choose the "Standard" version of the "Installer package (Mac OS X v10.4)." The "Debug" and "Max" versions are special builds that aren't necessary for our needs. Once the .dmg has finished downloading, mount it as usual and double-click the .pkg that reads "mysql-standard-4.1.13-" blah-blah-blah.

Once this .pkg is finished, you'll have a complete MySQL installation in /usr/local/mysql. Next, double-click the other .pkg in the archive: the MySQLStartupItem. This, oddly, ensures that MySQL will load every time your machine restarts. Nothing too exciting for that install either, which leaves us with the MySQL.prefPane. This Preference Pane gives us a cute little GUI to start-and-stop MySQL manually, which is something we'll rarely need to do. To install for just your user, drag it into ~/Library/PreferencePanes; for every user on your machine, use /Library/PreferencePanes.

Tweaking the Shell and Securing MySQL

The easy part is finished: MySQL is installed. Now we have to worry about our environment--imagine me waving my hands emphatically at the empty air around me. We're going to fiddle with our shell $PATH, which'll allow us to refer to our newly-installed programs as just mysql or mysql_secure_installation instead of the much more laborious /usr/local/mysql/bin/mysql.

How to do this depends on which shell you're using. The default OS X shell is tsch and its configuration lies in ~/.tcshrc. On the other hand, if you've told Terminal to use bash instead, config tweaks go into ~/.bash_profile. Open (or create) the file that corresponds to your shell.

For tcsh, add the following to ~/.tcshrc:

setenv PATH ${PATH}:/usr/local/mysql/bin

For bash, add the following to ~/.bash_profile:


Restart Terminal (so that the above changes will take effect) and run the following command: mysql_secure_installation. If everything has gone as intended, a new utility should start (as opposed to an error message about "command not found"). This utility leads us into our conclusion of this part of our series: securing MySQL. The loud intro admonishes:


But, honestly, this is a healthy step to use for any MySQL server, whether you're merely setting up a dev box or maintaining a database-driven list of all your "romance" movies. ("Research! I swear!") Thankfully, this script is more than happy to hold your hand throughout the process, and you can follow along with the desired answers and expected output below--I've bolded the places where your input is required.

In order to log into MySQL to secure it, we'll need the current
password for the root user. If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none): 
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into
the MySQL root user without the proper authorization.

Set root password? [Y/n] y
New password: 
Re-enter new password: 
Password updated successfully!
Reloading privilege tables..
 ... Success!

By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
 ... Success!
Normally, root should only be allowed to connect from "localhost." This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
 ... Success!

By default, MySQL comes with a database named "test" that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all
changes made so far will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...

All done! If you've completed all of the above
steps, your MySQL installation should now be secure.

Grrr... I Wanna Do Sumthin'-Sumthin'!

Sadly, you'll have to wait until the next installment before we get our hands dirty with a real-life, honest-to-goodness web application. These first two articles have focused on the foundation of a production-ready web server, with an eye toward battening down the hatches of PHP and MySQL. And, as the tired cliche goes, it is only with a good and strong foundation that we can build something magical. As before, if you have specific web applications or features you'd like to see covered in this series, or questions about the tweaking of MySQL and PHP, don't hesitate to leave a comment below.

Kevin Hemenway is the coauthor of Mac OS X Hacks, author of Spidering Hacks, and the alter ego of the pervasively strange Morbus Iff, creator of, which bills itself as "content for the discontented."

Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.