Zeta Components - high quality PHP components

eZ Components - Authentication

Introduction

The purpose of this document is to provide common methods of securing online applications, and links to sources of information. The list of methods presented here is not exhaustive, but can be used as a checklist for ensuring protection against common attacks.

Securing databases

Store encrypted passwords

Don't store the passwords in plain text because anybody will be able to read them if they gain access to the database.

Use the PHP functions md5 () or sha1 () to encrypt the passwords and then store them. When retrieving a password for the authentication, use the same function and compare the encrypted versions of the password.

To prevent cases where two users happen to choose the same password, a salt can be added to the password. The salt can be the first few characters from the username, and it should be added in front of the password before calling the encrypting function.

Although MySQL has it's own password() function, it is recommended to use PHP's md5() or sha1(), because with password() identical passwords will be stored as identical strings, and the passwords might be sent unencrypted between the web server and the MySQL database (depending on configuration).

Securing the session

For more information about the techniques discusses in this section, consult the articles Session fixation and Session poisoning.

Store SID in cookies

Settings in php.ini affects how the session identifier (SID) is handled by the application - in a cookie or in the GET/POST request. Storing the session identifier in a cookie is preferred (although not entirely hack-proof).

The php.ini settings are:

; Whether to use cookies. session.use_cookies = 1 ; This option enables administrators to make their users invulnerable to ; attacks which involve passing session ids in URLs; defaults to 0. session.use_only_cookies = 1 ; Don't append the SID to URLs session.use_trans_id = 0

Additionally, make sure PHP is not compiled with the option --enable-trans-id, as this will rewrite URLs to include the SID.

This requires informing users to use a browser that supports and accepts cookies.

Secure the SID generation

By modifying these settings in the php.ini file, the SID generation will depend on the first bytes of a file instead of the current date and time (default):

; What file to use for SID generation session.entropy_file = <path to a file> ; How many characters to use from the entropy file session.entropy_length = 10

For entropy file, the Unix /dev/urandom can be used, which generates a pseudo-random number.

Regenerate the SID on every request

Done by the Authentication component.

Accept only server generated SID

Done by the Authentication component.

Time-out old SIDs

Done by the Authentication component.

Move the session directory

It is possible that attackers might get access to the /tmp directory where sessions are stored by default. Use the following php.ini setting to specify another directory for this:

; where to store the sessions session.save_path = <directory>

The specified directory must be writable by the user under which PHP is running.

Clean-up the session directory

The session garbage collector is run when a new session starts. It's behaviour is controlled by the php.ini options (the values specified are the default):

; after how many seconds the data is seen as garbage and cleaned-up out session.gc_maxlifetime = 1440 ; with what probability the garbage collector is run on every session ; initialization. Use together with gc_divisor session.gc_probability = 1 ; value to divide the gc_probability to obtain the garbage collector ; probability to run. ; Probability = gc_probability/gc_divisor (default: 1/100 = 1%) session.gc_divisor = 100

Change these options to values more appropiate for your needs. Note that running the session garbage collector more often can have negative side-effects on the application's performance.

Accept only HTTP cookies

A setting in php.ini can make cookings accessible only by HTTP and prevent the cookies from being read by scripting languages:

session.cookie_httponly = on

Default is off. It is available only from PHP 5.2.0 and not all browsers support this (most notably Firefox). Can prevent identity theft through XSS (Cross-site scripting ) attacks.

Destroy the session at logout

When user logs out, the session must be destroyed to prevent reusing of the same SID by the attacker.

Destroy the session if the referrer is suspicious

If the referrer is not the same as the website address, then an attack might be taking place.

Verify the client's IP address

At login, the server records the IP address of the client, and then checks at each sensitive request if the stored address is the same as the current one.

This can be problematic due to the use of proxy servers. Also the IP address can be spoofed.

Verify the client's user-agent

At login, the server records the user-agent of the client, and then checks at each sensitive request if the stored user-agent is the same as the current one.

The user-agent can be spoofed, so this should not be the only method to be relyed upon.

Disable registering of globals

The php.ini setting for this is:

register_globals = off

This can prevent attacks which use Session poisoning. From PHP 4.2.0 it is off by default, and it is removed in PHP 6.0.0. Just make sure it isn't turned on by accident.

Securing the registration and login process

Request email confirmation

When users register in an online application, use an email confirmation process. This will prevent automated registrations from bots.

The registration emails should not contain the password that the user used to register, as it can be seen by people or sniffers.

Use CAPTCHAs

Another way to prevent automated registrations is to use CAPTCHAs. Choose one implementation that does not make it too hard for humans to read, but still maintaining a secure enough level to prevent bots using OCR technology.

Secure the password recovery process

If using the md5 () or sha1 () functions in PHP to store the passwords (as discussed earlier), there is no way to recover the password from the database.

