Tag Archives: Plesk

Modsecurity for Plesk 2

I already spend some attention on this subject. A customer complained that the webmail did not work no more. The user was able to logon but could not send an e-mail. Obviously this had something to do with Modsecurity. I’m running Plesk on a Ubuntu server. So enabling Modsecurity and enforcing this down on all customers is troublesome. But Plesk does not give you the option to enable or disable Modsecurity on “system” subdomains. Like for example webmail.domain.tld. So this causes an issue when Modsecurity is enabled system wide and webmail is needed. If you were to just read webmail and not answer everything will work just fine. There’re so many rules you’ll need to disable or exclude before it will run fine that it makes no sense to configure this. It’ll practically undermine the sole function of Modsecurity.

Plesk apparently  has a config file for each webmail subdomain. This file is located either here:

/etc/apache2/plesk.conf.d/webmails/roundcube/domain.tld_custom_webmail.conf

or here:

/etc/apache2/plesk.conf.d/webmails/horde/domain.tld__custom_webmail.conf

And now you’ll have to decide whether to exclude a great number of rules or to disabled Modsecurity completely.  If you want to disabled Modsecurity completely and want to enforce security by adding some security headers you can use the code below:

<IfModule mod_security2.c>
  SecRuleEngine Off
</IfModule>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options: nosniff
Header always set X-Frame-Options: SAMEORIGIN
Header always set X-XSS-Protection: "1; mode=block"
Header unset Content-Security-Policy
Header add Content-Security-Policy "default-src https: 'unsafe-inline' 'unsafe-eval';connect-src https: wss:"
Header always set Public-Key-Pins 'pin-sha256="2rpOTT3tKv5TF8/hZGAEvA5aJAwFrXSrEX404CzY9mM="; pin-sha256="gXyS+QHqbu4T0pNQLIgUX+WyMI3ndaViZacoqedaA/o="; max-age=2592000'
Header always edit Set-Cookie (.*) "$1; HTTPOnly; Secure"

If you want to disabled rules by ID (by url is impossible because that changes with every mail) you can use the code below:

<IfModule mod_security2.c>
SecRuleRemoveById 981319 960024 981231
</IfModule>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Content-Type-Options: nosniff
Header always set X-Frame-Options: SAMEORIGIN
Header always set X-XSS-Protection: "1; mode=block"
Header unset Content-Security-Policy
Header add Content-Security-Policy "default-src https: 'unsafe-inline' 'unsafe-eval';connect-src https: wss:"
Header always set Public-Key-Pins 'pin-sha256="2rpOTT3tKv5TF8/hZGAEvA5aJAwFrXSrEX404CzY9mM="; pin-sha256="gXyS+QHqbu4T0pNQLIgUX+WyMI3ndaViZacoqedaA/o="; max-age=2592000'
Header always edit Set-Cookie (.*) "$1; HTTPOnly; Secure"

I guess to risk is somewhat moderate if you consider that anyone can send you a message with html content. But sometimes security is a notch below functionality.

Making exclusions is a very normal task when Modsecurity is enabled. So how do you do that? Well next to what’s shown above you’ll have the option to disable rules based on the request url. When using WordPress for example. Many thing do not work very well. This is especially when OWASP is enabled.  So to create some rules you’ll have to edit the config file below:

/etc/apache2/modsecurity.d/rules/modsecurity_crs-plesk/modsecurity_crs_15_custom.conf

If the file does not exist, create it. You can than append to file with the rules and URL that you can find when watching the log file. Enable Modsecurity first in report only. Than add the necessary rules and your site is much safer.

SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261001,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973332"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261002,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=970903"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261003,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973344"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261004,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973333"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261005,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973306"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261006,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973304"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261007,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973300"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261008,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=973338"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261009,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=981243"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261010,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=981245"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261011,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=981317"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261012,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=950901"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261013,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=981231"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261014,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=950120"
SecRule REQUEST_FILENAME "@streq /wp-admin/post.php" "id:88261015,phase:1,t:none,nolog,pass,ctl:ruleRemoveById=960024"

 

HPKP explained

The CA system we currently have is insecure. Governments try to hack CA’s, hacker try to hack CA’s, Criminals try to hack CA’s basically anyone tries to hack CA’s. So if one of these hackers succeed that person can issue a certificate that is valid for whatever domain he want. No browser or client will ever doubt that certificate as it is issued by a trusted CA. This occurred when a Dutch CA Diginotar was hacked. Hackers used that certificate for gmail and so forth.

In defence for this security issue a RFC was created. Here’s where HKPK (HTTP Public Key Pinning)  is introduced. This technique is quite simple. A pin can be generated with a certificate. This pin is always the same with that certificate. You’ll need to have 2 certificates per CN or SAN. So if you have a certificate from let’s say StartSSL for the domain www.example.com you’ll need an additional certificate for www.example.com or let’s say a wildcard certificate for *.example.com. Next you generate a pin for these certificates. You enter the pin into a HTTPS header and your secure.

