Table of Contents
If you decide to use ModSecurity ‘s support of Lua, here you can find some usefull tips to consider.
Short story …
At the time of writing this article, the current ModSecurity version is 2.9.0, which provides plenty of functionalities. However if you want to go deep into web application filtering or maybe you need to “understand” some current configuration, soon or late you are going to meet ModSecurity Lua script execution engine.
Why would you need to use ModSecurity in combination with Lua scripts ?
Let consider you come to the moment when you need some really weird Request Processing, which has the need of executing external script.
What are the possibilities for executing external scripts with ModSecurity ?
If you want to execute some external script to make your processing, your have the following choices
- Execute the script, by using “exec:” action inside SecRule directive. Why this could be bad ?
- Drawbacks of exec
- exec: forks a new process, which has serious performance impacts on a highly loaded site
- exec: can send only Request Infromation by using Environment variables
- You cant manipulate the whole request by using “exec:”
- Try to execute the script by using the nasty “@inspectFile” directive
- Drawbacks of @inspectFile
- @inspectFile is designed for inspecting uploaded files
- @inspectFile forks shell and then executes the script
- @inspectFile passes the matched upon variable as a script argument
- @inspectFile cannot escape bad content (which makes it unsuitable for passing request_body, or arguments , or whatever variable could contain user-defined content)
- Finally there is the SecRuleScript directive, which allows you to execute Lua script
- Drawbacks of using SecRuleScript and Lua
- You must learn Lua
- …
- You must learn Lua – That’s all
- The good part of Lua
- ModSecurity handles Lua script internally, so it doesn’t fork
- It’s really fast !
- Drawbacks of using SecRuleScript and Lua
Why use Lua and SecRuleScript/Exec
The main reason you could go this way is because of the best performance you could get by executing lua scripts.
Performance
Lua itself is a minimalistic language, which includes as few as possible libraries and predefined code. The language was designed with performance and lightweight in mind.
No forks, no-recompilation
ModSecurity compiles the lua script at configuration time. So if you want to make any changes to your already existing script, you must restart Apache in order they to take effect.
Access to all ModSecurity context variables
Inside the lua script, you get access to all of the ModSecurity variables being propagated to the phase of running your script. Even if you have manually set some custom variables inside ModSecurity, you will get them also.
Bonus ModSecurity integration
You could even set variables , which will be visible in other ModSecurity rules.
One more feature is that you are able to write logs directly in Apache error_log from your lua script.
Writing a simple Lua script, which will handle requests
Most probably you wont be familiar with writing lua scripts, so I suggest finding whatever you need in this awesome online tutorial
In our example, we will use a simple script, which will extract Request_Body and save it to a file
vim /tmp/test.lua
#!/usr/bin/lua function main() m.log(1,"Starting script execution \n") local fileHandle = assert(io.open('/tmp/lua_output.txt','a')) fileHandle:write("--- Output start ---\n") fileHandle:write(m.getvar("REQUEST_BODY","urlDecodeUni")) fileHandle:write("\n --- Output end --- \n") m.log(1,"Script execution finished\n") end
Make sure apache is able to execute the lua script:
chown apache:apache /tmp/test.lua
chmod 750 /tmp/test.lua
I have put the Lua script in /tmp for these examples, but it is not good practice to put your processing scripts in global accessible directory such as /tmp.
Usually inside Lua script, you have access to the following ModSecurity internal variables/functions:
m.getvar | Gets the value of internal variable |
m.getvars | Gets array of values (useful to use with ARGS/REQUEST_HEADERS) |
m.setvar | Set a variable which will be visible in later ModSecurity phases |
m.log | Log a message to Apache error_log |
Make ModSecurity execute /tmp/test.lua
Finally we must tell ModSecurity to execute the Lua script based on our criteria.
It is very important to fine tune pre-conditions, before executing the script, cause this is something “expensive” in comparison of ordinary ModSecurity rules. That’s why you want to make sure, Lua script is executed only for requests you really care about processing.
The following example will execute our test script for every single POST request coming to the server:
SecRule REQUEST_METHOD "POST" "phase:2, id:22, pass, exec:/tmp/test.lua"
Final considerations and pitfalls
-
ModSecurity automatically compiles the script if it ends with “.lua” suffix
Whenever you use script which ends with “.lua” ModSecurity automatically compiles the script at Apache restart time and keeps it compiled in memory.
-
Make sure your scripts are executable by the Apache user
-
Always check Apache error_log for compile time errors during the lua script compilation by apache