Site Setup and Concrete CMS Hardening
Please spend the time to make sure that any code you write for your site is also secure - Developers check out https://documentation.concretecms.org/developers/security/overview
Here is a checklist to help you when you are ready to go live with your site. Most of these recommendations are security hardening recommendations but there are also a few “common sense” pointers included. Please note that any suggestions on how to configure your web server itself are not comprehensive. We are the Concrete CMS folks, not your web server folk! We recommend that you follow the best practices for whatever web server you are using
- Make sure you are using TLS 1.2 or above.
- Redirect http to https.
- Set HSTS (if you have subdomains that don't support HTTPS you should omit the "includeSubDomains" directive since HSTS headers will force it, but you should still have an HSTS header. )
- Make sure there is a CDN in front of the site and that caching has been optimized.
- Add the site to any monitoring tools you have.
- Ensure that the xframe security option is configured properly in your webserver (ex Nginx).
- Set your webserver (ex Nginx) policy to prevent execution of code from /application/files directory. In other words, configure your webserver to only statically serve files from /application/files and any other public storage locations you have configured in Concrete.
- Set your webserver (ex Nginx) policy to prevent the execution of any file named index.php except from the webroot.
- Set your webserver (ex Nginx) policy to prevent execution of any php files except those named index.php.
- Ensure that your webserver (ex: nginx, apache) directory browsing is disabled
- If you are going to be sending email from the website, configure a SMTP provider and make sure that the site can send email from the domain that will be pointed at it. (example no-reply@ [your site domain])
- If applicable, enable rate limiting.
- Concrete Logs go into the database by default. If you want them to also be written to another location off of the server you are using to host your Website, you can set that up.
- Configure what you want to log.
- Do you want form data to be logged?
- By default both email metadata AND the email body will both be included in the logs. You can configure whether you want just email metadata to be logged by going to /dashboard/system/mail/logging
- Make sure that the webroot does not contain test files, backups, or other non-production code.
- Ensure that you have a Web Application Firewall (WAF) in place.
- If you are running a Concrete CMS version below 9.0.0, set the Cross-Origin-Resource-Policy header configuration to either same-origin or same-site
- if unsure which to apply, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)
- For Nginx: In the location block - add_header Cross-Origin-Resource-Policy "same-origin";
- For Apache: In .htaccess or equivalent - Header set Cross-Origin-Resource-Policy "same-origin".
- if you are concerned about your administrators uploading files, configure your webserver to serve images as images and everything else as plain text for the uploaded files directory which is
/application/files/*Example, for html files, we recommend you either configure your webserver to serve .html files as text/plain (see documentation for whatever web server you are using) or alternatively configure your concrete site to prevent html upload as described in the CMS section below.
- Concrete CMS stores the cache in the uploaded files directory. If this concerns you, disable access to
- Make sure that you are downloading the latest supported version of Concrete CMS in one of the official ways available here: https://www.concretecms.org/download
- That includes the zip file downloaded from the website, composer-based installs which would update completely differently, or updating through the dashboard.
- Here are instructions https://documentation.concretecms.org/developers/introduction/installing-concrete-cms
- Use the latest version of PHP supported by your version of Concrete CMS.
- When your site was developed, were the recommendations to protect your Web application followed? If not, have whoever developed your site review https://documentation.concretecms.org/developers/security/overview
In the Concrete Dashboard:
- Turn off development debug output for production sites. /dashboard/system/environment/debug
- Set Canonical URLs to the proper url. (If you don’t your site could have “password reset poisoning” wherein a hacker could redirect password reset requests to a server of their choosing.)
- Make sure that redirects go to the Canonical URL.
- Set site to render only on TLS/HTTPS.
- Decide if your uploaded files should be publicly accessible. If not, configure a private storage location by setting correct permissions on a directory outside the site root. Use Concrete permissions to control who you want to be able to access the files. Do not rely on the file passwords to protect the file; file passwords are stored in plain text.The new directory must be specified using the Concrete CMS dashboard settings for Storage Locations. From Dashboard > System and Settings > Files > File Storage Locations, add a new storage location named "Protected Storage", make it a "Local" type, and give it a directory location outside the web root. Also, configure it to be the default location for all newly uploaded files.
- Review the default password security settings and make changes if necessary. /dashboard/system/registration/password_requirements)
- Make sure the admin pw is long and complex.
- Consider whether you'd like to lock down visitors to a list of known IP addresses. If so, configure the Concrete IP Ban list or IP allow list.
- Set up specific user groups for the types of roles your administrators and editors will have and assign users to those groups to grant access. - Configure groups with the minimum permissions necessary wherever possible. For example, content editors should not be set up as administrators with the ability to access all of the Dashboard.
- Set up Viewing permissions and Edit Access to meet your security requirements. dashboard/system/permissions/site
- Set allowable file types to be the minimum necessary (dashboard/system/files/filetypes)
- Verify the Automated Logout Settings meet your security requirements: dashboard/system/registration/automated_logout Set the max number of log in attempts you want to allow for your site.
- Suggested Account Options (dashboard/system/registration/open):
- Only allow admins to create accounts from the dashboard. Select Off under “Allow visitors to sign up as site members”
- Configure users and admin to log in with email address and not with username (select “ask for email & password” under Login Form”
- Make sure Recaptcha is set up. Select “CAPTCHA required” under Registration Form and also go to dashboard/system/permissions/captcha
- if you are concerned about your administrators uploading certain types of files:
- you can configure your concrete site to prevent html upload the same way you can configure your Concrete site to reject any file type for upload by excluding it from the allowed file types here: https://documentation.concretecms.org/user-guide/editors-reference/dashboard/system-and-maintenance/files/allowed-file-type
- If you want to prevent administrators from customizing the list of allowed file extensions, you can create a file named concrete.php in the application/config directory specifying which file types you would like to allow.
If the X frame security option is being handled by a webserver such as Nginx, then the X frame security option needs to be disabled in Concrete CMS (run command: ./vendor/bin/concrete5 c5:config set -g concrete.security.misc.x_frame_options null )
Make sure your Concrete CMS version number is hidden. Make sure the generator tag is set to hide the Concrete CMS version number. (This is hard coded into Concrete CMS versions 8.5.6+ and 9.0+)
- Make sure the Concrete CMS version number doesn’t show in your website’s header response.
- You can check this by looking at the network section in your browser’s developer tools and searching for CCM_IMAGE_PATH.
- When you upgrade through the dashboard, you get a new core downloaded into the /updates directory and then Concrete configures itself to reference the core from there. This is why we recommend Composer installs and deployment which swaps out the /concrete directory with new versions and never downloads them to the /updates directory.
- If you didn’t use composer and have to remove the Concrete version number from the CCM_IMAGE_PATH variable in the header response:
- rename folder to remove version number for for CCM_IMAGE_PATH
- Reference your new folder name instead of the old one in this file: application\config\update.php
- Make sure the Concrete CMS version number doesn’t show in your website’s header response.
Add your website and Concrete CMS to your company’s system list and Role Based Access controls. Review requests for changes in concrete permissions and access levels carefully using your companies procedures
Points to Ponder
- Consider integrating Concrete CMS login into your company’s SSO. Concrete supports Google authentication by default.
- Consider automatically deactivating users when they have not logged in for a configurable number of days or after a configurable number of failed login attempts. dashboard/system/registration/deactivation
Always Test Before You Go Live!
- Make sure your site functions the way you want it to! This is true especially when upgrading your Concrete CMS version!
- Test your production load prior to launch using your favorite load testing tool!
- Run a web application scanner if you have one.
- Make sure there are no console errors in the browsers you are intending to support (Such as Firefox, Chrome, Safari, Edge, etc.)
- Make sure any default “from” email is “no-reply@ [ site domain]“ otherwise whoever was testing this functionality is going to get spammed.
- Does your website have a favicon?
- Have you put tracking codes in place?
- Check both CNAME and A record - site should respond to both and redirect to one.