- EJBCA SECURITY
- Firewall ports
- Securing JBoss
- Setting up SSL
- File permission
- User authentication
- Passwords defined when configuring EJBCA.
- Passwords used by EJBCA taken from property files
- Passwords used by the application server
- Passwords prompted for by 'ant install'
- Database Integrity Protection
- Datasource Passwords
- Database privileges
- SSL connections in MariaDB
- Other precautions
- Denial of Service
- External Log Signing
EJBCA SECURITY
Security is CRITICAL for a CA. Protection of the CA's private key is essential, since compromise of the CA's private key will let anyone issue false certificates, which can then be used to gain access to systems relying on the CA for authentication and other security services.
After realizing this, please read on and take a pragmatic approach to security, suitable for your policy, application and environment.
Firewall ports
To have a functional EJBCA installation there are only two (or three) ports that need to be open to the outside:
- 8080 - the public HTTP port of you application server, used for clients to access the public web for information, should not be used for enrollment since it's not encrypted.
- 8442 - the public HTTPS port (server side only SSL) of you application server, used for clients to access the public web for enrollment.
- 8443 - the SSL protected HTTPS port used to access the Admin GUI of EJBCA. This port requires client certificate for access.
You can choose to have both 8080 and 8442, or only one of them open whichever suits your needs.
Additional ports that you may need to open are SSH, outgoing SMTP, outgoing LDAP, etc. Which additional ports that need to be opened depends on your specific setup and which services you use. You do not need outgoing SMTP if you don't use email notification, for example.
Locally on the host, a number of ports are used in order for your application server to function.
Securing JBoss
JBoss 7
*** Secure Management Interfaces ***
See Securing JBoss 7 Management Interfaces.
You can remove the JMX console completely by removing the following part from standalone.xml (JBoss EAP 6.2):
<subsystem xmlns="urn:jboss:domain:jmx:1.3"> <expose-resolved-model/> <expose-expression-model/> <remoting-connector/> </subsystem>
You can remove the welcome pages by setting enable-welcome-root to false (JBoss EAP 6.2):
<virtual-server name="default-host" enable-welcome-root="false"
By default the management web console is only accessible if you configure user access to it, and it is not configured by default.
*** Disable specific HTTP methods ***
EJBCA by default limits the use of PUT|DELETE|TRACE|OPTIONS in it's web applications, but if you are unsure you can further enfoce it.
You can use an Apache front end, or you can use a RewriteValve in standalone.xml
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false"> <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/> <virtual-server name="default-host" enable-welcome-root="true"> <rewrite pattern=".*" substitution="-" flags="F"> <condition test="%{REQUEST_METHOD}" pattern="^(PUT|DELETE|TRACE|OPTIONS)$" flags="NC" /> </rewrite> </virtual-server> </subsystem>
*** Content Security Policy ***
CSP is something implemented in browsers to mitigate cross site scripting attacks. See the description over at Mozilla Developer Network, or Content Security Policy Reference.
CSP has been added to EJBCA web interfaces as of ECA-3519.
You can also implement if globally if you use an Apache front end with mod_headers):
<IfModule mod_headers.c> Header set Content-Security-Policy "default-src 'self'" </IfModule>
The above apache configuration is just an example and will probably not work well. See the headers returned by EJBCA for a working config.
JBoss has a description how to secure a default installation of JBoss. You should read this document before putting your application in production.
Check out: SecureJBoss
You should also make sure all sensitive files can only be read by the JBoss user. These files are for example the server.xml file where the password for the SSL keystores are stored.
Configuring for security
The easiest way to keep the installation secure is to block all default JBOSS ports (1099, 1476, 4444, 8082, 8083) from the outside and only allow traffic to Tomcat ports (8442 and 8443). This is because the public end-user actions can be performed through the public servlets, while administration tasks are performed directly on the beans.
See 'ejbcafirewall.sh' for an example how to configure an Iptables firewall in Linux for protecting EJBCA running on JBoss.
See JBoss documentation for a list of ports used in JBoss and for information about security in JBOSS/EJB and for how to set up SSL with JBOSS.
Setting up SSL
To set upp SSL communication for all HTTP traffic to the server follow the instructions for installing EJBCA, this will set up HTTPS for the admin-GUI automatically.
This will set up an SSL port open for the public at 8442 and an SSL port which require client certificate to access the Admin GUI at 8443.
File permission
The application server should be run as a special user. Files should be protected so the ONLY the user running the application server can access them.
By default JBoss is probably unpacked with read access to all, you should issue:
umask 077 chmod -R go-rwx *
In the JBOSS_HOME directory, to make the files readable only by the jboss user, and make this the default permission for files copied here.
If PKCS12 files are generated for users, the subdirectory ('p12') where they are stored and the generated files should be protected in the same way.
User authentication
Default user authentication for enrollment in EJBCA is done with a one-time password scheme. When a user has enrolled for a certificate his status is set to GENERATED and the password can not be used again to enroll for a new certificate. An administrator must re-set the user's status and preferably set a new password.
If you are exposing your public web pages to a wider audience there are a few functions you can use in order to counter perceived threats such as brute-force attacks.
- Enable password expiry using the User Password Expire Service. If a user forgets to use his/her one-time password it will be automatically disabled.
- Use password expiry with the Maximum number of failed login attempts setting when registering users.
- Increase the number of BCrypt rounds, making password hashing slower. See the setting of ejbca.passwordlogrounds in conf/ejbca.properties.sample.
If implementing other user authentication scenarios you should remember that certificate authentication is stronger than password based authentication (for example LDAP). If EJBCA users authenticate with some other (not one-time) password instead of usual one-time password, a strong authentication mechanism will be built on a weaker one.
Passwords defined when configuring EJBCA.
The configuration files (in $EJBCA_HOME/conf) contain some passwords. It is not considered to be a security risk to declare all these passwords in clear text. Anyone that can log on to the server with EJBCA can, apart from reading these files, also do anything he wants with the CLI of EJBCA. If an unauthorized person can use the CLI, then this is a severe security risk. Access to these passwords is itself not much of a problem, since they have no use outside of the server (except for 'password.encryption.key' which can be used to decrypt encrypted passwords stored elsewhere, e.g. external database server).
It is very important to restrict the access to the server to only a very few trusted individuals and promote separation of duties and the principle of least privilege, e.g. if there is a DBA, that person shouldn't have access to the application server, this way (if 'password.encryption.key' has been customized in application server) DBA won't be able to decrypt passwords stored in the database he has access to.
If you want to do something about these passwords the subsections of this section describes what should be done:
Passwords used by EJBCA taken from property files
Some of the passwords are used directly by EJBCA code. All these passwords may be configured encrypted in the same way as PINs used for auto activation could be encrypted (except for 'password.encryption.key' which is itself used for encrypting the other passwords).
List of these passwords across several configuration files:
password.encryption.key
ca.tokenpassword
ca.keystorepass
ca.cmskeystorepass
databaseprotection.tokenpin
Passwords used by the application server
Some of the passwords are not used by EJBCA but by the application server. If these passwords should be encrypted, it must be in a way so that they can be decrypted by the application server. You have to consult the documentation of the application server to find out how to encrypt them (example: datasource password in JBoss).
These passwords are:
mail.password in mail.properties
database.password in database.properties
Passwords prompted for by 'ant install'
If you don't define superadmin.password in web.properties, then 'ant install' will prompt for it. Since this password does not has to be known by EJBCA after the super admin token has been created, it will not exist in any file after the installation.
The passwords java.trustpassword and httpsserver.password, also in web.properties, are used to generate keystore files at 'ant install'. If either of these passwords are not predefined, then they will be prompted for during deploy and install.
If you let 'ant' prompt for these passwords you must set them (encrypted, if possible) in the application server configuration. The file for the application server is copied to the application server at 'ant web-configure' (e.g. jboss-5.1.0.GA/server/default/deploy/jbossweb.sar/server.xml) You must then manually substitute the strings 'changeThisToThePassword' in the configuration file with clear text or possible encrypted (application server specific) passwords.
Database Integrity Protection
Database integrity protection consists of an additional column in all protected tables, "rowProtection".
In order to be flexible, versions and keyid are embedded in the rowProtection line. Versions allow the tables themselves to be modified safely because the entity class can create "version 2" of the protect string with new fields, but still verify "version 1" and other legacy versions rows without the additional field. ProtectData can update the algorithm used, etc. by iterating the version number. The protection key can be changed because the user defines the keyid of the crypto token and always verifies using a specific keyid. The keyids are specified in configuration and multiple keyids can be active at any moment.
Configuration
Database integrity protection should be configured before EJBCA is installed and used the first time, otherwise unprotected data will be inserted into the database; once the system is configured for database protection, using that data will fail.
Database protection is configured in the file:
conf/databaseprotection.properties
Limitations
- One limitation on the application using this protection scheme is that we can now not do bulk updates using a JPQ query with UPDATE where xyz. This is because the @Pre/Post events are not triggered for JPA beans when doing that. This is a limitation that can be severe in very large installations;
- We can not do direct database updates, this is of course the whole idea of the database protection, but it is also a limitation nonetheless;
- When using an HSM for the protection, only digital signature protection is currently available, not HMAC.
Integrity Protected Security Audit
The IntegrityProtectedDevice security audit implementation stores the log entries to the database and relies on the Database Integrity Protection in EJBCA for protecting the authenticity of the log events. Each log event is given an identifier consisting of a "nodeId" and a "sequenceNumber" that is part of the integrity protected data. The nodeId is configurable and must be unique for each JVM in a cluster with shared database access. The "sequenceNumber" is unique per nodeId and starts with 0. The sequenceNumber for a node (JVM) is read when the first log entry is written to this device and then kept in memory for duration of the JVM's lifetime.
The security of this implementation relies on:
- Database integrity protection token.
- The sequence number in memory for each node (JVM).
Using a sequence number will prevent that a single log entry is removed without detection being possible. Keeping the sequence number in the JVM will prevent that all log entries up to a previous point in time is removed without detection being possible.
This implementation can not detect:
- Removal of all the latest log entries for a node up to a previous point in time if the node's JVM is not running.
- Forged log entries if the database integrity protection token is compromised.
The motivation for this design is that each node (JVM) does not have to wait for other nodes in a (shared database only) cluster. Internally in each JVM the only locking between threads happens when the sequence number is atomically updated. This will allow horizontal scaling to a very high degree without any JVM-to-JVM communication when the shared database supports row-locking.
When using this implementation the integrity of each fetched log entry is always validated when loaded from the database, but full validation with checks for missing sequenceNumbers are not performed.
Configuration of integrity protected log device is in the files:
conf/cesecore.properties conf/databaseprotection.properties
It is possible to verify a whole table with the Local Database CLI tool. If any row in the table can't be verified the tool will show it. For the AuditRecordData table it will also be checked with the sequence number that no row is missing.
*** Repairing sequence gaps ***
The audit log sequence number per node is a counter. Whenever there is a need to write an audit log the counter is fetched and incremented (as an atomic operation). If the write to the database fails:
- the counter will not be decreased (since multiple threads might be operating on it there is no way of knowing what the missing entry is)
- there will be a sequence gap in the log
There is currently no user friendly way to "repair" such sequence, but it should rather be document why this happened (or rather that the cause was technical and not malicious).
A non-user friendly way to repair sequence gaps follows this principle:
- In a copy of the database, delete all records except 1 (or as many as you have holes)
- update the record(s), using sql to change sequenceNumber to the ones missing, and primaryKey to something unique
- run ejbca-db-cli export from the copy database. This will result in a dump with only these records, matching the holes in the original database
- run ejbca-db-cli import to import this dump in the original database. This will insert the missing sequenceNumbers, using databaseprotection as configured in your ejbca-db-cli
Datasource Passwords
Before venturing into this chapter, be aware that it increases complexity of management of your system. You should consider if this really increase the security of your system and if it is required from policy.
If you do not like to have your DataSource password available in clear text in EjbcaDS.xml,
there is an article in the JBoss Wiki about encrypting them:
http://community.jboss.org/wiki/encryptingdatasourcepasswords
There is another explanation for JBoss 7 at Stackoverflow.
Summary:
- Run the following script to generate an encrypted encoded password (PLACEHOLDER is your password):
# java -cp /opt/jboss/modules/system/layers/base/org/picketbox/main/picketbox-4.1.1.Final-redhat-1.jar:/opt/jboss/modules/system/layers/base/org/jboss/logging/main/jboss-logging-3.1.4.GA-redhat-2.jar:$CLASSPATH org.picketbox.datasource.security.SecureIdentityLoginModule PLACEHOLDER
- Copy the encoded password. Paste it into the standalone.xml configuration file.
$ vim /opt/jboss/standalone/configuration/standalone.xml
Paste the following segment into the security-domains segment (where PLACEHOLDER_ENC is the encoded password you got from the above command):
<security-domain name="EjbcaDsEncryptedPassword"> <authentication> <login-module code="SecureIdentity" flag="required"> <module-option name="username" value="ejbca"/> <module-option name="password" value="PLACEHOLDER_ENC"/> </login-module> </authentication> </security-domain>
Look for the EjbcaDS datasource segment and replace the security segment (where PLACEHOLDER is your old clear text database password):
<security> <user-name>ejbca</user-name> <password>PLACEHOLDER</password> </security>
with the following value:
<security> <security-domain>EjbcaDsEncryptedPassword</security-domain> </security>
You can also look into using the vault.sh script, which you can find on the Internet.
Database privileges
During the installation process of EJBCA, JBoss creates all the required database tables. To do this the EJBCA-database user has to have CREATE TABLE privileges. During upgrades EJCBA needs CREATE and ALTER TABLE privileges. (SELECT, UPDATE, INSERT and DELETE privileges are also required.)
After the installation during normal operations only regular SELECT, UPDATE, INSERT and DELETE commands are needed. The table LogEntryData will only be used with SELECT and INSERT.
Instead of changing the privileges of the EJBCA-user, it's recommended to have two different users "ejbca" and "ejbca-admin". For regular operations "ejbca" is used, but for the installation and upgrades, EJBCA is re-deployed with "ejbca-admin" configured in conf/database.properties.
There is a script, doc/howto/mysql-privileges.sh that creates an SQL script that can be run to limit privileges on a MySQL database. The script will set restricted privileges for every table in the EJBCA database. See the script for in-line documentation.
SSL connections in MariaDB
MariaDB have a guide regarding usage of SSL connection for the database connection.
Other precautions
Switching to production mode
Switching to production mode (default) by setting ejbca.productionmode in conf/ejbca.properties will prevent ant from starting JUnit tests and deploying the CA build on an OCSP responder and vice versa.
System accounting in Linux
See your distribution for details about their package, below are general links.
http://www.linuxsecurity.com/docs/SecurityAdminGuide/SecurityAdminGuide-5.html
Denial of Service
Due too large data packages or slow connections
There is no way to limit the datapackages reaching JBoss through a HTTP request from within JBoss. This is due to the way the TCP protocol works. The best way to avoid this kind of DOS attacks is to use a firewall or proxy that can limit the size of request and configure it properly. For example, mod_reqtimeout can be used in an APache proxy.
External Log Signing
Signing logs as they are being archived (rotated) can be done in several ways, here are a few examples:
- Logs are sent to syslog and the syslog rotation can sign files when they are rotated, using openssl or a Time Stamp Server (TSA).
- Logs are stored by JBoss, a special log4j appender can be used to sign files when they are rotated, using a time stamp server (TSA).
Log signing using logrotate and openssl
Save and enable the below logrotate configuration for syslog, is should normaly be stored as the file syslog in the directory /etc/logrotate.d. If there is a previous configuration for syslog, you should remove this, perhaps merge some changes if you like. Syslog is normally not rotated in most linux distributions. After enabling the configuration you should make sure that logrotate is run as often as you like. Normally is is run every night. If you want to run it more often for syslog, you can add a crontab entry to run it every hour for example. In that cas call logrotate with the specified configuration file: logrotate syslog.conf Note, that when stored in logrotate.d, the syslog configuration will be called when logrotate is normally run. qc1.priv is the private key used for signing the logfile.
/var/log/syslog { rotate 5 postrotate /usr/bin/killall -HUP syslogd endscript lastaction OPENSSL=/usr/bin/openssl LOGFILE=/var/log/syslog FILE="$LOGFILE.`date +%F.%H:%M:%S`.log" SIGNATUREFILE="$FILE.sign" cp $LOGFILE.1 "$FILE" PRIVATEKEY="/etc/logsigner/qc1.priv" $OPENSSL dgst -sign $PRIVATEKEY -sha1 $FILE > $SIGNATUREFILE endscript }
A simple shell script below for signing anv verifying using openssl, as well as to convert a pkcs12 file to the public key and private key files that openssl uses.
To verify, you can issue the command:
log-sign.sh verify qc1.pub logfile.log lofile.log.sign
#!/bin/bash # Openssl command for signing and verifying #openssl dgst -sign superadmin.key -sha1 test.txt > test.txt.sign #openssl dgst -verify superadmin.pubkey -signature test.txt.sign -sha1 test.txt # Openssl command to convert a p12 file to cert and key files in pem # First cert: #openssl pkcs12 -in qc1.p12 -nodes -nokeys -clcerts -out qc1.pem # Then public key #openssl x509 -in qc1.pem -pubkey -noout > qc1.pub # Then private key: #openssl pkcs12 -in qc1.p12 -nodes -nocerts -out qc1.priv OPENSSL=/usr/bin/openssl SCRIPTNAME=`basename $0` OPTION=$1 DATE=`date +"%Y-%m-%d"` if [ "$OPTION" = "sign" ]; then PRIVATEKEY="$2" FILE="$3" SIGNATUREFILE="$4" $OPENSSL dgst -sign $PRIVATEKEY -sha1 $FILE > $SIGNATUREFILE exit 0 elif [ "$OPTION" = "verify" ]; then PUBLICKEY="$2" FILE="$3" SIGNATUREFILE="$4" $OPENSSL dgst -verify $PUBLICKEY -signature $SIGNATUREFILE -sha1 $FILE exit 0 else echo "Usage:" echo "To sign files you have to edit the script and add the files you want signed." echo "$SCRIPTNAME sign " echo "$SCRIPTNAME verify " exit 0 fi
Log signing using logrotate and TSA
The same approach is taken for signing using a Tie Stamp Authority (TSA) server instead of openssl. In this example the timeStampClient and TSA from SignServer is used. After building'signserver' the timeStampClient.jar and other needed jar files are availale.
The new logrotate configuration:
/var/log/syslog { rotate 5 postrotate /usr/bin/killall -HUP syslogd endscript lastaction TSACLIENTDIR=/opt/signserver/dist-client LOGFILE=/var/log/syslog FILE="$LOGFILE.`date +%F.%H:%M:%S`.log" SIGNATUREFILE="$FILE.sign" cp $LOGFILE.1 "$FILE" java -jar "$TSACLIENTDIR/timeStampClient.jar" -url "http://127.0.0.1:8080/signserver/tsa?signerId=1" -infile $FILE -outrep $SIGNATUREFILE -base64 endscript }
Replace the ip-address and port (127.0.0.1:8080) with the ip and port of your actual TSA.
And the new shell script for verification (or manual signing), log-sign-tsa.sh
#!/bin/bash TSACLIENTDIR=/opt/signserver/dist-client SCRIPTNAME=`basename $0` OPTION=$1 DATE=`date +"%Y-%m-%d"` if [ "$OPTION" = "sign" ]; then FILE="$2" SIGNATUREFILE="$3" TSAURL="$4" java -jar "$TSACLIENTDIR/timeStampClient.jar" -url $TSAURL -infile $FILE -outrep $SIGNATUREFILE -base64 exit 0 elif [ "$OPTION" = "verify" ]; then PUBLICKEY="$2" FILE="$3" SIGNATUREFILE="$4" java -jar "$TSACLIENTDIR/timeStampClient.jar" -verify -inrep $SIGNATUREFILE -signerfile $PUBLICKEY sha1sum $FILE exit 0 else echo "Usage:" echo "To sign files you have to edit the script and add the files you want signed" echo "$SCRIPTNAME sign" echo "$SCRIPTNAME verify" exit 0 fi
When verifying the time stamp token, the sha1 hash from the time stamp token (signed) and the calculated sha1 hash of the file is printed. You must compare them manually.
Log signing using external script in JBoss
(unsupported)
There is an implementation that runs an external script when JBoss log files are rolled-over. This can be every hour for example.
- Stop JBoss
- Issue the command: ant jbosslogsigning
- Copy dist/ejbcalogsigning.jar to jboss.home/standalone/lib
- Configure jboss.home/server/default/conf/log4j.xml, according to below
- Create the script that is to be run (see example below)
- Start JBoss
Replace the logging section in standalone.xml with something like the following in order to use the ScriptrunningDailyRollingFileAppender.
<!-- A time/date based rolling appender that signs rolled over log files using TSA --> <appender name="FILE" class="org.ejbca.appserver.jboss.ScriptrunningDailyRollingFileAppender"> <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/> <param name="File" value="${jboss.server.log.dir}/server.log"/> <param name="Append" value="false"/> <param name="Script" value="/home/jboss/log-sign-tsa-jboss.sh"/> <!-- Rollover at midnight each day --> <param name="DatePattern" value="'.'yyyy-MM-dd"/> <!-- Rollover at the top of each hour <param name="DatePattern" value="'.'yyyy-MM-dd-HH"/> --> <!-- Rollover at the beginning of every minute <param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm"/> --> <layout class="org.apache.log4j.PatternLayout"> <!-- The default pattern: Date Priority [Category] Message\n --> <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/> <!-- The full pattern: Date MS Priority [Category] (Thread:NDC) Message\n <param name="ConversionPattern" value="%d %-5r %-5p [%c] (%t:%x) %m"/> --> </layout> </appender>
Where /opt/log-sign-tsa-jboss.sh is your script that should be run to do the signing.
#!/bin/bash TSACLIENTDIR=/opt/signserver/dist-client TSAURL="http://127.0.0.1:8080/signserver/tsa?signerId=1" SCRIPTNAME=`basename $0`FILE="$1" SIGNATUREFILE="$FILE.tsa" java -jar "$TSACLIENTDIR/timeStampClient.jar" -url $TSAURL -infile $FILE -outfile $SIGNATUREFILE exit 0
You can verify time stamps with a time stamp client, for example the one that comes with SignServer TSA.
java -jar timeStampClient.jar -verify -signerfile tsa1.pem -inrep server.log Token was validated successfully. Token was generated on: Fri Jul 28 18:59:00 CEST 2006 Token hash alg: SHA1 MessageDigest=1a02dc7e05d06df45d2e0f74da502513852064d5
sha1sum server.log.2006-07-28-18-58 1a02dc7e05d06df45d2e0f74da502513852064d5 *server.log.2006-07-28-18-58
If the hashes from the time stamp token (MessageDigest) matches the hash from sha1sum, the file is verified, i.e. has not been changed since the time stamp token was generated.