## LAMP HOWTO

David
Lechnyr, MSW, MCSE+I, CNE

    david@lechnyr.com

3.05
2003-07-01
drl
MySQL configuration updates, PHP5/Apache2 comments, PHP Superglobals
3.0
2003-06-01
drl
Major updates & conversion to SGML -- enjoy! :-)
2.0
2002-12-15
drl
Added sections: imapd, mm, mhash, mcrypt
1.0
2001-11-08
drl
Initial draft

The goal of this document is to provide assistance in building a Linux-based Apache, MySQL and PHP webserver, affectionately known as `LAMP`. We'll also cover the optional installation of bells and whistles including, but not limited to, secure imapd/ipop3d, OSSP-mm shared memory, mhash/mcrypt encryption and so forth. Many documents assist you in building Apache with another single package, but for using more than one package the waters start to get a little murky and the instructions start to contradict. Hopefully this document will alleviate some of these issues.

## Apache

Apache is an HTTP server designed as a plug-in replacement for the NCSA HTTP server. It fixes numerous bugs in the NCSA server and includes many frequently requested new features, and has an API which allows it to be extended to meet users' needs more easily. Apache is the most popular web server in the known universe; over half of the servers on the Internet are running Apache or one of its variants.

### Apache 1.3 vs. Apache 2.x

This document covers the installation of Apache 1.3.x. Currently, both PHP 4 and 5 support for Apache 2.0 is `experimental` and not recommended by the PHP team. The main issue is thread safety, which requires a lot of code verification time and effort. See http://www.php.net/manual/en/install.apache2.php for specifics.

### Verify PGP signature

It's recommended that you verify the PGP key for the Apache package before doing anything else. Download the Apache PGP KEY file from http://www.apache.org/dist/httpd/KEYS and run:

    bash$ gpg --import KEYS
    bash$ gpg --verify apache-1.3.x.tar.gz.asc

If you receive a message like, "Good signature from user..." then all is well. The warnings about trust relationships can be ignored.

### Initial Preparation

Next, we need to configure the source tree for Apache first before installing any of the other associated programs.

    bash$ tar xzf apache-1.3.x.tar.gz
     $ cd apache-1.3.x
     $ ./configure

You'll get a warning message about compiling with default settings; don't worry; we will do the actual configuration of apache later. This is just a preliminary configure, required just to get things going. Without this, some of your other packages won't install into the apache source tree correctly. You'll also need to create an apache user and group if you want to run Apache as a user other than root, which is highly recommended.

## MySQL

MySQL is a fast, multi-threaded, multi-user and robust SQL (Structured Query Language) database server. It provides a nifty API which makes it easy to integrate into other applications, including PHP.

If you don't install MySQL, then PHP will use its own bundled SQL engine (SQLlite starting with PHP5). This is probably not what you want.

### MySQL 3.23 vs 4.0

This document covers the installation of MySQL 3.23. While listed as in "production" status, MySQL 4 is still relatively new to the playing field and there may be unknown issues when interacting with other software packages (as we're doing here).

### Verify PGP signature

After you have downloaded the MySQL source code, you should make sure it is intact and has not been tampered with. You can obtain a copy of MySQL AB's public PGP key and verify the package by running the following commands. Do not be alarmed about any messages about it not finding a "valid trust path"; this message is harmless.

    bash$ gpg --keyserver www.keyserver.net --recv-keys 0x5072E1F5
    bash$ gpg --verify mysql-[version].tar.gz.asc mysql-[version].tar.gz
    gpg: Signature made Sat May 17 10:25:18 2003 PDT using DSA key ID 5072E1F5
    gpg: Good signature from "MySQL Package signing key (www.mysql.com) <build@mysql.com>"

### Installation

Noteworthy configuration parameters include:

    CC=gcc				Use gcc as your c compiler

    CXX=gcc				Use gcc as your c++ compiler instead of g++.  This has the
    				advantage of not linking in libg++ and libstdc++, which
    				has been known to cause strange problems with MySQL.
    				Using libg++/libstdc++ also increases the size of the
    				mysqld binary without giving you any added benefits.

    --enable-local-infile		Enable the "LOAD DATA LOCAL" command in the MySQL server.
    				Note that using this has some security implications and it's
    				best to specify this from your mysql client at run time if you
    				need it.  See http://www.mysql.com/doc/en/LOAD_DATA_LOCAL.html
    				If in doubt, don't use this option.  Thanks to Michael Smith
    				for providing the info on this!

    --without-bench			Do not compile the set of benchmarking tests.  Unless you will
    				specifically be using these, using this option will speed up
    				your build time.

    --with-extra-charsets=none	This builds a faster MySQL server by only using the ISO-8859-1
    				(Latin1) characterset.  Unless you are using a language other
    				than English, this is a good option to select.

    --disable-shared		Do not build a shared libmysqlclient.so library.  If your
    				build fails and complains about not being able to create this
    				file, using this switch is a good work-around.

    --enable-thread-safe-client	Build the MySQL client binary with suport for threads.

Add a user and group for `mysqld` to run as:

    bash# groupadd mysql
    bash# useradd -g mysql mysql

To build using gcc-3.2 or later:

    bash$ tar xzf mysql-[version].tar.gz
    bash$ cd mysql-[version]
    bash$ CFLAGS="-O3 -march=pentium3" \
    	CC=gcc \
    	CXX=gcc \
    	CXXFLAGS="-O3 -march=pentium3 -felide-constructors -fno-exceptions -fno-rtti" \
    	./configure \
    	--prefix=/usr/local/mysql \
    	--with-client-ldflags=-all-static \
    	--with-extra-charsets=none \
    	--without-bench \
    	--without-debug \
    	--without-readline \
    	--with-mysqld-user=mysql \
    	--enable-assembler \
    	--enable-thread-safe-client \
    	--mandir=/usr/local/man \
    	--infodir=/usr/local/info
    bash$ make
    bash# make install
    bash# ln -s /usr/local/mysql/bin/* /usr/local/bin/

Using `ldconfig(8)`, update your shared libraries:

    bash# echo "/usr/local/mysql/lib/mysql" >> /etc/ld.so.conf
    bash# ldconfig -v

Change ownership of the MySQL binaries to root and ownership of the data directory to the user that you will run `mysqld` as:

    bash# chown -R root.mysql  /usr/local/mysql
    bash# chown -R mysql /usr/local/mysql/var

Create the MySQL the configuration files:

    bash# cp support-files/*.cnf /etc/
    bash# cp support-files/my-medium.cnf /etc/my.cnf

If this is your `first-time` installation of MySQL, you'll need to set up the initial mysql database, which contains all database privilege information, and establish a mysqld root password.

    bash# scripts/mysql_install_db
    bash# mysqladmin -u root -p password 'new-password'
    bash# mysqladmin -u root -h <hostname> -p password 'new-password'

To start MySQL by hand, run:

    bash# /usr/local/mysql/bin/safe_mysqld --user=mysql &

To have MySQL start automatically when your system boots, copy `support-files/mysql.server` to the location where your system has its startup files. You'll need to `chmod 755` it, of course. For example, under Slackware you could rename it to rc.mysql and add something like the following to your startup scripts:

    if [ -x /etc/rc.d/rc.mysql ]; then
            echo "Starting MySQL: /etc/rc.d/rc.mysql start"
            . /etc/rc.d/rc.mysql start
    fi

If you plan to run MySQL and Apache on the same server, you can disable the networking options in MySQL since you'll never need them (everything's on the same server). This is generally considered a good idea since it cuts down on security issues across the network, and is probably what you want. Edit the mysql.server startup script and make sure to add `--skip-networking` to the `safe_mysqld` command.