When a clients connects to your site it saves this pin. Lets say a CA gets hacked and a certificate is generated for your domain. Then that hacker uses your certificate for example in a MITM attack of if it is a government it can alter DNS for your domein. Then the client connect to your site again and the certificate does not match the saved pin. The client browser present you with an error that someone is trying to intercept the traffic and further processing will be stopped.

For now only a maintained list by Google is being used for HKPK. On this list are only large sites like Facebook, Twitter and Google. But soon this RFC will be implemented and available for anyone who uses a SSL certificate. When HKPK is being used a hacker or government will have to burn a CA every time they try to intercept traffic. A very nice result. Yet in the world before HKPK we have more than 1500 certificates.

By the end of 2015 HKPK is already proven to be valuable. Only for google.com Symantec issued a certificate while Google did not request the certificate. Symantec fired those employees. But this must only be the tip of the iceberg. Image how much HKPK can contribute to a safer net. I can only imagine that some government already are deleting certificates

But what if the certificate expires? Well that one is easy. You have two certificates in the header. Than just use the not expired certificate. Request a new certificate generate the new pin. Enter this in the header and remove the old pin. The only thing important here is that you must use a shorter period than the time between the expiration of certificate1 and certificate2.

So how do you make this work? First you’ll need to generate a SHA256 pin. You can do this for an online certificate like this:

Non-SNI

openssl s_client -connect www.bexit.nl:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

SNI

openssl s_client -servername www.bexit.nl -connect www.bexit.nl:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Or for a certificate by running this command

openssl x509 -in certificate.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

When you’ve generated the pins you can add them to the header by using a string like this:

Header always set Public-Key-Pins 'pin-sha256="hOKXjMHg/QuZ2q6n6Hzp6j+P+217O6Mzd70WJ2LgEBw="; pin-sha256="t3EN6WT7PvqYPL0af/BoG4YAjVucNaIhg+CkT/sDExM="; max-age=15768000'

Be careful not to enter a very long timeframe as you might run in to trouble later on.

Problem with Zen Photo

A webhosting client reported that he wasn’t able to connect to the admin area of Zen Photo.  My web service uses Plesk. His previous webhosting was under Cpanel. I first uploaded the setup files of his version of Zen Photo. Then I asked him to perform the update. This failed by an internal server error (500). The first thing I did at that point was looking through the logfiles. The log contain the following errors

mod_fcgid: stderr: PHP Warning: file_exists(): open_basedir restriction in effect. File(/var/lib/php5) is not within the allowed path(s):

To resolve this I added the path /var/lib/php5 to the allowed list. This is a slight security risk. Most binaries are located in the allowed list anyways so I added the configuration like below:

Screen Shot 2015-04-04 at 16.34.17

After this configuration change the users was able to connect to the admin area and also capable of upgrading Zen photo to the last stable built.

However quite soon after that the user started to report another problem. When he was browsing through the admin area he experienced internal server errors again. I started to look at the log files for that user and the following error was reported:

[core:error] [pid 5159] [client x.x.x.x:49520] End of script output before headers: admin-options.php, referer: 
[fcgid:warn] [pid 5159] (104)Connection reset by peer: [client x.x.x.x:49520] mod_fcgid: error reading data from FastCGI server, referer

The log reported this error from many different reference scripts. Mostly any reference script. By then I tried many things from disabling the XSS protection. Disabling HSTS, Disabling PHP modules and changing the file and folder permissions. I started looking at the error log of apache2 server located in /var/log/apache2/error.log. The following error caught my eye:

[fcgid:error] [pid 1886] mod_fcgid: process /var/www/cgi-bin/cgi_wrapper/cgi_wrapper(7917) exit(communication error), get unexpected signal 11
[fcgid:error] [pid 1886] mod_fcgid: process /var/www/cgi-bin/cgi_wrapper/cgi_wrapper(8193) exit(communication error), get unexpected signal 11

The first thing I thought was that the account of that user did not have enough permissions over the wrapper script. But that was not the case. So at that point I was able to rule out the web server and any file permissions. I somehow got the idea that this error must have had something to do with the difference in it’s PHP version between mine server at the one of the previous hoster. So I had an older server with PHP 5.3 installed. I migrated the account of that customer to that older server and let him recheck. The user reported no issue at all. PHP 5.3 uses xcache and PHP 5.5 uses the newer opcache as Zend extension. So what I did was changing an PHP configuration file (/etc/php5/apache2/conf.d/05-opcache.ini) from this:

; configuration for php ZendOpcache module
; priority=05
zend_extension=opcache.so

into this

; configuration for php ZendOpcache module
; priority=05
#zend_extension=opcache.so

I restarted the web server and have the customer look again. He did not find any issues. Of course this causes many issues on the server. I quickly reverted the file to the previous version.

Ultimately resolving this issue I did the following to his PHP configuration:

Screen Shot 2015-04-04 at 16.53.12

 

opcache.enable=0

 

I am so happy with Plesk and all it does to make my life easier.