Secure LAMP server for production use
Medidas especificas para un Web sever:
Check out Bastille, it's a series of scripts that implements best practices in Linux.
Don't send authentication data over plaintext protocols. For example, disable FTP. If you send authentication data via Apache, use SSL.
Disable and remove any unnecessary software including the GUI interface.
Audit any files with the SUID bit set and remove. (This will severely limit non-root abilities. Understand the implications for each individual change.)
Audit public writable directories and remove the writable bit. (Leave /tmp alone.)
Avoid running any daemon as root.
Research all multi-user software that listens on sockets in detail for security best practices.
Avoiding adding users to the system is one of the best approaches. Multi-user systems require greater attention to detail.
Enforce password standards. For example: minimum 10 characters, non-alphanumeric characters, using letters and numbers. This is to make brute forcing more difficult in case of password file compromise. Enforce this via the system.
Lock out users after 5 failed authentication attempts with a minimum of 10 minute lockout. Maintain a password history so users can't use the past 5 passwords.
If you have a larger environment, using network segregation with multiple subnets to isolate risk is an absolute requirement. If a smaller environment, running a firewall on the local system to limit exposure is recommended. For example, only allowing SSH to your IP. tcpwrappers can be used too for an extra layer. (/etc/hosts.allow, /etc/hosts.deny)
And, of course, keeping all software up to date. Especially public facing daemons.
With SSH:
Check out Bastille, it's a series of scripts that implements best practices in Linux.
Don't send authentication data over plaintext protocols. For example, disable FTP. If you send authentication data via Apache, use SSL.
Disable and remove any unnecessary software including the GUI interface.
Audit any files with the SUID bit set and remove. (This will severely limit non-root abilities. Understand the implications for each individual change.)
Audit public writable directories and remove the writable bit. (Leave /tmp alone.)
Avoid running any daemon as root.
Research all multi-user software that listens on sockets in detail for security best practices.
Avoiding adding users to the system is one of the best approaches. Multi-user systems require greater attention to detail.
Enforce password standards. For example: minimum 10 characters, non-alphanumeric characters, using letters and numbers. This is to make brute forcing more difficult in case of password file compromise. Enforce this via the system.
Lock out users after 5 failed authentication attempts with a minimum of 10 minute lockout. Maintain a password history so users can't use the past 5 passwords.
If you have a larger environment, using network segregation with multiple subnets to isolate risk is an absolute requirement. If a smaller environment, running a firewall on the local system to limit exposure is recommended. For example, only allowing SSH to your IP. tcpwrappers can be used too for an extra layer. (/etc/hosts.allow, /etc/hosts.deny)
And, of course, keeping all software up to date. Especially public facing daemons.
With SSH:
- Disable SSH protocol 1
- Only allow root authentication
without-password
(only keypair)
- Disable any modules that are not needed
- Disable .htaccess and public directories
- Disable FollowSymlink and any unnecessary options
- Do not install PHP if you don't need it.
- Disable default users.
- Don't use wildcard hosts.
- Be sure to set unique host for every user.
- Don't listen on tcp unless necessary. (Unusually unavoidable.)
- Limit application user privileges as much as possible. (SELECT,INSERT,UPDATE,DELETE ideal for write and SELECT for read)
Install and configure common services
At this stage, you should have a solid base to build upon. The next step is to compile and install the software servers you will use to serve up your web applications. Installing software from source can be tedious; and there is a great temptation to use the packaged binaries that come with your distribution of choice. I would recommend against this. In a world of zero day exploits, the time it takes the package maintainer to compile and distribute the binaries may be unacceptable. By compiling from the source, you will be in full control of your security situation.
Apache 2.2.0
Now to start compiling and install the services you will be using. Apache is a good place to start, since other packages have compile time dependencies on it.
Always verify the `checksums` of packages you download, if there is a mismatch start over and download it again
md5sum httpd-2.2.0.tar.gz tar -xzvf httpd-2.2.0.tar.gz ./configure --prefix=/usr/local/apache \ --enable-ssl --enable-speling \ --enable-rewrite --enable-proxy make make install
MySQL 5.0.18
Download the MySQL binaries from the mysql site (see resources). This is an exception to my mantra of compiling from source—since the binaries come directly from MySQL, as soon as there is an update you can download the latest version.
Note: due to space constraints {.} is shorthand for the version of the tarball.
md5sum mysql-{.}-linux-i686-glibc23.tar.gz cp mysql-{.}-linux-i686-glibc23.tar.gz /usr/local tar -xzvf mysql-{.}-linux-i686-glibc23.tar.gz ln -s /usr/local/mysql-{.}-linux-i686-glibc23 \ /usr/local/mysql groupadd mysql useradd -g mysql mysql cd mysql scripts/mysql_install_db --user=mysql chown -R root . chown -R mysql data chgrp -R mysql . bin/mysqld_safe --user=mysql & cp support-files/mysql.server /etc/init.d/mysql # Make sure that mysql is started if # there is a reboot cd /etc/rc3.d/ ln -s /etc/init.d/mysql S90mysql ln -s /etc/init.d/mysql K90mysql # Copy the configuration file cp support-files/my-medium.cnf /etc/my.cnf
PHP 5.1.2
Now download the php package from the php.net site (see resources).
md5sum php-5.1.2.tar.gz tar -xzvf php-5.1.2.tar.gz ./configure --with-prefix=/usr/local/php \ --with-apxs2=/usr/local/apache/bin/apxs \ --with-mysql=/usr/local/mysql make make install cp php.ini-dist /usr/local/php/php.ini
Tomcat 5.5.16
Apache-Tomcat is also an exception to my always compile rule.
md5sum apache-tomcat-5.5.16.tar.gz tar -xzvf apache-tomcat-5.5.16.tar.gz
mod_jk
Download
mod_jk
from the the tomcat project page (see resources).tar -xzvf jakarta-tomcat-connectors.tar.gz cd jakarta/jk/native ./configure --with-apxs=/usr/local/apache/bin/apxs make make install
mod_security
mod_security
is the most excellent Apache module written by Ivan Ristic.md5sum modsecurity-apache-1.9.2.tar.gz tar -xzvf modsecurity-apache-1.9.2.tar.gz cd modsecurity-apache-1.9.2/apache2 /usr/local/apache/bin/apxs -cia mod_security.c
Configuring Apache 2.2.0
By this point, you should have installed all of the services and apache modules needed to host and secure PHP and Java environments. Now it’s time to take a look at properly configuring everything for security.
_Apache 2.2.0_ has the ability to list both statically compiled and shared modules with the `-M` option to `apachectl`
Apache 2.2.0 introduces a new configuration layout and new default options in the
httpd.conf
file. In previous versions of Apache, there was only one configuration file by default, which wasconf/httpd.conf
; in the current version, the Apache project has taken another step towards its goal of making configuration more managable and modular. The conf/httpd.conf
is the main configuration file with include statements for the various configuration files under conf/extra
such as httpd-ssl.conf
and httpd-vhosts.conf
.
Another new and exciting security feature in Apache 2.2.0 is that the root
httpd.conf
access is denied to all directories. This can be confusing to users coming from previous versions such as 2.0.55 but it is a great step forward in terms of security.
Here is the configuration directive mentioned above; I would suggest leaving it the way it is. We will allow access for specific virtual-hosts and directories later on.
# Default access control \ # Highly restrictive and applies \ # to everything below it.Options FollowSymLinks AllowOverride None Order deny,allow Deny from all
Every host, which Apache will be responsible for, will be a virtual host or “vhost”. So, start by uncommenting the
Include
directives at the bottom of conf/httpd.conf
. This is done so that thehttpd-vhosts.conf
, httpd-ssl.conf
and httpd-dedfault.conf
are included in the mainhttpd.conf
configuration file.
Yet another cool new feature in Apache 2.2.0 is the ability to see all modules both statically compiled and shared with the
-M
option to apachectl
.# Edit /usr/local/apache/conf/httpd.conf # Move the LoadModule Statement for mod_security # to the top of all module # statements. This is needed to use SecChrootDir # Add the following line to load mod_jk LoadModule jk_module modules/mod_jk.so # The default User and Group is set to daemon, # create and apache user and group and then # configure apache to run as such. User apache Group apache # List all modules to verify that mod_jk, # mod_security, mod_php, and mod_ssl # are correctly installed and loaded. /usr/local/apache/bin/apachectl -M # Virtual hosts Include conf/extra/httpd-vhosts.conf \ -> Include conf/extra/httpd-vhosts.conf # Various default settings Include conf/extra/httpd-default.conf \ -> Include conf/extra/httpd-default.conf # Secure (SSL/TLS) connections Include conf/extra/httpd-ssl.conf \ -> Include conf/extra/httpd-ssl.conf
Now it’s time to descend into the extra directory to configure virtual hosts. Open
httpd-vhosts.conf
in your favorite editor. Next, configure the document root to be /srv/www/vhost1
. Also, add theAddType
directive for php. PHP files don’t have to have the .php
file extension. In fact, it’s probably a good idea if they are .html
files. By using the .php
file extention you are advertizing more information about your setup than you need to.
`mod_security` makes `chrooting` Apache easy!
Here is the full vhost configuration stanza. It is annotated with inline comments: please take the time to read through it.
# Enable mod_security engine SecFilterEngine On # Chroot Apache the easy way just make sure # your web content is under the chrooted directory # Note: The log directives must also be valid # directories releative to the chroot dir. # Note: THIS CANNOT GO INSIDE VHOST STANZA # as it applies to the entire apache configuration SecChrootDir /chroot/apache # Delete the 2nd Vhost stanza as you will only be # using one for nowServerAdmin webmaster@mydomain.com DocumentRoot /srv/www/vhost1 ServerName vhost.mydomain.com ServerAlias www.mydomain.com ErrorLog logs/vhost.mydomain.com-error_log CustomLog logs/vhost.mydomain.com-access_log \ common # The PHP engine will interpret all # .php and .html files for php code AddType application/x-httpd-php .php .phtml \ .html AddType application/x-httpd-php-source .phps # Add a local Directory directive to override # the global Deny From all in conf/httpd.conf Options FollowSymLinks AllowOverride None Order deny,allow Allow from all # Restrict Access to sensitive filesDeny from all # Configure MOD_JK JkWorkersFile \ "/usr/local/apache/conf/workers.properties" JkLogFile \ "/usr/local/apache2/logs/mod_jk_www.log" JkLogLevel info JkLogStampFormat "[%a %b %d %H:%M:%S %Y] " JkOptions +ForwardKeySize +ForwardURICompat \ -ForwardDirectories JkRequestLogFormat "%w %V %T" # Any URL that begins with /java/ will be # forwarded to the java webapp in tomcat JkMount /java/* ajp13 # Enable and configure MOD_SECURITY # See the mod_security documentation # link in resources [3] # POST Scanning is disabled by default SecFilterScanPOST On # Make sure only content with standard # encoding types is accepted SecFilterSelective HTTP_Content-Type \ "!(^$|^application/x-www-form-urlencoded$|^multipart/form-data;)" # Default Action is to reject request, log, and # then return HTTP Response 404 which is # File Not Found. # Another option would be 403 which is access # denied. SecFilterDefaultAction "deny,log,status:404" # Enable URLEncoding validation. This can # prevent Cross-Site Scripting attacks SecFilterCheckURLEncoding On # Catch and Prevent PHP Fatal Errors from # being displayed to the USER SecFilterSelective OUTPUT "Fatal error:" \ deny,status:500 # Obviously you have to code up this custom # Error Page ErrorDocument 500 /php-fatal-error.html # This can be useful to avoid stack overflow # attacks default is 0 255 ie All bytes allowed SecFilterForceByteRange 32 126
Here is the
mod_jk
configuration file apache/conf/workers.properties
(referenced above):worker.list=ajp13 workers.tomcat_home= \ /usr/local/java/apache-tomcat-5.5.12 workers.java_home=/usr/local/java/jdk1.5.0_06 worker.ajp13.type=ajp13 worker.ajp13.host=localhost worker.ajp13.port=8009
Apache can be used to configure other web consoles such as the Tomcat Manager application
The configuration stanza below is a variation on an article:
ServerAdmin webmaster@zero-analog.com DocumentRoot /srv/www/hercules.zero-analog.com ServerName hercules.zero-analog.com ErrorLog logs/hercules.zero-analog-error_log CustomLog logs/hercules.zero-analog-access_log \ common # This is the ssl-vhost under extra/httpd-ssl.confOptions FollowSymLinks AllowOverride None Order deny,allow Deny from all # This whole stanza is really to forward http # requests to https:// tomcat manager # so appended /html to the rewrite target. # since the full path is manager/htmlRewriteEngine on RewriteCond %{HTTPS} !^on$ [NC] RewriteRule . \ https://%{HTTP_HOST}%{REQUEST_URI}/html \ [L] # General setup for the virtual host DocumentRoot "/srv/www/outpost.zero-analog.com" ServerName hercules.zero-analog.com:443 ServerAdmin webmaster@zero-analog.com ErrorLog /usr/local/apache2/logs/error_log TransferLog /usr/local/apache2/logs/access_log Options FollowSymLinks AllowOverride None Order deny,allow RewriteEngine on RewriteRule "^/manager$" \ "https://outpost.zero-analog.com/manager/html" \ [R,L] JkWorkersFile \ "/usr/local/apache2/conf/workers.properties" JkLogFile \ "/usr/local/apache2/logs/mod_jk_hercules.log" JkLogLevel info JkLogStampFormat "[%a %b %d %H:%M:%S %Y] " JkOptions +ForwardKeySize +ForwardURICompat \ -ForwardDirectories JkRequestLogFormat "%w %V %T" JkMount /manager/* ajp13 # Enable/Disable SSL for this virtual host. SSLEngine on # List the ciphers that the client is permitted # to negotiate. See the mod_ssl documentation # for a complete list. SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA: \ +HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL # Server Certificate: Point SSLCertificateFile # at a PEM encoded certificate. SSLCertificateFile \ /usr/local/apache/conf/server.crt # Server Private Key: SSLCertificateKeyFile \ /usr/local/apache/conf/server.key SSLOptions +FakeBasicAuth \ +ExportCertData \ +StrictRequireSSLOptions +StdEnvVars SSLOptions +StdEnvVars BrowserMatch ".*MSIE.*" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 # Per-Server Logging: # The home of a custom SSL log file. # Use this when you want a compact non-error # SSL logfile on a virtual host basis. # *** Note there are \ characters in this # string. These are not my artificial line- # breaks, please include them *** CustomLog /usr/local/apache2/logs/ssl_request_log %t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
Configuring PHP
I have already copied the
php.ini
to /usr/local/php/php.ini
so that it will be read by the PHPengine at startup. By default it’s fairly secure, but there are one or two things you can do to improve security.
Disable PHP display_errors for security
# Edit /usr/local/php/php.ini with your # favorite editor # Since you're are going through the trouble of # hiding PHP files you might as well disable # this as well expose_php = On -> expose_php = Off # You really don't want users, or worse yet # an attacker to see error messages display_errors = On -> \ display_erros = Off # But you do want them logged \ log_errors = Off -> log_errors = On # Log to a file ;error_log = filename -> \ error_log = /var/log/php-err
Another option to consider is the Hardened-PHP project (see resources section). This is the brain child of three German developers, who continously perform code audits of popular PHP applications. They also release a patch for the standard PHP code, which fixes bugs and security holes in fringe configuration cases, where the main project developers have not had the time or the desire to find a fix.
Configuring Tomcat
The main configuration files from Tomcat are under
$CATALINA_HOME/conf
. The following files are of interest:- tomcat-users.xml
- server.xml
- web.xml
As you might have guessed, the
tomcat-users.xml
file contains user access information. It is important to create a custom user with a hard-to-guess password for the manager application.# Edit tomcat-users.xml with your favorite # editor and append the following line.
Another important tenet of security is preventing information leakage. That’s why you enable PHP pages to masquerade as html files. It’s also why you want to disable directory listings in Tomcat. This is achieved by editing the
tomcat/conf/web.xml
file.# Open web.xml in your favorite editor and # look for the following lines:# The default value is true, which enables # directory listings, simply change this to false # to prevent tomcat directory listings. listings false
You can also hide JSP files by using a
servlet-mapping
directive in the webapps web.xml
configuration file. The following lines will map all html files to the JSP servlet, which is internal to Tomcat.
Note: The listing above goes in the main
tomcat/conf/web.xml
, while this listing should go in theweb.xml
of each application. You can put it in the main web.xml
. However, there may be cases where this is undesirable.jsp *.html
I want to restrict access to
apache-tomcat
to control the flow through Apache. iptables
is already blocking port 8080 by default, but following the onion principle I’m going to bound Tomcat to loop-back device. This will prevent direct traffic to Tomcat in case of any unforeseen circumstances such as theiptables
rules being flushed.# Open sever.xml in your favorite editor # and look for the following lines:# And change to this:
Configuring MySQL
The default MySQL installation is not very secure. This is the case when it is installed manually and also when you use your distribution’s precompiled binaries. The default root password is blank, which means anyone can login is as the DBA. This is understandable, as, obviously, a DBA has to login to set the password. However, it’s too easy to forget that anyone can login as the MySQL root user and anonymous users are also enabled by default.
# Set the root password mysqladmin -u root -h localhost password subGen1us # Once this is done, log in as the root user and # disable anonymous accounts mysql -u root -p # Drop the test database which comes installed # by default mysql> drop database test; # Disable anonymous accounts mysql> use mysql; mysql> delete from db where User=’’; mysql> delete from user where User=’’; # Change DBA NAME mysql> update user set user="mydbadmin" \ where user="root"; mysql> flush privileges; # Make sure to login again to make sure # all the changes work mysql -u mydbadmin -p password: subGen1us # Configure /etc/my.cnf for security Uncomment # the following line to disable TCP connections # to mysql. As with tomcat this prevents remote # connections event in the even of the firewall # even in the even of the firewall rules being # flushed. skip-networking
Security mistakes in web applications
Now that you are done with configuration, it’s time to put your web developer hat on. You now have a very solid base upon which to build your web applications. This brings me to the Achilles heal: the web applications themselves.
What is Cross Site Scripting (XSS)?
Wikipedia [4] defines the term Cross Site Scripting as inaccurate as it really refers to an entire class of vulnerabilities. In general XSS vulnerabilities come down to an age old security problem: not verifying user input. The most common vector of attack is when data is passed to a processing program such as a PHP or JSP script, and then printed back out to the page without being URLEncoded.
The following (highly contrived) PHP code is vulnerable to XSS. If the database to this PHP script contains javascript, it will be executed.
Never trust user input, it is the root of all evil
# Vulnerable Code # Secure Code
JSP’s or Java Servlets are no less vulnerable. First of all, it is important to understand that all JSP’s are compiled to servlets the first time a JSP is called. So, the two are basically the same thing, with different source code representations.
Here is the same vulnerability in the Java world. Note: JSP’s have access to the same
HttpServletRequest
object as the servlets they are compiled to. So, in a JSP page, this would manifest itself as request.getParameter()
.# Vulnerable Code public class myServlet extends HttpServlet { public static void doGet (HttpServletRequest req, HttpServletResponse res) { // Get User Input String userInput = req.getParameter("input"); // Print User Input to page PrintWriter out = response.getWriter(); out.write(""); out.write(userInput); out.write(""); } } # Secure Code import java.net.URLEncoder; public class myServlet extends HttpServlet { public static void doGet (HttpServletRequest req, HttpServletResponse res) { // Get User Input String userInput = req.getParameter("input"); // URLEncode Input userInput = URLEncoder.encode(userInput, "UTF-8"); // Print User Input to page PrintWriter out = response.getWriter(); out.write(""); out.write(userInput); out.write(""); } }
What is SQL Injection?
SQL Injection is the ability to insert and execute arbitrary SQL code through a web-application. Like XSS attacks, it involves mishandling user input. In this case, properly escaping the input that is to become part of the SQL query. The PHP solution is to use the
mysql_real_escape_string
statement, and the java solution is to use PreparedStatements
, with the user input as bind variables.
The following code snippets are from Wikipedia [5]:
# Partial PHP $query_result = mysql_query ( "select * from users where name = \"" . mysql_real_escape_string($user_name) . "\"" ); # Partial Java, ? is the bind variable Connection con = (acquire Connection) PreparedStatement pstmt = con.prepareStatement ("SELECT * FROM users WHERE name = ?"); pstmt.setString(1, userInput); ResultSet rset = pstmt.executeQuery()
Conclusion
There is no magic bullet. “Conclusion” is a misleading heading: there is no conclusion when it comes to security. Security holes are constantly found and patched. Attackers change their methods, and security professionals respond; it is a process, rather than a result. When it's implemented correctly, this process can mitigate or prevent the damage done by attacks.
There is a silver lining: there is an entire community of security professionals sharing their experience, tips and tricks on the web. Sites like Securityfocus.com provide heaps of useful information, and the latest trends in security. It’s also the home of bugtraq, a mailing list of security holes that I highly recommend you subscribe to. Sites like freshmeat.net are the yellow pages of free software projects, listing new releases and updates. All of these sites have RSS feeds, a resource I would also recommend you take advantage of to stay abreast of the latest news.
Resources:
http://scottlinux.com/2010/06/22/securing-apache-and-php/
http://www.symantec.com/connect/articles/securing-apache-step-step
http://www.debian-administration.org/articles/138
http://www.debian-administration.org/articles/138
Securing PHP
Fortificando un Servidor Apache
http://hell0.us/?p=43
Install mod_security in debian
http://www.symantec.com/connect/articles/securing-apache-step-step
http://www.debian-administration.org/articles/138
http://www.debian-administration.org/articles/138
Securing PHP
Fortificando un Servidor Apache
http://hell0.us/?p=43
Install mod_security in debian
No hay comentarios:
Publicar un comentario