### Disaster Recovery

To backup your MySQL database, you should run something similar to this:

    bash# mysqldump --all-databases -p > all_databases.sql
    bash# chown root.root all_databases.sql
    bash# chmod 400 all_databases.sql

You can automate this to a small degree using an interactive backup script (below). Note that you should `never` place the MySQL root password in any configuration file (which is why it's interactive)!

    #!/bin/sh

    TODAY=`date +%Y-%m-%d`

    if [ ! -d /var/adm/mysql ]; then
            mkdir -p /var/adm/mysql
    fi

    chmod 700 /var/adm/mysql
    cd /var/adm/mysql
    /usr/local/mysql/bin/mysqldump --all-databases -p > all_databases-$TODAY.sql
    gzip -9 all_databases-$TODAY.sql
    chmod 400 all_databases*

    if [ -f /root/.mysql_history ]; then
            rm -f /root/.mysql_history
    fi

To restore your database from a backup file, you can use:

    bash# mysql < all_databases.sql

### Optimization

To analyze, check and optimize (respectively) your tables, you can run something like the following (below). Note that you should back up your database prior to running any type of maintenance!

    bash$ mysqlcheck --all-databases -avp
    bash$ mysqlcheck --all-databases -cvp
    bash$ mysqlcheck --all-databases -ovp

If you want to perform a more in-depth optimization of your tables http://www.mysql.com/doc/en/Maintenance_regimen.html you can try:

    bash# cd /usr/local/mysql/var
    bash# myisamchk --fast --silent */*.MYI
    bash# myisamchk -r --silent --sort-index -O sort_buffer_size=16M */*.MYI
    bash# myisamchk --fast --silent */*.MYI

You should check your tables at least once per week for any corruption. You can use something like the following in your system's crontab file:

    35 0 * * 0 /path/to/myisamchk --fast --silent /path/to/datadir/*/*.MYI

You can increase MySQL performance on Linux by running the following from your server startup scripts:

    if [ -f /proc/sys/fs/file-max ]; then
    	echo 65536 > /proc/sys/fs/file-max
    fi
    if [ -f /proc/sys/fs/dquot-max ]; then
    	echo 8192 > /proc/sys/fs/dquot-max
    fi
    if [ -f /proc/sys/fs/super-max ]; then
    	echo 1024 > /proc/sys/fs/super-max
    fi

You should also add the following to /etc/my.cnf:

    [safe_mysqld]
    	open-files-limit=8192

On a Linux 2.4 SMP machine, you can add the `renice -20 $$` command to `safe_mysqld` to increase performance.

To create an index on a column that you reference frequently using WHERE statements, you can use:

    mysql> ALTER TABLE mytable ADD INDEX (existing-column-name)

It's worth noting that, according to the INSTALL-SOURCE document in the MySQL source code, MySQL database files run particularly well on a ReiserFS-formatted Linux partition.

### Sample Database Creation & Permissions

Most books talk about creating a database, but touch very lightly on setting appropriate permissions for the table. First, create the database that you wish to use:

    mysqladmin create niftydatabase -u root -p

Now that you have the database, you need to set up some basic security permissions that govern how it can be accessed. Enter the mysql program and change to the mysql security database:

    bash$ mysql -u root -p
    mysql> USE mysql

Now, we can get down to business. If you want to grant more than just the ability to select items, make the changes in the 'db' database. Otherwise you'll be granting access to other databases that you don't want the account to have access to.

    mysql> INSERT INTO user (Host, User, Password) VALUES('localhost','niftyguest',password('niftypassword'));
    mysql> INSERT INTO host (Host,Db) VALUES('localhost','niftydatabase');
    mysql> INSERT INTO db (Host, Db, User, Select_priv) VALUES('localhost','niftydatabase','niftyguest','Y');
    mysql> exit

    bash$ mysqladmin reload -u root -p

## OpenSSL

OpenSSL is an Open Source toolkit that implements Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols along with a robust cryptography library. By itself, OpenSSL won't do much. Its chief benefit is to other applications which can take advantage of the functions it provides. Specifically, OpenSSL is a "toolkit" that includes:

- libssl.a -- The SSLv2, SSLv3, and TLSv1 raw code

- libcrypto.a -- General encryption routines for encryption ciphers (libdes, RC4/2, Blowfish, IDEA), Message Digests (MD5/2, SHA0/2, MDC2), Public Keys (RSA, DSA, Diffie-Hellman), X.509v3 certificates (encrypting/decrypting via private key), and various other encodings & routines.

- openssl -- A command line tool that can be used to access OpenSSL routines

Of interest, the no-threads argument prevents building in support for multi-threaded applications. In the case of Apache 1.3.x, this is actually quite handy as it does not use threads in the first place. This prevents the performance overhead (memory/time) relating to these locking mechanisms from happening and thus being wasted. The -fPIC option is not needed unless you are planning on using mod_ssl as a Dynamic Shared Object (DSO), which is not covered by this document.

Remember, run ./config and not ./configure! If the script failes to detect your operating system and compiler, you can instead run ./Configure and select the operating system and compiler that matches your setup. Next, running make will build the libraries libcrypto.a and libssl.a and the OpenSSL binary itself. This will take some time to complete. To run make test, you'll need to have `bc(1)`, the Unix calculator, installed; otherwise, skip that step.

    bash$ ./config no-threads shared
    bash$ make
    bash$ make test
    bash# make install
    bash# echo "/usr/local/ssl/lib" >> /etc/ld.so.conf
    bash# ldconfig -v

## mod_ssl

modssl allows you to add SSL support to Apache 1.3.x. Here, we will apply the mod_ssl source extension and source patches to the Apache source tree. The actual installation of mod_ssl is done when you install Apache. If you don't yet have your SSL certificate yet, you can skip the --with-crt and --with-key directives as we'll address this later.

    bash$ tar xzf mod_ssl-2.8.x-1.3.y.tar.gz
    bash$ cd mod_ssl-2.8.x-1.3.y
    bash$ ./configure \
    --with-apache=../apache-1.3.x \
    --with-crt=/etc/ssl/certs/server.crt \
    --with-key=/etc/ssl/certs/server.key

That's it for this portion. Once you have finished installing the rest of Apache from this document, your default httpd.conf file will have some SSL commands as well. At a minimum, you'll definately want to tell Apache where your Server Certificate and Server Key are located:

    SSLCertificateFile	/etc/ssl/certs/server.crt
    SSLCertificateKeyFile	/etc/ssl/certs/server.key

If you wanted to adjust some of the other parameters, a good first step would be to disable SSL version 2, which is not as secure as SSL version 3:

    SSLProtocol	all -SSLv2

To tell your server to only allow strong encryption connections, you could try:

    SSLCipherSuite	HIGH:MEDIUM

A step down from this, but still somewhat secure, would be to disable all Export Ciphers (EXP), Null (empty), and SSLv2 ciphers.

    SSLCipherSuite	ALL:!ADA:RC4+RSA:+HIGH:+MEDIUM:+LOW:!EXP:!NULL:!SSLv2

If you decide to build Apache with OSSM mm (shared memory) support, you'll be able to take advantage of RAM-based session caches instead of disk-based ones. You can make the modifications here:

    SSLMutex		sem
    SSLSessionCache		shm:/usr/local/apache/logs/ssl_gcache_data(512000)

If you have `/dev/urandom` support on your system, you can use this instead of `/dev/random` for seeding your Pseudo Random Number Generator (PRNG) of the SSL library. `/dev/random` is not always sufficient for generating enough entropy, which translates to very long connection times (to generate the entropy to establish the connection).

    SSLRandomSeed startup	file:/dev/urandom 1024
    SSLRandomSeed connect	file:/dev/urandom 1024

Regardless, when you establish your SSL directories, make sure to enable SSL!

    <Directory	"/usr/local/apache/ssldocs">
    SSLRequireSSL
    Options None
    Order allow, deny
    Allow from all
    </Directory>

## imapd

imapd is a POP3/IMAP server from the folks at the University of Washington. As an added bonus, it provides access via SSL if configured correctly for use with OpenSSL.

### Background

Setting up imapd can be really frustrating. Actually, I take that back; setting up secure imapd is the frustrating bit. This is mostly due to the apparent lack of any customer-service talent from the folks who produce imapd. Reading the documentation is akin to an experience with a lurking singular, sinister attitude of mind. For example:

> imapd/docs/SSLBUILD

> We can NOT provide you with support in building/installing OpenSSL, or in obtaining certificates. If you need help in doing this, try the contacts mentioned in the OpenSSL README.

Never mind the vagueness with &the contacts& or the lack of said references in the OpenSSL README. Most of what they implement, they implement well. But where it doesn't work on the client end, or where it relies on outside sources (i.e., the not us syndrome), they deflect, deny, and blame others for not following the "official IMAP standards". Given that type of tone, I have to agree with the following philosophy regarding such things:

> Alan Cox

> Take TCP for example. The TCP protocol is specified in a series of documents. If you make a formally correct implementation of the base TCP RFC you won't even make connections. Much of the flow control behaviour, the queueing and the detail is learned only by being directly part of the TCP implementing community. You can read all the scientific papers you like, it will not make you a good TCP implementor.

### Prerequisites

First, modify your mail spool permissions:

    bash# chmod 1777 /var/spool/mail

Update `/etc/services` with:

    imaps 993/tcp
    pop3s 995/tcp

Update `/etc/inetd.conf` with:

    pop3s stream tcp nowait.100 root /usr/sbin/ipop3d ipop3d
    imaps stream tcp nowait.100 root /usr/sbin/imapd imapd

### Installation

Current versions of imapd are now built with TLS/SSL encryption support by default. To build, you'll need to examine the file `Makefile` to find your system type. In this example, we'll build it for Linux (slx):

    bash# make slx
    bash# cp imapd/imapd /usr/sbin
    bash# cp ipopd/ipop3d /usr/sbin

To provide imap/pop3/nntp support for PHP, you'll need to copy the following file to your system:

    cp -fa c-client/c-client.a /usr/local/lib/libc-client.a
    cp -fa c-client/linkage.h c-client/os_slx.h /usr/local/include/
    ln -fs /usr/local/include/os_slx.h /usr/local/include/osdep.h
    cp src/osdep/unix/env_unix.h /usr/local/include/
    cd src/c-client
    cp -fa c-client.h imap4r1.h rfc822.h mail.h env.h fs.h \
    	ftl.h misc.h nl.h nntp.h smtp.h tcp.h \
    	/usr/local/include

Generate your `PEM Private Key`. Essentially, the same file as your RSA Private Key, but saved in a different format.

    bash# /usr/local/ssl/bin/openssl rsa -outform PEM -in example.com.key -out example.com.pem

Generate your `PEM Public Key`. For this to work, you need to have either purchased or signed your own CRT certificate.

    bash# cd /usr/local/ssl/certs
    bash# cat example.com.pem example.com.crt > imapd.pem
    bash# chmod 400 imapd.pem
    bash# ln -s imapd.pem ipop3d.pem

## OSSP mm

OSSP mm is a 2-layer abstraction library which simplifies the usage of shared memory between forked (and this way strongly related) processes under Unix platforms. On the first layer it hides all platform dependent implementation details (allocation and locking) when dealing with shared memory segments and on the second layer it provides a high-level malloc(3)-style API for a convenient and well known way to work with data structures inside those shared memory segments. Installing OSSP mm allows both mod_ssl and PHP to use a high-performance RAM-based session cache instead of a disk-based one.

    bash$ ./configure --prefix=/usr --enable-static --enable-shared
    bash$ make
    bash$ make test
    bash# make install

OSSP mm allows you to do really neat things with SSL-enabled Apache. For example, if you compile Apache+EAPI with shared memory (MM) support, you can edit your httpd.conf file and make the following changes:

    SSLSessionCache	shm:/usr/local/apache/logs/ssl_gcache_data(512000)
    SSLSessionCacheTimeout 300

This makes use of a high-performance hash table inside a shared memory segment in RAM. This cache is an optional facility which speeds up parallel request processing and helps to avoid unneccessary session handshakes. The path is just a hook to the shared memory (shm) along with the appoximate size in bytes listed in parenthesis. The timeout is listed in seconds and is the default setting if not specified.

## Crytographic/Hash Libraries

### Mhash

Mhash is a library which provides a uniform interface to a large number of hash algorithms. These algorithms can be used to compute checksums, message digests, and other signatures.

    bash$ ./configure
    bash$ make
    bash$ make check
    bash# make install

Next, make sure that `/usr/local/lib` exists in `/etc/ld.so.conf` and run:

    bash# ldconfig -v

### Libmcrypt

Libmcrypt is a library that provides uniform interface to access several encryption algorithms, and is used primarily by mcrypt (see below).

    bash$ ./configure
    bash$ make
    bash$ make check
    bash# make install

Next, make sure that `/usr/local/lib` exists in `/etc/ld.so.conf` and run:

    bash# ldconfig -v

### Mcrypt

#### Installation

Mcrypt enables add-on support of string encryption and decryption, which is much broader and more efficient than PHP's own cryptographic code. This is mainly used for saving sensitive data to disk that you need to decrypt at a later time. If you only want to encrypt (and not decrypt) strings, or aren't interested in the advanced cryptographic functions this module profides, skip this feature and use the built-in PHP function crypt() instead. Installation of mcrypt requires mhash and libmcrypt.

    bash$ ./configure
    bash$ make
    bash$ make check
    bash# make install

Next, make sure that `/usr/local/lib` exists in `/etc/ld.so.conf` and run:

    bash# ldconfig -v

#### Mcrypt Example

A good use of Mcrypt under PHP is to encrypt/decrypt passwords. Here's an example that should get you pointed in the right direction:

    <?php

    // Our ultra-secret key that no one knows
    $mcrypt_key     = "Is It Not Nifty?";

    function encrypt_me ($secret) {
    		global $mcrypt_key;

    		// Choose our encryption module and mode
    		$mcrypt_module = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "");

    		// Create our Initialization Vector (IV), a temporary, ultra-secret key as well
    		srand((float) microtime() * 1000000);
    		$mcrypt_iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($mcrypt_module), MCRYPT_RAND);

    		// Create the buffers that Mcrypt needs to do any encryption
    		mcrypt_generic_init($mcrypt_module, $mcrypt_key, $mcrypt_iv);

    		// Encrypt our data and make it portable
    		$encrypted_data = mcrypt_generic($mcrypt_module, $secret);
    		$encrypted_data = base64_encode ($encrypted_data);

    		// Make our IV portable as well (we'll need it to decrypt later)
    		$mcrypt_iv = base64_encode($mcrypt_iv);

    		// Close all buffers and modules
    		mcrypt_generic_end($mcrypt_module);

    		return($encrypted_data . "::Fluffy::" . $mcrypt_iv);
    }

    function decrypt_me ($secret) {
    		global $mcrypt_key;

    		// Extract our encrypted data
    		$encrypted_data = preg_replace("/::Fluffy::.*$/", "", $secret);
    		$encrypted_data = base64_decode($encrypted_data);

    		// Extract our IV
    		$mcrypt_iv = preg_replace("/^.*::Fluffy::/", "", $secret);
    		$mcrypt_iv = base64_decode($mcrypt_iv);

    		// Choose our encryption module and mode
    		$mcrypt_module = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "");

    		// Create the buffers that Mcrypt needs to do any encryption
    		mcrypt_generic_init($mcrypt_module, $mcrypt_key, $mcrypt_iv);

    		// Decrypt our data
    		$decrypted_data = mdecrypt_generic($mcrypt_module, $encrypted_data);

    		// Close all buffers and modules
    		mcrypt_generic_end($mcrypt_module);

    		return($decrypted_data);
    }

    ?>

## PHP

PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML.

This document covers the installation of PHP 4.x. While available in beta form, PHP 5 is still experimental and not recommended for production environments.

### PHP4 vs. PHP5

This document covers PHP version 4.x. PHP version 5 is currently in beta status and is not recommended for production environments.

### Installation

Note that some of these parameters require some common sense. For example, if you specify `--with-mysql=/usr/local/mysql`, then you'd better have already installed MySQL!

    --disable-debug			This is recommended by the PHP team

    --enable-inline-optimization	This is also recommended by the PHP team if you have "lots of memory"
    				and are using gcc as your compiler.

    --enable-memory-limit		Allows you to control the maximum amount of memory that a script can
    				use.  This helps to prevent buffer overflows (security)

    --enable-force-cgi-redirect	Prevent direct access to the PHP CGI binary

    --with-mysql=[PATH]		This will use the bundled MySQL library unless you specify the path to
    				your MySQL base installation.

    --with-imap			Allows PHP to use the UW-IMAP c-client library to provide IMAP/POP3
    				support.

    --with-imap-ssl			Allows for secure IMAP/POP3 SSL support. Since password are sent
    				unencrypted without this, it's a good idea.

    --with-mhash			Enables the interface hooks to the Mhash encryption functions.

    --with-mcrypt			Enables add-on support of string encryption and decryption, which is
    				much broader and more efficient than PHP's own cryptographic code.
    				This is mainly used for saving sensitive data to disk that you need to
    				decrypt at a later time. If you only want to encrypt (and not decrypt)
    				strings, or aren't interested in the advanced cryptographic functions
    				this module profides, skip this feature and use the built-in PHP
    				function crypt() instead.

    --with-mm			Allows you to use OSSP mm for shared-memory session storage via RAM
    				instead of your hard drive. If you've got extra RAM to spare and
    				intend to use sessions, say yes.

    --with-sysvsem			Enable System V sempahore (specialized file locking to prevent
    				simultaneous access) support.

    --with-apache			This builds PHP to work with Apache. Just say yes.

    --with-xml			Include XML parsing ability.

To begin, issue the following commands:

    bash$ CFLAGS="-O2 -march=i686" \
    	./configure \
    	--with-config-file-path=/etc \
    	--with-mysql=/usr/local/mysql \
    	--with-apache=../apache_1.3.x \
    	--with-openssl \
    	--with-imap \
    	--with-imap-ssl \
    	--with-mm \
    	--with-mcrypt \
    	--with-mhash \
    	--enable-memory-limit \
    	--enable-inline-optimization \
    	--enable-sysvsem \
    	--disable-debug \
    	--enable-force-cgi-redirect \
    	--with-xml
    bash$ make
    bash# make install

It's necessary to create the php.ini configuration file next:

    bash# cp php.ini-recommended php.ini-dist /etc
    bash# cp php.ini-recommended /etc/php.ini

### Security & PHP Superglobals

"Avoid strange women and temporary variables." -- Anonymous

A few years ago, my wife and I decided to go on a skiing trip up north. To reserve skiing equipment, you had to give 24 hours advance notice using the ski lodge's on-line website. The catch was that my wife had asked me to make the reservations 23 hours before the deadline.

So I got to thinking, and examined the online website, which would not let you make any reservations within the 24 hour timeframe. However, once you selected an appropriate date, I noticed that the URL was:

    https://www.somewhere.com/reservations.php?date=01-23-01

It occurred to me that, while they had locked down security on what dates I could choose from, the final value was placed into a `GET` statement at the end of the web address. I modified the web address to use "date=01-22-01" and indeed, our skies were waiting for us first thing the next morning (we paid for them, of course).

This innocent yet practical example is just one of the dangers we have to be aware of when using any programming language that can be used in ways that we did not intend, which leads us into our discussion on PHP Superglobals.

Forms

To understand Superglobals, it is critical that you understand how data is passed from one web page to another (e.g., forms). Specifically, you must be aware of two methods known as GET and POST. You should also probably be familiar with the HTML <FORM> statement (a good reference is http://www.w3.org/TR/html401/interact/forms.html).

You've probably seen something like this before:

    <form name="form1" method="post" action="process.php">
        <p>Please enter your name:</p>
        <p><input type="text" name="yourname" /></p>
       <p><input type="button" name="Submit" value="Submit" /></p>
     </form>

This is standard, nothing-fancy HTML form code that asks for some information and then submits the data to the file "process.php" . The critical bit here is the `method` declaration, which tells the form `how` to submit the data, for which we need to digress for a moment or two (hold your breath):

For those that recall the early days of HTML, forms were provided by means of the <ISINDEX> HTML tag. By inserting this tag into the HEAD of your HTML documents, a text field appeaed where you could fill out input. As the new HTML+ standard evolved, a <FORM> tag was designed and could be used with a METHOD attribute of GET, POST, or PUT. So, this leaves us with a few different ways to send our data.

GET

With GET, variables and their values are sent in the header of the URL request appended as part of the URL itself. The limitation is that web addresses (URLs) are limited to 8,192 characters; if the amount of data is too long, it will be truncated. Also, even with an SSL connection, the data is not encrypted since it is part of the web address.

For example, a web page might have a form statement like this:

    <form name="form1" method="get" action="process.php">
        <p>Please enter your name, e-mail address, and a comment:</p>
        <p><input type="text" name="yourname" /></p>
        <p><input type="text" name="email" /></p>
        <p><input type="text" name="comment" /></p>
        <p><input type="button" name="Submit" value="Submit" /></p>
     </form>

When you clicked Submit, your web browser would take the values you filled out in the form and redirect you to this web address:

    http://www.fluffygerbil.com/process.php?yourname=fred+smith&email=fred@nowhere.com&comment=I+have+no+comment

Notice how the values of the form are part of the web address itself? That's the essence of GET.

For the curious, what is actually sent in the raw HTTP transmission to accomplish this transaction is:

    GET /process.php?yourname=fred+smith&email=fred@nowhere.com&comment=I+have+no+comment HTTP/1.0
    Accept: image/gif, image/x-xbitmap, image/jpeg, */*
    Accept-Language: en-us
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)
    Host: www.fluffygerbils.com
    Connection: keep-alive

