Install PHP

From UNPM.org Wiki
Jump to: navigation, search

PHP is a common scripting language and a staple in modern web servers. Nearly all popular web applications are written in PHP.

This article will install PHP onto an Ubuntu 18.04 server that has been configured per the previous articles in this series.

Most of the article will require root privileges.

username@servername:~$ sudo -i

Install and configure PHP

To get the latest version of PHP, install the following PPA and run an update:

root@servername:~# add-apt-repository ppa:ondrej/php && aptitude update && aptitude upgrade

Note that this PPA will be updated much more frequently than the official repositories are updated. Each time the PHP packages are updated by the owner of the PPA, the update will want to replace several of the PHP files modified below, but will ask what to do before completing the update. The best way to handle this is to look at the difference between the installed files and the new files. If the changes the update makes are only to replace the user modified settings and change settings that are commented out (annotated with ;), then it may be easier to keep the installed version instead of allowing the update to replace them and then making the changes again.

Install the packages for PHP and memcached. Memcached is used to make PHP sites load faster while consuming less resources. This set of PHP packages provides for a baseline install of PHP. Some software packages may require additional PHP packages.

root@servername:~# aptitude install libpcre3-dev make memcached php-apcu php-apcu-bc php-memcache php7.2-fpm php7.2-bz2 php7.2-curl php7.2-dev php7.2-gd php7.2-mbstring php7.2-xml php7.2-zip

Package dependencies will force php7.1-cli and associated packages to be installed. This will make 7.1 the default for command line. To change this to 7.2, run the following command:

root@servername:~# update-alternatives --set php /usr/bin/php7.2

Configure PHP

apc.ini

Increase the amount of RAM PHP can use by editing apcu.ini. Depending on available system RAM, this number can be higher or lower, but 128 MB is generally a good place to start.

root@servername:~# nano /etc/php/7.2/mods-available/apcu.ini

Add:

apc.shm_size = 128M

www.conf

Prevent 504 gateway timeouts

root@servername:~# nano /etc/php/7.2/fpm/pool.d/www.conf

Change:

request_terminate_timeout = 300

php.ini

root@servername:~# nano /etc/php/7.2/fpm/php.ini

Change:

post_max_size = 20M
cgi.fix_pathinfo = 0
max_execution_time = 300
upload_max_filesize = 20M
session.save_handler = memcache
session.save_path = unix:/tmp/memcached.sock

While editing the file, it isn't a bad idea to turn off expose_php, which reports the version of PHP being used, however, this is not perfect protection from revealing such information, but easy enough to do.

The post_max_size setting in /etc/php5/fpm/php.ini limits the maximum size POST (total data being uploaded - can be multiple files in one upload) through a PHP application. The default setting is 8 MB. This is different from the upload_max_filesize setting which determines the largest size for each file, which has a default setting of 2 MB. Many applications will have their own settings for maximum upload size, but administrators should keep in mind that both nginx and PHP have configurable limits. Some applications require larger sizes to run properly, particularly true for phpMyAdmin when uploading and installing databases. For this reason this guide recommends higher limits than default.

Changing cgi.fix_pathinfo = 0 forces the PHP interpreter to only process the literal file path. This is an important security issue because otherwise an uploaded file that contains malicious code may be run.[1]

The session.save_handler and session.save_path settings can be configured to allow memcached to manage sessions. This means that user sessions will be served from RAM instead of the hard drive. In the event of a reboot of memcached, all session data will be lost. However, using memcached will increase the performance of the server.

Configure memcached

Some changes must be made to the memcached configuration file.

root@servername:~# nano /etc/memcached.conf

Change:

# -p 11211
# -l 127.0.0.1

At the bottom, add:

-s /tmp/memcached.sock
-a 666

Configure systemd to permit creating /tmp/memcached.sock:

root@servername:~# mkdir -p /etc/systemd/system/memcached.service.d
root@servername:~# echo "[Service]" > /etc/systemd/system/memcached.service.d/override.conf
root@servername:~# echo "PrivateTmp=false" >> /etc/systemd/system/memcached.service.d/override.conf
root@servername:~# systemctl daemon-reload
root@servername:~# systemctl restart memcached

Configure nginx

Create a new file in the nginx /conf.d/ directory for PHP.

root@servername:~# nano /etc/nginx/conf.d/php-sock.conf

Add to the file:

upstream php7.2-fpm-sock {
   server unix:/run/php/php7.2-fpm.sock;
}

Adding some new settings to the fastcgi_params can prevent problems that occur with some web applications:

root@servername:~# nano /etc/nginx/fastcgi_params

Add to the bottom of the file:

fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;

Now nginx will need to know to look for PHP index files.

root@servername:~# nano /etc/nginx/nginx.conf

Change:

index index.html index.htm index.php;

Telling nginx to pass PHP files

For PHP to work, nginx must be configured to pass a given file to the PHP interpreter. This presents a security risk in the event that a PHP package has a security exploit or a server is incorrectly configured. Additionally, if a .php file is not configured to be passed to the PHP interpreter, then nginx will treat this file as any other and serve it as a download. Some of these files can be used to determine server configurations as well as find information such as database passwords, administrator passwords and other bits of information that a hacker may use to break into a server.

There are commonly three ways that are used to pass .php files to the PHP interpreter:

