Piwik
Note: OSE Decided *not* to use Piwik. The numerous security concerns documented below did not justify its benefits over awstasts. We should just use awstats.
Contents
- 1 Why
- 2 Security Concerns
- 3 Install
- 3.1 Step 0: Trigger Backup Scripts for System-Wide backup
- 3.2 Step 1: Set variables
- 3.3 Step 2: Make local backups
- 3.4 Step 3: Download & Configure Files
- 3.5 Step 4: Configure DB
- 3.6 Step 5: Configure Apache
- 3.7 Step 6: Install via WUI
- 3.8 Step 7: Harden config file
- 3.9 Step 8: Prevent OSSEC active response bans
- 3.10 Step 9: Add cron for auto-archiving
- 3.11 See Also
Why
Piwik is a self-hosted alternative to Google Analytics (GA). Unlike GA, Piwik is FOSS and can be self-hosted, so we're not dependent on sending (sensitive user's) data to an external service provider.
Security Concerns
When first adopted into the OSE stack in 2017, Piwik appears to be the best self-hosted GA alternative. That said, it is not without issues. Specifically, we found many eyebrow-rasising red flags about the project's security history & practices
Note: In the end, piwik is not a publicly accessible service. As such, the usual concerns (SQL Injection, XSS attacks, etc) are moot if the service runs behind http basic auth. Therefore, we use Piwik on "basic auth lockdown" until a better option is available
Requires ini_set()
The piwik project expects (in fact, very explicitly requires) the ini_set() function to be enabled.
Enabling the ini_set() function would effectively allow any php site to override all our php hardening made in php.ini. For example, we disabled exec() that prevents php from executing commands on our server. If Piwik can execute ini_set, then it could re-enable the exec() function. Combine that with a PHP injection vulnerability--which the Piwik project has had issues with in the past, and we have a huge security issue. This is why ini_set() is necessarily disabled on any php web server that wants any baseline for security.
It is absurd for an application to *require* ini_set() to be enabled, and--in fact--this is exactly what Piwik does. During the install process, Piwik errors-out (from 'core/testMinimumPhpVersion.php') if the function ini_set() is disabled.
Moreover, this issue was raised to them in 2009 (7 years ago from the time of writing), and the offical response was "wontfix"
See the Installation section below for instructions on how to bypass the ini_set() check during the install process.
Opaque Security Audits
Piwik likes to boast that they have strong security in their project, including a blog post from 2011 describing a professional security review of Piwik.
But the audit itself is glaringly missing from the blog post.
Professional security audits are a great idea, but the results should be published--especially after the remediations & patches have been released. Without transparently publishing the results of the audit, how can the community establish any vetting of the project that was audited? For example, maybe some critical, fundamental security concerns were marked "wontfix" as above. Without the results of the audit being published, we can't know.
Passwords stored in docroot
Obviously, it's necessary for Piwik's configuration to have some passwords (ie: db user credentials). However, we should store these outside the docroot. Their How to configure Piwik for security guide does not mention how to move this config file outside the docroot, which is stored in 'htdocs/config/config.ini.php' by default.
If this file is hit directly, it won't output anything--assuming php is running. However, it *is* a possibility that apache's config gets fucked (ie: if someone at OSE has to step-up & do the sysamdin work, but doesn't know exactly what they're doing), and php isn't running. Then Apache will happily dump the contents of the config file, including passwords.
For this reason, web application config files that contain passwords are usually located outside the docroot. This is a common/obvious practice that is either entirely unknown to the Piwik team, or simply undocumented. For example, wordpress will search one directory up above the docroot to find 'wp-config.php', which is where we store it at OSE.
Numerous Vulnerabilities
There have been numerous Piwik security issues in the past, mostly in the old versions.
The good news is they fix them. For example, this XSS mistake allowed anyone (even when not logged in) to update the plugin's settings--allowing anyone to add custom JS code to the WP website. This, of course, is extremely dangerous. After an independent security researcher notified the developer, they pushed a fix 19 days later.
This is worth noting, but not necessarily bad. I'd rather use a mature project with many users (and Piwik appears to be the most mature of its kind) where vulnerabilities have been reported & fixed--rather than a smaller project whoose vulnerabilities simply haven't yet to be discovered..
Install
The following commands will install piwik in a vhost
Step 0: Trigger Backup Scripts for System-Wide backup
For good measure, trigger a backup of the entire system's database & files:
sudo time /bin/nice /root/backups/backup.sh &>> /var/log/backups/backup.log
When finished, SSH into the dreamhost server to verify that the whole system backup was successful before proceeding
sudo bash -c 'source /root/backups/backup.settings; ssh $RSYNC_USER@$RSYNC_HOST du -sh backups/hetzner2/*'
Step 1: Set variables
Type these commands to set some variables, which will be used by the commands in the sections below. Replace 'opensourceecology.org' with the corresponding directory for the piwik vhost.
sudo su - export vhostDir="/var/www/html/piwik.opensourceecology.org" export piwikDocroot="${vhostDir}/htdocs" export stamp=`date +%Y%m%d_%T` export tmpDir="/var/tmp/piwik.${stamp}"
Step 2: Make local backups
The backups made in the 0th step are huge. Because it's easier to work with local backups, let's make a redundant copy available in /var/tmp/:
rootDbPass=CHANGEME chown root:root $tmpDir chmod 0700 $tmpDir pushd $tmpDir # create backup of all DBs for good measure service httpd stop time nice mysqldump -uroot -p$rootDbPass --all-databases | gzip -c > preBackup.all_databases.$stamp.sql.gz service httpd start
Step 3: Download & Configure Files
# make vhost docroot mkdir -p "${piwikDocroot}" # download mkdir "${tmpDir}" pushd "${tmpDir}" wget https://builds.piwik.org/piwik.zip # extract & copy to docroot unzip piwik.zip rsync -av --progress piwik/* "${piwikDocroot}/" # remove ini_set() requirement perl -pi -e ' BEGIN{undef $/;} s%(.*)if(.*)function_exists(.*)ini_set[^}]*}%${1}\n/***************************************************************************\n * disabling ini_set detction\n * it was intentionally disabled for security! -maltfield\n****************************************************************************\nif${2}function_exists${3}ini_set${4}\n*/%smg' "${piwikDocroot}/core/testMinimumPhpVersion.php" # htpasswd for basic auth; use a long, random password for the 'admin' user to # be stored in the shared keepass. feel free to add additional admin-specific # usernames here (all with long, random passwords) to be added to the admin's # personal keepass--without needing to open the higher-security shared keepass # on every login htpasswd -c -B "${vhostDir}/.htpasswd" admin # use -B for bcrypt for all users htpasswd -B "${vhostDir}/.htpasswd" maltfield # set (hardened) permissions chown -R apache:apache "${vhostDir}" find "${vhostDir}" -type d -exec chmod 0750 {} \; find "${vhostDir}" -type f -exec chmod 0640 {} \; chown apache:apache "${vhostDir}/.htpasswd" chmod 0400 "${vhostDir}/.htpasswd"
Step 4: Configure DB
Create your piwik db & db user. Use a unique, randomly-generated password of exactly 70 characters for the db user. Store this password in the shared keepass; we'll also need it when we install Piwik through their WUI below.
CREATE DATABASE piwik_ose_db; GRANT ALL PRIVILEGES ON piwik_ose_db.* TO "piwik_ose_user"@"localhost" IDENTIFIED BY "thisShouldBeARandomlyGeneratedPasswordOfExactly70Characters"; FLUSH PRIVILEGES;
Step 5: Configure Apache
TODO: update with config that allows non-basic-auth for necessary public files.
Create your vhost file, such as /etc/httpd/conf.d/piwik.opensourceecology.org
Listen 4444 <VirtualHost piwik.opensourceecology.org:4444> ServerName piwik.opensourceecology.org ServerAlias piwik.opensourceecology.org DocumentRoot "/var/www/html/piwik.opensourceecology.org/htdocs" Include /etc/httpd/conf.d/ssl.opensourceecology.org </VirtualHost> <LocationMatch .*\.(svn|git|hg|bzr|cvs|ht)/.*> Deny From All </LocationMatch> <Directory "/var/www/html/piwik.opensourceecology.org/htdocs"> # we don't trust piwik due to obvious sec incompetence in the project, so we # restrict the whole site with basic auth *in addition* to the untrusted # applicaiton-level auth AuthType Basic AuthName "Authentication Required" AuthUserFile /var/www/html/piwik.opensourceecology.org/.htpasswd Require valid-user Options -Indexes -Includes AllowOverride None Order allow,deny Allow from all # Harden vhost docroot by blocking all request types except the 3 essentials <LimitExcept GET POST HEAD> deny from all </LimitExcept> </Directory> # disable mod_security with rules as needed (found by logs in: # /var/log/httpd/modsec_audit.log <Location "/"> <IfModule security2_module> SecRuleEngine On </IfModule> </Location>
Step 6: Install via WUI
Now browse to the site in your browser, auth with the password given to htpasswd above, and follow the WUI to install Piwik.
For "Database Server" enter 'localhost' For "Login" enter 'piwik_ose_user' For "Password" enter the randomly generated 70-character db user password generated above & added to keepass For "Database Name" enter 'piwik_ose_db' For "Table Prefix" enter 'piwik_ose_' Leave "Adapter" at the default
Click "Next"
For "Super user login" enter 'insecure' to make it clear that we don't trust this application-level authorization Genereate a long, random, unique password, and enter it into the "Password" fields
Click "Next"
Step 7: Harden config file
After install we can lock-down the site-specific config file (this can't be created before the install):
chown apache:apache "${piwikDocroot}/config/config.ini.php" chmod 0440 "${piwikDocroot}/config/config.ini.php"
We also have to edit the 'trusted_hosts[]' variable in the '[General]' section. Add the port to the URI that's configured. If you don't do this, the WUI will yell at you until you fix it.
Also add 'force_ssl = 1' to the '[General]' section per the How to configure Piwik for security guide
vim ${piwikDocroot}/config/config.ini.php
... [General] trusted_hosts[] = "piwik.opensourceecology.org:4444" force_ssl = 1 ...
Ideally, we these config files should be located outside the docroot (as is done with wordpress). But this isn't an issue if the site is behind basic auth & therefore not accessible to the world.
Step 8: Prevent OSSEC active response bans
It is normal behaviour for the piwik admin site to do many sequential, rapid POST queries in the WUI. This behaviour is very DOS-like, and OSSEC's default config will monitor /var/log/httpd/access_log for such behaviour. When it sees it, OSSEC bans the client's IP address for 10 minutes per the active response config using iptables & hosts.deny.
Add the following rule to '/var/ossec/rules/rules/local_rules.xml' file at the end, within the <group> block. Be sure to update the rule id to be unique and the <match> to coorespond to your new piwik vhost uri & port:
<rule id="100050" level="0"> <if_sid>31533</if_sid> <match>https://piwik.opensourceecology.org:4443/index.php</match> <description>it is normal piwik behaviour to spam POST requests; this prevents an active response ban of admins interacting with the piwik wui</description> </rule>
And restart OSSEC
service ossec restart
Step 9: Add cron for auto-archiving
TODO