In this case, the server can generate a temporary password with which the user can login to his account administration tool and change the password to something else. The temporary password can be sent to the user by email.

The process of password recovery can be secured more by having the user answer a question which he/she specified during the registration process, such as "What was your pet's name?" or "What is your birthday?. This will prevent the case where anyone can get a new password for an account name which they found out.

The password recovery should be limited to only a few uses to prevent attacks.

Limit the number of login attempts

Sometimes an attacker might know a partial password and will try repeteadly to login. Specifying a limited number of permitted login attempts can prevent this.

Login to the application for that account should be blocked for a specified amount of time. It can also be blocked for a longer time and an email should be sent to the account owner with a link to the or to the account unlocker tool or to the password recovery tool.

Time-out "Keep me logged-in"

When using "Remember me" or "Keep me logged-in" during login, the logged-in time must be limited (eg. 24 hours) to ensure attackers can't gain access when using a stolen computer.

Securing the server-side

Protect include files

Do not use the extension .inc for include files, instead use .php, so that the files will not be displayed in the browser if they are accessed directly.

Protect server-side code and data

By adding in the httpd.conf entries for the directories that contain sensitive code or data of the application, attackers are prevented to see their contents.

<Directory "/home/user/http/logs/"> Order deny,allow Deny from all Allow from 127.0.0.1 </Directory>

An alternative is to use a .htaccess file in every directory that must be protected.

Order deny,allow Deny from all Allow from 127.0.0.1

The server will access the .htaccess files on every request. The httpd.conf file is accessed only on starting the server. This means there is a small performance hit by using .htaccess files, which must be taken into consideration when developing large applications.

Protect sensitive files from search engine spiders

You can create a file named robots.txt in the website's root directory, with the contents:

User-agent: * Crawl-delay: 120 Disallow: /path/

Write as many "Disallow: /path/" lines as necessary, with "path" being the directories which should not be indexed by the search engine spiders, like templates, compiled templates, server-side code, configuration directories, log directories, tmp directories, premium content directories, etc.

The Crawl-delay setting (in seconds) can prevent a spider indexing the website too quickly (which might cause bottlenecks).

There are spiders which don't follow the robots.txt (bad bots). Against these spiders you can use a bot trap or the httpd.conf or .htaccess files to prevent the bad spiders to access the application directories.

The robots.txt file is not needed if you used the previous method (httpd.conf or .htaccess files) to protect directories, as it gives away information about the directory structure of the application.

Prevent directory traversal

More information can be found on the Directory traversal article on Wikipedia.

Prevent cross-site attacks

Recommended reading: - Cross-site request forgery - Cross-site cooking - Cross-site scripting - Cross-zone scripting

Secure file uploads

Use generated names for uploaded files or mail attachments instead of the original names, so that attacks relying on filenames containing paths will be prevented.

Use a maximum file size for uploads, using php.ini:

; maximum upload size upload_max_filesize = <value in bytes> ; maximum size for POST data post_max_size = <value in bytes> ; the temporary directory to keep the uploaded files upload_tmp_dir = /tmp

The temporary directory must be writable by the user under PHP is running. The php.ini value for memory_limit should be higher than the upload_max_filesize value.

Validate and clean input

Prevent SQL injection attacks by validating and cleaning data coming from a web request before running SQL queries on them.

Don't rely only on client-side validation for data (although client-side validation can be employed to speed up validation and reduce network load).

Do not display errors

Displaying errors might provide attackers with security information about the application, such as file paths, database schema, etc. The php.ini settings for this are:

; do not display errors display_errors = off ; do not display errors occuring during PHP's startup sequence display_startup_errors = off

Securing the client-side

Sending passwords from clients

In order to transmit securely a password from the login page to the server code, the PHP functions md5 () or sha1 () can be used.

A more secure way is to add a random number (generated by the server) to the password, and then apply md5() or sha1() to this string. That is because rainbow tables of hashes can be used by attackers to find the unhashed strings.

This method requires implementing an md5() or sha1() function in JavaScript, or using an already made one.

Since PHP 5.1.2 you can use the function hash () which has support for different hashing methods (MD5, SHA1, SHA256, etc). One added bonus is that the hash() function is faster to execute than the md5() and sha1() functions.

Passwords should be enforced to contain numbers and other characters apart from letters, and/or be at least 8 characters in length, to make them more secure against dictionary and rainbow tables attacks.

Use POST forms

Use POST requests instead of GET for operations that may involve modifying the data on the server (add, delete, edit). GET requests should be employed only for retrieving data.

Protection from reloads

After a request from the client (login, delete, add, buy, etc), it is possible that the user reload the page, thus sending the request again. This should be detected and avoided.

Use an encrypted connection

Use SSL/TLS to prevent sniffers from stumbling upon sensitive information like usernames, passwords, social security numbers or credit card numbers.

References

Configuration files