Skip to content

ModSecurity

The ModSecurity module allows OpenLiteSpeed to use common ModSecurity rules, like OWASP or Comodo, to improve server security. For this guide, we assume you already have a working installation of OpenLiteSpeed 1.4.29 or greater.

If you install OpenLiteSpeed through packages, such as yum or apt-get, the ModSecurity module may have been included in your package already. Look in /usr/local/lsws/modules/ for the mod_security.so file. If it exists, you can skip the ModSecurity module installation step.

If your OpenLiteSpeed server was installed from a Cloud Image, ols1clk, or the LiteSpeed repo, and you want to use OWASP, the following script will do the job and save you time:

bash <( curl -k https://raw.githubusercontent.com/litespeedtech/ols1clk/master/ols1clk.sh ) --owasp-enable

Add the ModSecurity Module

Choose the module installation method below that is the same as your OLS installation method.

Method 1. Install from Repository

Run the following command:

apt-get install ols-modsecurity -y
yum install ols-modsecurity -y

Method 2. Download from Binary

Visit the download page and download the binary file. Untar the file with the following command:

tar -zxf openlitespeed-x.x.x.tgz

Copy openlitespeed/modules/mod_security.so to your OpenLiteSpeed folder in /usr/local/lsws/modules.

Method 3. Build from source

Enter the ModSecurity module directory and run the make command, like so:

cd /openlitespeed_download/src/modules/modsecurity-ls
make -f Makefile.f

This command provides the latest version supported by OpenLiteSpeed. Do not use other versions of ModSecurity as they may not be supported.

All dependencies will be automatically handled in this step. If you encounter any problems at this stage, you can build the module manually by following the instructions here.

Copy the compiled module to the modules directory, like so:

cp mod_security.so /usr/local/lsws/modules

Enable the Module

You can set up the ModSecurity module through the OLS WebAdmin Console, if you like.

If you prefer the command line, you can use that instead. Edit OpenliteSpeed's httpd_config.conf configuration file directly from the command line, and append the ModSecurity configuration and rules . You may combine rules, or list them out over multiple lines.

For example, a simple rule which would deny access to phpinfo.php might look something like this:

module mod_security {
modsecurity  on
modsecurity_rules `
SecRuleEngine On
SecRule REQUEST_URI "@pm phpinfo.php" "phase:1,id:'10',log,deny,status:403"
`
modsecurity_rules_file          /rule/file/path
  ls_enabled              1
 }

Here's what each option does:

  • modsecurity on|off: Enables or disables ModSecurity. Usually it is set to on.
  • modsecurity_rules: Use backticks (`) at the beginning and the end of each rule, to enclose the ruleset. You can include multiple rules but keep in mind that ModSecurity rules load in order. Make sure you sort the configuration files accordingly.
  • modsecurity_rules_file: Specifies the file path to the rules. This file must exist and be correct (see below) if you define this option.
  • ls_enabled 1: Used to have OpenLiteSpeed activate the ModSecurity module. If using the user interface, this is not specified with the other rules but as described below.

Testing

You can test the example rule above following these steps:

  • Before adding the definition, verify that https://example.com/phpinfo.php works and delivers a phpinfo page as expected in your browser.
  • Add the sample rule above
  • In the WebAdmin Console, set the Enable option On to enable the ModSecurity plugin
  • Restart OpenLiteSpeed
  • Retry the URI in your browser and you should see a 403 message on the screen.

Again, this is just a test sample to verify that ModSecurity is working. You may want to remove this particular rule before going into production.

OWASP

The OLS ModSecurity engine uses the ModSecurity 3.x+ only and doesn't support the ModSecurity 2.x rule set.

For OWASP, please make sure to use ModSecurity Core Rule Set (CRS) Version 3+.

Enabling OWASP ModSec rule set by script

bash <( curl -k https://raw.githubusercontent.com/litespeedtech/ols1clk/master/ols1clk.sh ) --owasp-enable

Enabling OWASP ModSec rule set on OLS Manually

Download OWASP 3+ rule set:

mkdir -p  /usr/local/lsws/conf/owasp
cd /usr/local/lsws/conf/owasp/
wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v4.2.0.zip
unzip -qq v4.2.0.zip
rm -f v4.2.0.zip
mv coreruleset-* owasp-modsecurity-crs
cd owasp-modsecurity-crs

Rename the sample crs-setup.conf.example to crs-setup.conf:

mv crs-setup.conf.example crs-setup.conf

Rename some sample rules under the rules/ directory:

cd rules
mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
cd ..

Create an include configuration file:

echo "include modsecurity.conf
include owasp-modsecurity-crs/crs-setup.conf
include owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
include owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
include owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
include owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
include owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
include owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
include owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
include owasp-modsecurity-crs/rules/REQUEST-922-MULTIPART-ATTACK.conf
include owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
include owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
include owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
include owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
include owasp-modsecurity-crs/rules/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf
include owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
include owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
include owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
include owasp-modsecurity-crs/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
include owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
include owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
include owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
include owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
include owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
include owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
include owasp-modsecurity-crs/rules/RESPONSE-955-WEB-SHELLS.conf
include owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
include owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
include owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf">modsec_includes.conf

Configure OLS to use the OWASP Master ModSecurity rule file you just created:

module mod_security {
modsecurity  on
modsecurity_rules `
SecRuleEngine On
`
modsecurity_rules_file         /usr/local/lsws/conf/owasp/modsec_includes.conf
}

Save and restart OLS.

Testing OWASP with OLS

You can quickly test how your system responds to an XSS attack by appending ?user=<script>alert(123)</script> to your URL, as shown below. If the ModSecurity rule works, then we would expect a 403 status return.

curl 'http://www.example.com/?q="><script>alert(123)</script>'

Error log:

[Module:mod_security] ModSecurity: Warning. detected XSS using libinjection. [file "/usr/local/lsws/conf/owasp/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "38"] [id "941100"]

Here's another example of SQL injection to test:

curl "http://www.example.com/?q='1 OR 1=1"

Common Questions

What if I get a false positive error?

Please update to the latest Core Rule Set version, see Release Tags. Or raise an issue on the coreruleset official repo

How do I comment out a rule?

We'll use rule ID 941100 as an example:

Comment out the whole id 941100 from the config file (/usr/local/lsws/conf/owasp/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf), or add SecRuleRemoveById 941100 to the /usr/local/lsws/conf/owasp/owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf file to exclude the ID.

How do I comment out rules for a certain domain?

Since OpenLiteSpeed does not support SecRuleRemoveById in the .htaccess file, you can manually set rule conditions to exclude certain rules for specific domains or files.

Edit the /usr/local/lsws/conf/owasp/owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf file.

Comment out a rule for a domain (example.com):

SecRule SERVER_NAME "www\.example\.com$"
    "id:1234, phase:1, nolog,\
    ctl:ruleRemoveById=941100, \
    ctl:ruleRemoveById=941160"

Comment out a rule for a file (index.php):

SecRule REQUEST_URI "@beginsWith /index.php" \ 
    "id:1234, phase:1, nolog,\ 
    ctl:ruleRemoveById=941100, \ 
    ctl:ruleRemoveById=941160"

Check the OWASP Official Guide for more options and examples

Comodo

All commonly used Comodo rule sets, including those named LiteSpeed,Apache,Nginx or IIS are ModSecurity 2.x rule sets. None of these rule sets will match the OpenLiteSpeed ModSecurity 3.x engine, so they should not be used. 

To use the Comodo rules, choose the Nginx/ModSec_3.0 release. Don't worry that it specifically mentions "Nginx." It is a simple ModSec_3.0 set, and it works with the OpenLiteSpeed ModSecurity engine. See the screenshot of the Comodo WAF download portal below to see how to choose the Nginx/ModSec_3.0 source.

Note

You should use only one set at a time (either Comodo or CRS) to avoid conflicts.

Enabling the COMODO ModSec_3.0 rule set on OLS

Log in to your account at https://waf.comodo.com/ and download the full Nginx/ModSec_3.0 rule set.  The file will be a .tgz file and will include the version number, like so: cwaf_rules_nginx_3-1.223.tgz

Use an FTP tool to transfer this file to your server, and place it in the /usr/local/lsws/modsec/comodo/ directory. Create the directory if it doesn't exist.

comodo# pwd
/usr/local/lsws/modsec/comodo
comodo# ll
total 308
-rw-r--r-- 1 root root 312040 Oct 23 15:04 cwaf_rules_nginx_3-1.223.tgz
comodo# tar -zxvf cwaf_rules_nginx_3-1.223.tgz
comodo# mv rules.conf.main rules.conf

Comodo has already provided an example master rules.conf.main file to include all rules. Rename it to rules.conf. Then you can configure this Master COMODO rule file instead of manually including all rule files one by one.

comodo# cat rules.conf
Include 00_Init_Initialization.conf
Include 01_Init_AppsInitialization.conf
Include 02_Global_Generic.conf
Include 03_Global_Agents.conf
Include 04_Global_Domains.conf
Include 05_Global_Incoming.conf
Include 06_Global_Backdoor.conf
Include 07_XSS_XSS.conf
Include 08_Global_Other.conf
Include 09_Bruteforce_Bruteforce.conf
Include 10_HTTP_HTTP.conf
Include 11_HTTP_HTTPDoS.conf
Include 12_HTTP_Protocol.conf
Include 13_HTTP_Request.conf
Include 14_Outgoing_FilterGen.conf
Include 15_Outgoing_FilterASP.conf
Include 16_Outgoing_FilterPHP.conf
Include 17_Outgoing_FilterSQL.conf
Include 18_Outgoing_FilterOther.conf
Include 19_Outgoing_FilterInFrame.conf
Include 20_Outgoing_FiltersEnd.conf
Include 21_PHP_PHPGen.conf
Include 22_SQL_SQLi.conf
Include 23_ROR_RORGen.conf
Include 24_Apps_Joomla.conf
Include 25_Apps_JComponent.conf
Include 26_Apps_WordPress.conf
Include 27_Apps_WPPlugin.conf
Include 28_Apps_WHMCS.conf
Include 29_Apps_Drupal.conf
Include 30_Apps_OtherApps.conf

Configure OLS to use Comodo Master ModSecurity rule file:

module mod_security {
modsecurity  on
modsecurity_rules `
SecRuleEngine On
`
modsecurity_rules_file /usr/local/lsws/modsec/comodo/rules.conf }

Save the change and restart OLS.

Troubleshooting

You can turn on detailed auditing, including debug logging, if the rules are not working as expected:

module mod_security {
modsecurity  on
modsecurity_rules `
SecRuleEngine On
SecDebugLog /tmp/auditlog-debug.txt
SecDebugLogLevel 9
SecAuditLogParts AB
SecAuditEngine On
SecAuditLog /tmp/auditlog.txt
SecAuditLogType Serial
SecAuditLogStorageDir /tmp/
SecRule REQUEST_URI "@pm phpinfo.php" "phase:1,id:'10',log,deny,status:403"
` }

An attempt to access the phpinfo.php file will result in a large number of updates to the /tmp/auditlog-debug.txt file and a smaller number to the /tmp/auditlog.txt file.  We recommend that you not enable debug logging unless necessary in production. Additionally, we suggest that you only enable more limited audit logging when there are techniques in place to prune the logs.

Notes

  • The last occurrence of mod_security on|off will be the one that takes effect.
  • We recommend that you use backticks ` (as shown above) to surround mod_security_rules rules to avoid any possible issues with single and double quotes used in the rules themselves.
  • mod_security_rulesmod_security_rules_file, and mod_security_rules_remote can mixed and used multiple times each if desired with all rules being combined.

Using ModSecurity inspectFile in OpenLiteSpeed

The @inspectFile operator runs a Lua program or external program of your own (which can be a script) to examine variables in the target list. Most commonly it is used to examine uploaded files and that is the only way it has been tested for this article. It's not a feature that is commonly used, as it is not provided as part of the functionality of the RuleSet providers (Comodo or OWASP). But if you wish to examine data transferred before it actually is used by the web server, this is the most powerful way to do it.

Using @inspectFile can be somewhat problematic as your script might not be executed, or if it is, it may not do what you expect. The purpose of this section is to give you some lessons, hard won, in understanding the use of @inspectFile.

Many of the details of this article are specific to OpenLiteSpeed using the SpiderLabs ModSecurity library. If you are using LiteSpeed Enterprise the issues are fewer and they are denoted below.

Basic Setup

The steps for using @inspectFile

  • Write a rule using inspectFile
  • Write a script which does the inspectFile and outputs the values expected
  • Set up a test for your rule/script

Write a Rule Using inspectFile

Most of the time, when writing an inspectFile rule, you will need to make it chained. All rules require IDs and it's easiest to add the ID as part of the preceding rule in the chain. Also running a script can add overhead and you can use the preceding rule to limit the frequency that the script is run.

An example rule might be:

SecRule ARGS:b "@streq inspectfail" "chain,phase:request,log,deny,id:159" 
SecRule FILES_TMPNAMES "@inspectFile /usr/local/lsws/cxscgi.sh"

This gives us a two step rule:

  • Examine the arguments of the request. The rule will only run the script if there is an argument b with the exact value inspectfail
  • Run the script /usr/local/lsws/cxscgi.sh to examine the data passed up. Our test below will assume it's a file upload. FILES_TMPNAMES is the file name uploaded and is the recommended value.

This rule can be inserted into a rules include file, or as module parameters when the ModSecurity module is loaded (as in OpenLiteSpeed), or through Rules Definition in the WAF (as in Enterprise).

Additional SpiderLabs (OpenLiteSpeed) Requirements

Note

This section is not relevant for LiteSpeed Enterprise.

According to the reference documentation , using FILES_TMPNAMES requires the SecTmpSaveUploadedFiles directive to be set to On, or the SecUploadKeepFiles directive to be set to RelevantOnly. In our testing we strongly recommend the following be added to your rules to make writing and debugging rules easier:

SecRequestBodyAccess On
SecUploadKeepFiles On
SecUploadDir /tmp/modsec
SecDebugLog /tmp/auditlog-debug.txt
SecDebugLogLevel 9

You must precreate the /tmp/modsec directory and the LiteSpeed user must have read/write access to this directory.

SecDebugLog and SecDebugLogLevel are debugging tools that will be discussed below.

Write a Script to do the inspectFile

The really important thing to remember about the user script is that the first character written to standard output is used as the return code for the operation. 1 is failure and 0 is success. The following is a sample script which may help you to debug the facility.

```

!/bin/bash

echo "1 FAIL!"; echo (date) >> /usr/local/lsws/cxscgi.log args=("@") ELEMENTS={#args[@]} echo " There are {ELEMENTS} command line parameters" >> /usr/local/lsws/cxscgi.log for (( i=0;i<ELEMENTS;i++)); do echo " Param[ELEMENTS;i++)); do echo " Param[ELEMENTS;i++)); do echo " Param[ELEMENTS;i++)); do echo " Param[]: {args[]}" >> /usr/local/lsws/cxscgi.log done echo " Script run and it returned it failed" >> /usr/local/lsws/cxscgi.log exit 1

You must have the execute bit on for this script for the LiteSpeed user.

!!! note "Notes"
      - `#!/bin/bash` indicates the bash interpreter is used.
      - `echo "1 FAIL!"` causes the engine to interpret this as a failure. In initial testing you're going to want to do that. Eventually, you'll want your script to do things like run a virus scan or something similar
      - The remaining lines are used to output the command line parameters and the date and time to a log file (written in this example to `/usr/local/lsws/cxscgi.log`). In this case the LiteSpeed user was given read/write access to the `/usr/local/lsws` directory, but if you don't want to do that, you may choose to write your log to a different location, where the LiteSpeed user has access. Having this information available will help in debugging as it will let you know that the script was run and what parameters were passed to it.

It is highly recommended that you simply run this script manually as the LiteSpeed user if you can, and you should see it write to the output log file with the date/time and any command line parameters you pass to it. This will verify that it is executable and that it can write to the directory where you expect to log to.

### Set up a Test for Your Rule/Script

It is recommended that you use curl to test your rule. You may wish to write a simple script so that the request is repeatable. In the example above, we wrote a simple script with one line in it.
curl --http1.1 -F file=@/usr/local/lsws/cxscgi.sh 'http://127.0.0.1:8088/phpinfo.php?b=inspectfail' -v -s > /usr/local/lsws/403error-1.txt 2>&1 ```

This script will set the argument b to the correct value to hit the first part of the chain, upload the script file and write the output to the file /usr/local/lsws/403error-1.txt.

Debugging Tips

  • As with any debugging, it is recommended that you isolate the condition as much as possible. A single rule set should be used to test inspectFile. You can add it to your larger rule sets once it has been validated.
  • You should check the LiteSpeed error.log file and verify that there were no errors in loading the rules.
  • There are a number of suggestions already mentioned in the example above. For OpenLiteSpeed, a debugging log will be written in /tmp/auditlog-debug.txt and it will contain details which will show you any issues that the SpiderLabs library may be having in executing the rule or your script. This log should be examined in detail.
  • We recommend that initially, you create a script that writes to a log file. You should verify that this log file is updated.
  • Once you have the script executed and the rule is causing the transaction to fail correctly (as you can see in the 403error-1.txt file in the example above), you can change the rule to return success and validate that the rule no longer causes a failure. At this point you can modify your script to perform the examination function you wish to perform.

Last update: July 27, 2024