Lighttpd: mod_security via mod_magnet

In most large enterprises there is a requirement to comply with various standards. The hot potato in the Ecommerce space at the moment (and has been for a few years!) is PCI-DSS.

At $WORK we have to comply with PCI-DSS with the full audit and similar occurring due to the number of transactions we perform. Recently we’ve deployed lighttpd for one of our platforms, which has caused an issue for our Information Security Officers and Compliance staff.

PCI-DSS 6.6 requires EITHER a Code review to be preformed, which whilst this may seem to be an easy task, when you’re talking about complex enterprise applications following a very……… agile development process it’s not always an option. The other option is to use a WAF (Web Application Firewall). Now there are multiple products available that sit upstream and perform this task. There is however an issue if you use SSL for your traffic. Most WAF will not do the SSL decryption / reencryption between the client and server (effectively becoming a Man in the Middle). There are however a few products which do this, F5 networks’ ASM being one that springs to mind. Unfortunately this isn’t always an option due to licensing fees and similar. An alternative is to run a WAF on the server its self. A common module for this is Mod_Security for Apache. Unfortunately, a similar module does not exist for Lighttpd.

In response to $WORKs requirement for this I’ve used mod_magnet to run a small lua script to emulate the functionality of mod_security (to an extent at least!). Please note that mod_magent is blocking, so will cause any requests to be blocked until the mod_magnet script has completed, so be very careful with the script, and ensure that it’s not causing any lag in a test environment, prior to deploying into live!

Below is a copy of an early version of the script (most of the mod_security rules that we have are specific to work, so are not being included for various reasons), however I’ll post updates to this soon.


-- mod_security alike in LUA for mod_magnet
LOG = true
DROP = true

function returnError(e)
        if (lighty.env["request.remote-ip"]) then
                remoteip = lighty.env["request.remote-ip"]
                remoteip = "UNKNOWN_IP"
        if (LOG == true) then
                print ( remoteip .. " blocked due to ".. e .. " --- " ..
                                lighty.env["request.method"] .. " " .. lighty.request["Host"] .. " " .. lighty.env["request.uri"])
        if (DROP == true) then
                return 405

function SQLInjection(content)
        if (string.find(content, "UNION")) then
                return returnError('UNION in uri')

function UserAgent(UA)
        UA = UA:gsub("%a", string.lower, 1)
        if (string.find(UA, "libwhisker")) then
                return returnError('UserAgent - libwhisker')
        elseif (string.find(UA, "paros")) then
                return returnError('UserAgent - paros')
        elseif (string.find(UA, "wget")) then
                return returnError('UserAgent - wget')
        elseif (string.find(UA, "libwww")) then
                return returnError('UserAgent - libwww')
        elseif (string.find(UA, "perl")) then
                return returnError('UserAgent - perl')
        elseif (string.find(UA, "java")) then
                return returnError('UserAgent - java')

-- URI = lighty.env["request.uri"]
-- POST = lighty.request
if ( SQLInjection(lighty.env["request.uri"]) == 405) then
       ret = 405
if ( UserAgent(lighty.request["User-Agent"]) == 405) then
       ret = 405
return ret

The following needs to be added to lighttpd.conf to attach this LUA script via mod magnet

server.modules += ( "mod_magnet" )
magnet.attract-physical-path-to = ( "/etc/lighttpd/mod_sec.lua")

*Update – 23 Aug 09* Updated to return code even if one test passes*

Comments or suggestions are appreciated!

8 replies on “Lighttpd: mod_security via mod_magnet”

I think you made a little mistake at the end of your script. The value of ret is overwritten the second time thus the request is *not* dropped if the user agent check is succesful.
You need to check ret after the function call to SQLInjection.

Hi Joschi

Thanks for this, I'll get this example updated some time this week, the script in use at $WORK is much more complex than this example and has this fix already in place.

Thank you very much! Now I can feel better about making the leap from Apache. Much appreciated… Patiently awaiting the updated example 🙂

@Alex : This is just an example, which as you say has flaws such as the case changes, and infact if you encode the string the same result will occur. Its by no means 'production' ready, its was more of an example for others to use and expand on 🙂

Do you find that the more complex and full-blown version of this script you use at $WORK gives you acceptable performance? I like the possibilities that mod_magnet offers but the fact that it is completely blocking makes me a little bit nervous about writing long and complicated security scripts for it.

I’ve since moved from that $WORK, but as such we didn’t notice any major issues with performance. My advise is always however to test, test and test again!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.