POST

With `POST,` the variables and their values are sent in the body of the URL request, not the header. The advantages of this type of data transmission is that there is no limit to the size of the data being sent since it is contained in the body of the HTTP request, not the header. Also, if you're using an SSL connection, the data will be encrypted too, what a deal. ;-) For example, a web page that has a form statement like:

    <form name="form1" method="post" action="process.php">
        <p>Please enter your name, e-mail address, and a comment:</p>
        <p><input type="text" name="yourname" /></p>
        <p><input type="text" name="email" /></p>
        <p><input type="text" name="comment" /></p>
        <p><input type="button" name="Submit" value="Submit" /></p>
     </form>

When you clicked Submit, your web browser would take the values you filled out in the form and redirect you to this web address:

    http://www.fluffygerbil.com/process.php

Notice how the values of the form are not part of the web address itself? That's the essence of PUT.

For the curious, what is actually sent in the raw HTTP transmission to accomplish this transaction is:

    POST /process.php HTTP/1.0
    Accept: image/gif, image/x-xbitmap, image/jpeg, */*
    Accept-Language: en-us
    Content-Type: application/x-www-form-urlencoded
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Q312461)
    Host: www.fluffygerbils.com
    Content-Length: 94
    Pragma: no-cache
    Connection: keep-alive

    yourname=fred+smith
    email=fred@nowhere.com
    comment=I+have+no+comment

So, why is all this background information useful? When you install PHP 4.2.2 or later, you might happen to notice that when compiling PHP, it states:

    +--------------------------------------------------------------------+
    |                          *** NOTE ***                              |
    |            The default for register_globals is now OFF!            |
    |                                                                    |
    | If your application relies on register_globals being ON, you       |
    | should explicitly set it to on in your php.ini file.               |
    | Note that you are strongly encouraged to read                      |
    | http://www.php.net/manual/en/security.registerglobals.php          |
    | about the implications of having register_globals set to on, and   |
    | avoid using it if possible.                                        |
    +--------------------------------------------------------------------+

This means that PHP will be ultra-paranoid about the data that is passed to it, and will require that you state which method the data should be coming from.

Which brings us to `Superglobals`, a relatively new concept to PHP. For example, the above diagram presents a slight problem: If you're working with the variable $yourname, how do you know that during your script it hasn't been redefined by one of these six other methods of variable assignment by someone attempting to hack into your script? For example, imagine having someone who has managed to upload a PHP script to your webserver that performs the following (php exploit by Daniel Phoenix):

    <?php
    	setcookie("test","../../../../../../etc/passwd");
    	echo "cookie inserted";
    ?>

Wouldn't it be great to have a way to isolate variables based on how the data gets assigned to it in the first place? Superglobals allow you to specify which variables `received by a specific method` should be used.

Superglobals are PHP's attempt at helping you determine where a particular value comes from. If you haven't heard of this new feature as of PHP 4.1.0, you'll want to start adapting to it. Most PHP training books don't touch this subject, so you will need to be aware of how to transition to this new input method. Ultimately, you should re-visit your /usr/local/lib/php.ini file and make the following change:

    register_globals = Off

This will prevent the ability for any user-submitted variable to be injected into your PHP code and can reduce the amount of variable poisoning a potential attacker may inflict. They will have to take the additional time to forge submissions, and your internal variables are effectively isolated from user submitted data. If a user then tried to fill out a form, the server wouldn't assign any data to the global variables `$name`, `$email`, or `$comment`. Instead, it would divide up the data into the following hashed arrays:

    $_POST['name']
    $_POST['email']
    $_POST['comment']

The main Superglobal arrays are:

- $_GET['variable'] - Variables provided to the script via HTTP GET. Analogous to the deprecated HTTP_GET_VARS array

- $_POST['variable'] - Variables provided to the script via HTTP POST. Analogous to the deprecated $HTTP_POST_VARS array

The other, less-common Superglobal arrays are:

- $_COOKIE['variable'] - Variables provided to the script via HTTP cookies. Analogous to the deprecated $HTTP_COOKIE_VARS array

- $_REQUEST['variable'] - Variables provided to the script via any user input mechanism (GET, POST, COOKIE) and which therefore cannot be trusted.

- $_GLOBALS['variable'] - Contains a reference to every variable which is currently available within the global scope of the script. The keys of this array are the names of the global variables.

- $_SERVER['variable'] - Variables set by the web server or otherwise directly related to the execution environment of the current script. Analogous to the deprecated $HTTP_SERVER_VARS array

- $_FILES['variable'] - Variables provided to the script via HTTP post file uploads. Analogous to the deprecated $HTTP_POST_FILES array

- $_ENV['variable'] - Variables provided to the script via the environment. Analogous to the deprecated $HTTP_ENV_VARS array

- $_SESSION['variable'] - Variables which are currently registered to a script's session. Analogous to the deprecated $HTTP_SESSION_VARS array

For more details, see http://www.php.net/manual/en/reserved.variables.php.

So instead of `$name` being set to "John", you would either have `$_GET['name'] = "John"` or possibly `$_POST['name'] = "John"` depending on how the form data was submitted. The advantage is that you will know:

- $name can never be faked; if your script sets its value, that's the value!

- The $_GET and $_POST arrays help you to determine if the user appended the data as part of the URL or as part of the request body; therefore you don't have to worry about having a form accepting POST data and having the values change by someone sending a hacked URL with GET data appended to the URL. This will make sense shortly, so hang on...

- These 'superglobals' allow you to 'compartmentalize' not only your variable's values, but how the values were provided to the server in the first place. Someone attempting to hack into your server will have a very difficult time bypassing this.

Additional Resources

- On the Security of PHP, by Jordan Dimov

- A Study In Scarlet: Exploiting Common Vulnerabilities in PHP Applications, by Shaun Clowes

## Bringing it all Together

This is where we tie all our various and assorted parts into the final product. You can use static or dynamic (DSO) modules; here we're focused on static modules. The philosophy is simple: Only compile in what you need, and nothing more! Below are a few specifics worth mentioning. For in-depth information, review Apache's src/Configuration.tmpl file.

    --enable-module=speling		This module attempts to correct misspellings of URLs that
    				users might have entered, by ignoring capitalisation and
    				by allowing up to one misspelling

    --disable-module=include	Prevents the ability to parse Server Side Includes (SSI)

    --disable-module=imap		This has nothing to do with e-mail. This refers to imagemap
    				files. If you don't know what this is, you don't need it.

    --disable-module=negotiation	Prevents dynamic document presentation based on client-supplied
    				preferences. For example, multiple pages of the same page in
    				different languages

    --disable-module=status		Prevents server activity and performance from being published
    				on a HTML page

    --disable-module=userdir	Prevents user-specific home directories, such as
    				http://www.myserver.com/~johndoe

    --disable-rule=SSL_COMPAT	Build the final code of mod_ssl without backward compatibility
    				code for Apache-SSL 1.x, mod_ssl 2.0.x, Sioux 1.x and
    				Stronghold 2.x

    --enable-rule=SSL_SDBM		The SSL_SDBM rule controls whether the built-in SDBM library
    				should be used instead of a custom defined or vendor supplied
    				DBM library. Enable this to force the use of SDBM in case the
    				vendor DBM library is buggy or restricts the data sizes too
    				dramatically.

    SSL_BASE=SYSTEM			Tells Apache to use the system-installed OpenSSL libraries.

    EAPI_MM=SYSTEM			This should be added after the SSL_BASE declaration when
    				configuring Apache in the final step if you want to support
    				shared memory RAM-based session support for SSL sessions
    				(instead of hard drive based).

Then the actual configuration of the source tree:

    bash$ EAPI_MM=SYSTEM SSL_BASE=SYSTEM \
    	CFLAGS="-O2 -march=i686" \
    	./configure \
    	--prefix=/usr/local/apache \
    	--enable-module=ssl \
    	--enable-module=speling \
    	--disable-module=include \
    	--disable-module=imap \
    	--disable-module=negotiation \
    	--disable-module=status \
    	--disable-module=userdir \
    	--disable-rule=SSL_COMPAT \
    	--enable-rule=SSL_SDBM \
    	--activate-module=src/modules/php4/libphp4.a \
    	--server-uid=apache \
    	--server-gid=apache

Review the output to make sure all went as planned. Next, to build Apache:

    bash$ make

If this is a new installation of Apache, run:

    bash# make install
    bash# chmod 0511 /usr/local/apache/bin/httpd

Otherwise, if you are upgrading an existing installation of Apache, run the following command and then see http://httpd.apache.org/docs/misc/security_tips.html if you've previously modified any of the default permissions on your apache files:

    bash# cp src/httpd /usr/local/apache/bin/
    bash# chmod 0511 /usr/local/apache/bin/httpd

Edit /etc/apache/httpd.conf and add (if it's not already there):

    AddType application/x-httpd-php .php .php3
    AddType application/x-httpd-php-source .phps

It would probably be a good idea to review the SSL configuration options available in your shiny new httpd.conf file, which are discussed at http://www.modssl.org/docs/2.8/ssl_reference.html

## Running Your Shiny New Apache

Start normal Apache only, without SSL:

    /usr/local/apache/bin/apachectl start

Start SSL-Aware Apache:

    /usr/local/apache/bin/apachectl startssl

Abruptly stop Apache, which will affect anyone currently connected to your web page:

    /usr/local/apache/bin/apachectl stop

Gracefully restart (and as a result, reread your apache config file) Apache, which will invisibly (to anyone viewing your web page) restart your server. When making configuration changes, this is probably the option you want:

    /usr/local/apache/bin/apachectl graceful

## SSL Certificates & Keys

In a nutshell, all the ambiguity of SSL keys can be boiled down to one golden rule: the goal of all key generation for running a SSL server is to create the two following files:

    /usr/local/ssl/certs/server.key
    /usr/local/ssl/certs/server.crt

The first file we can create ourselves; the second file we can either purchase (which will be recognized by most browsers) or create for free (which won't be recognized by most browsers and requires the user to not be thrown by the error message). Most of this section describes how to generate the first file.

### Overview

First, "Certificate Authority" requires a bit of a definition for the uninitiated. A Certificate Authority (CA) is either an official organization, like VeriSign (http://digitalid.verisign.com/server/apacheNotice.htm) and Thawte Consulting at http://www.thawte.com, or a fake organization that you create yourself. Yes, it seems silly to create a request to submit to yourself to be approved by yourself to send back a certificate to yourself. But, it has to be done unless you submit to a real organization. What's the difference? Web browsers and e-mail clients typically already know about official certificate authorities, whereas they don't know who you are. It's part security-related, part money-making. Take your pick. If you are primarily supporting non-tech-savvy folks, pay the money and go for the official certificate authority bit and save yourself having to answer lots of questions and touch each computer to install your own certificate authority file. There really is no difference in the security provided.

- example.key - Your RSA Private Key. Should be 1024 bits in length. No one should have access to this file other than yourself. RSA is a public-key encryption technology developed by RSA Data Security, Inc. The acronym stands for Rivest, Shamir, and Adelman, the inventors of the technique. The RSA algorithm is based on the fact that there is no efficient way to factor very large numbers. Deducing an RSA key, therefore, requires an extraordinary amount of computer processing power and time. You will never want to delete this key. chmod 0400.

- example.pem - Your PEM (Privacy Enhanced Mail) Private Key. This is a copy of your RSA Private Key, but saved in a different format. You don't have to keep this key after you've set things up, and is only required if you want SSL-imapd/ipop3d. chmod 0400.

- example.com.csr - Your CSR (Certificate Signing Request) file. This is what you need to submit to request your official SSL certificate. Once you have your official SSL certificate, you can archive this file. You don't have to keep this key after you've set things up. chmod 0400.

- example.crt - Your CRT (CeRTificate) Public Key. This is what you get from a Certificate Authority - your "SSL Certificate" - without this, you can't do web SSL. You will never want to delete this key. chmod 0400

- imapd.pem - Your PEM (Privacy Enhanced Mail) Public Key. This is actually just a copy of your PEM Private Key and your CRT Public Key concatenated into a single file. Without this, you can't do SSL e-mail. You will never want to delete this key. chmod 0400

- ipop3d.pem - This should be a soft link to imapd.pem, or a copy of the same file. You will never want to delete this key.

### Using a Valid Certificate Authority

This is done in two steps: Generating your server request, and sending it to a valid certificate authority. (Hint: It is not unlike the relationship between server and client). For any time you are prompted for `Common Name`, it is critical that you specify your web server address minus the http:// bit (e.g., www.fluffygerbils.com).

First we create our own `private server key`:

    bash# cd /usr/local/ssl/certs
    bash# openssl genrsa -des3 -out server.key 1024
    bash# chmod 400 server.key

Then we create our own server `certificate request (CSR)`:

    bash# cd /usr/local/ssl/certs
    bash# openssl req -new -key server.key -out server.csr

Finally, you need to submit your server.crt request to a valid certificate authority to be signed.

### Creating your own Certificates

This is done in two steps: Pretending to be a `Certificate Authority`, and then issuing yourself a server certificate signed by your own Certificate Authority. (Hint: It is not unlike the relationship between server and client). For any time you are prompted for `Common Name`, it is critical that you specify your web server address minus the http:// bit (e.g., www.fluffygerbils.com).

First, to pretending to be a Certificate Authority (CA), we need to do this:

    bash# cd /usr/local/ssl/certs
    bash# openssl genrsa -des3 -out ca.key 1024
    bash# chmod 400 ca.key
    bash# openssl req -new -x509 -days 365 -key ca.key -out ca.crt
    bash# mkdir ca.db.certs
    bash# echo '01' > ca.db.serial
    bash# cp /dev/null ca.db.index

And create the following file in `/usr/local/ssl/certs/ca.config`:

    [ ca ]
    default_ca              = CA_own
    [ CA_own ]
    dir                     = .
    certs                   = $dir
    new_certs_dir           = $dir/ca.db.certs
    database                = $dir/ca.db.index
    serial                  = $dir/ca.db.serial
    RANDFILE                = $dir/ca.db.rand
    certificate             = $dir/ca.crt
    private_key             = $dir/ca.key
    default_days            = 365
    default_crl_days        = 30
    default_md              = md5
    preserve                = no
    policy                  = policy_anything
    [ policy_anything ]
    countryName             = optional
    stateOrProvinceName     = optional
    localityName            = optional
    organizationName        = optional
    organizationalUnitName  = optional
    commonName              = supplied
    emailAddress            = optional

Next, we create our own `private server key`:

    bash# cd /usr/local/ssl/certs
    bash# openssl genrsa -des3 -out server.key 1024
    bash# chmod 400 server.key

Then we create our own server `certificate request (CSR)`:

    bash# cd /usr/local/ssl/certs
    bash# openssl req -new -key server.key -out server.csr

Finally, we sign our own request:

    bash# cd /usr/local/ssl/certs
    bash# openssl ca -config ca.config -out server.crt -infiles server.csr
    bash# openssl verify -CAfile ca.crt server.crt

## Additional Resources

- Analog - The most popular, or at least realistic, web logfile analyzer in the world.

- Apache Toolbox

- Apache Week

- IMAP Toolkit Frequently Asked Questions

- mod-ssl User Manual

## About this Document

Copyright (c) 2003 David Lechnyr. Redistribution and use, with or without modification, are permitted provided that the copyright notice, this list of conditions and the following disclaimer be included.

THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This document was lovingly handcrafted on a Dell Latitude C400 laptop running Slackware Linux 9.0, in case anyone asks.