Least secure:
server {
    ...
    location ~ \.php$ {
        <PHP configuration>
    }
}

This is most insecure because it passes all .php files in the site's directory to the PHP interpreter unless a location is specifically denied. If a hacker is able to write or rewrite any file in the site's directory, then the hacker can navigate to www.example.com/hackedfile.php and run the malicious code.

Improved, but still vulnerable:
server {
    ...
    location ~ /subdirectory/.*\.php$ {
        <PHP configuration>
    }
}


This configuration is often provided with some specific location blocks with deny all and default_type text/plain statements. Although more secure than allowing all .php files in the site's root directory be passed to the PHP interpreter, it still leaves the vulnerability of any .php file altered or added to the /subdirectory/ as being a vector for attack. It is also impossible to use this method when installing a PHP package as a site's home page, since the /subdirectory/ is in fact the site's root directory.

Very secure:
server {
    ...
    location = /directoryname/file.php$ {
        <PHP configuration>;
    }

    location /directoryname/ {
        location ~ \.php$ { deny all; }
    }
}

Or:

server {
    ...
    location /directoryname/ {
        location ~ (filename1|filename2|filename3).*\.php$ {
            <PHP configuration>;
        }
        location ~ \.php$ { deny all; }
    }
}

These configurations pass only specific files to the PHP interpreter, thus a hacker will have to change those specific files to pass malicious code.

The first example uses the location = directive, which tells nginx to stop all searching and run this location, which is faster. However, as written, this configuration will only work with PHP packages using MVC framework.

The second example requires identifying every single .php file a package requires a user to have publicly accessible. This can be difficult as plugins and upgrades to the package may remove the requirement for some files while adding others, requiring testing of every single link and function every time there is an upgrade to the package. If a package is behaving unexpectedly, it may be because of the PHP configuration. Checking /var/www/example.com/logs/error.log for errors will reveal such causes.

Both examples have a deny all statement to block access to all other .php locations. Since nginx processes directives in the order stated, this should generally be the last directive for locations that contain PHP packages.

Sites-available configuration

It is good practice to use a configuration file to manage PHP. When security holes or configuration issues are discovered, they can be easily fixed for all passing of PHP on the server by editing only one or two files, plus the sites-available files will be smaller and easier to manage by cleaning up the file and reducing the opportunity for syntax errors.

Create the global-configs files

There will be two files, one for use in HTTP server blocks and the other for use in HTTPS server blocks.

root@servername:~# nano /etc/nginx/global-configs/php.conf

Add:

try_files $uri =404;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_pass php7.2-fpm-sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;

And create the HTTPS file:

root@servername:~# nano /etc/nginx/global-configs/php_https.conf

Add:

try_files $uri =404;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_pass php7.2-fpm-sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_param HTTPS on;
Configure PHP test & info location
root@servername:~# nano /etc/nginx/sites-available/example.com

There is a standard notation used that makes human reading of configuration files a little easier. In the examples below, this format is used.

In the HTTPS server block:

    location ~ phpinfo.php {
        include global-configs/php_https.conf;
    }

For the actual phpinfo.php entries, it may be more convenient to enter everything on one line, this way the entry can be easily commented or uncommented for various testing purposes.

In the HTTPS server block, add:

    location ~ phpinfo.php { include /etc/nginx/global-configs/php_https.conf; }

Reboot the server.

root@servername:~# reboot

Verify installation

PHP can display its current settings by creating a PHP file and running it in a browser.

username@servername:~$ touch /var/www/example.com/public/phpinfo.php
username@servername:~$ chmod 664 /var/www/example.com/public/phpinfo.php
username@servername:~$ sudo chown www-data /var/www/example.com/public/phpinfo.php
username@servername:~$ nano /var/www/example.com/public/phpinfo.php

Add the following line:

<?php phpinfo(); ?>

Navigate to http://www.example.com/phpinfo.php and https://www.example.com/phpinfo.php, which should bring up the long list of PHP settings. Testing both is necessary to verify everything is working properly.

It is good practice to comment out the phpinfo.php location blocks in the sites-available file when they are not needed.

A quick way to view the PHP configuration without passing it through nginx is to run it directly through command line, though this will not reveal if there are any issues with nginx and PHP:

username@servername:~$ php /var/www/example.com/public/phpinfo.php > phpinfo.txt
username@servername:~$ nano phpinfo.txt

Supporting PHP mail() function

Many popular PHP packages make use of the PHP mail() function to send emails for various purposes. For this function to work properly, an MTA must be installed in the server. A simple workaround for those not planning to install a mail server is to configure an SMTP mail server, such as Gmail, to manage the mail. (However, note that Google does not like Gmail to be used for transactional mail and will likely disable the account for 24 hour periods upon detection.) Most of the popular PHP packages will either have this option built-in to the package or plugins will be available. When using an SMTP mail server, be sure to note the maximum message limits. These services will also commonly have bandwidth limitations, but so long as the messages do not include images it is highly unlikely that bandwidth limits will be reached before hitting the message limits.

For more on mail servers, there is currently a series of incomplete sandboxed articles on mail servers.

Next step

And now, for the final step in completing the UNPM Server: Install MariaDB.

References

External links

PHP: Hypertext Preprocessor

Ondřej Surý maintains several PPAs through Launchpad

Sury.org wiki on GitHub

Bolting on PHP with PHP-FPM | Ars Technica