En webbapplikationsbrandvägg, förkortat WAF, filtrerar trafik innan den når din applikation och stoppar payloads för SQL-injektion, cross-site scripting och path traversal redan vid dörren. Enligt Verizon Data Breach Investigations Report 2026 analyserades över 31 000 säkerhetsincidenter och mer än 22 000 bekräftade dataintrång i 145 länder, och exploatering av sårbarheter var den vanligaste startpunkten för angripare, med 31 procent av alla intrång. Den här guiden visar hur du bygger en egen, gratis WAF med ModSecurity och OWASP Core Rule Set framför Nginx, steg för steg, med färdiga konfigurationsfiler du kan kopiera rakt av.

Du behöver ingen molntjänst och ingen licensavgift. Allt som beskrivs här körs på en vanlig Linux-server, och när du är klar har du ett skyddslager som fångar en stor del av de vanligaste webbattackerna innan de ens når din kod. Vi går igenom installation, konfiguration, testning, felsökning och avancerad tuning, och avslutar med en komplett konfiguration att klistra in direkt i produktion. Det här kompletterar andra lager i din säkerhetsstack, till exempel OWASP Top 10-härdning av API:er och rate limiting mot brute force, snarare än att ersätta dem.

Guiden passar dig som redan driftar Nginx framför en webbapplikation, ett API eller en backend i molnet, oavsett om servern står hos en svensk leverantör, i ett hyperscale-moln eller på egen maskinvara i en serverhall. Uppsättningen fungerar likadant oberoende av var servern fysiskt sitter, det enda kravet är att du har rotbehörighet och kan bygga från källkod. Om du hellre vill se hela resonemanget kring varför öppen källkod ofta slår färdiga molntjänster på kontroll, kostnad och transparens innan du bestämmer dig, läs vidare i nästa avsnitt innan du börjar installera.

Varför du behöver en webbapplikationsbrandvägg 2026

Hotbilden mot webbapplikationer har inte lugnat ner sig. ENISA Threat Landscape 2026 pekar på obehörig åtkomst som orsak till 39,5 procent av de registrerade incidenterna i sitt dataset, medan DDoS stod för 51,3 procent. Det sistnämnda är värt att nämna direkt, för en WAF som ModSecurity stoppar inte volymetriska DDoS-attacker. Den skyddar mot applikationslagrets attacker: injektion, skriptinjektion, protokollmissbruk och missbrukade uppladdningsfält. Verizons rapport visar också att tredjepartsinblandning i intrång har ökat till 48 procent, upp från 30 procent föregående år, vilket gör det ännu viktigare att lägga försvar direkt framför sina egna webbtjänster istället för att enbart lita på leverantörers säkerhet.

Många svenska och nordiska bolag driver fortfarande Nginx eller Apache utan något filtreringslager alls mellan internet och applikationen. Ett enda felkonfigurerat formulärfält eller ett oparchat bibliotek räcker då för att en angripare ska komma åt databasen. En WAF fångar inte allt, men den fångar de kända mönstren, den ger dig tid att patcha, och den loggar exakt vad som försöktes. Det gör den till ett billigt och effektivt komplement, inte en ersättning, till säker kodning och regelbunden patchning enligt till exempel CISA:s KEV-katalog.

Tänk på en typisk attackkedja: en angripare hittar ett formulär, testar SQL-injektion i ett sökfält, får ett felmeddelande som avslöjar databasmotorn, och fortsätter därifrån mot autentiseringstabellen. Varje steg i den kedjan lämnar ett spår i HTTP-trafiken, oavsett hur väl applikationen i övrigt är skriven. ModSecurity läser exakt den trafiken innan den når applikationen, matchar mönstren mot tusentals kända signaturer i CRS, och stoppar kedjan redan vid första försöket. Utan det lagret upptäcker du ofta inte försöket förrän loggarna granskas i efterhand, om de ens granskas alls.

Vad är ModSecurity och OWASP Core Rule Set

ModSecurity är en öppen källkods-motor som granskar HTTP-förfrågningar och svar i realtid mot ett regelverk. Motorn i sig innehåller inga regler, den bara utvärderar dem. Reglerna kommer istället från OWASP Core Rule Set (CRS), som är standardregelsamlingen för ModSecurity och riktar in sig direkt mot kategorierna i OWASP Top 10. Projektet grundades redan 2006 av säkerhetsforskaren Ofer Shezaf, och när version 3.0 av regelsamlingen släpptes i november 2016 minskade antalet falska positiva med över 90 procent jämfört med tidigare versioner, enligt presentationsmaterial som Nginx och F5 delat vid gemensamma webbinarier om ämnet.

Arkitekturen består av fyra delar som måste samverka: Nginx (webbservern själv), libmodsecurity v3 (motorn), en Nginx-koppling som laddar motorn som en dynamisk modul, och CRS (regelverket). Det är en annan uppbyggnad än i Apache, där ModSecurity historiskt kördes som en inbyggd modul. Med Nginx måste du kompilera kopplingen separat, vilket är anledningen till att den här guiden ägnar flera steg åt just byggprocessen. Projektsidan för ModSecurity listar version 3.0.17 som den senaste stabila utgåvan av motorn, släppt så sent som 29 september 2026, och CRS-projektet på GitHub låg vid samma tidpunkt på version 4.29.0 från den 17 augusti 2026.

Så är en ModSecurity-regel uppbyggd

Innan du börjar installera är det värt att förstå grundformen på en regel, eftersom du kommer skriva egna undantag senare i guiden. Varje regel börjar med SecRule, följt av vilken del av förfrågan som ska granskas, ett mönster att matcha mot, och en åtgärd om mönstret träffar.

SecRule ARGS "@detectSQLi" \
    "id:9001,\
    phase:2,\
    deny,\
    status:403,\
    msg:'Möjlig SQL-injektion upptäckt',\
    severity:'CRITICAL'"

Här granskas alla request-parametrar (ARGS) mot en inbyggd SQL-injektionsdetektor (@detectSQLi). Träffar den, nekas förfrågan med statuskod 403 och ett meddelande skrivs till loggen. phase avgör när i förfrågans livscykel regeln körs, från 1 (innan headers är helt lästa) till 5 (efter att svaret skickats). Varje regel i CRS har ett eget numeriskt id, och det är just dessa id:n du refererar till när du senare skapar undantag i steg 9. Att förstå den här grundstrukturen gör felsökning betydligt enklare, för loggmeddelandena pekar alltid tillbaka på ett specifikt id och en specifik fas.

Utöver @detectSQLi använder CRS ett flertal andra operatorer beroende på vad som ska matchas: @rx för reguljära uttryck, @detectXSS för skriptinjektion, @validateUrlEncoding för trasig URL-kodning, och @within för att kontrollera om ett värde finns i en given lista. Du behöver inte memorera alla operatorer för att följa den här guiden, men när du senare läser en loggpost och undrar varför en viss regel triggade, är det just operatorn i regeldefinitionen som avgör exakt vad som matchades.

Förutsättningar: Detta behöver du innan du börjar

Du behöver root-åtkomst till en Linux-server, helst en färsk installation, eftersom vi kompilerar Nginx från källkod med rätt modulflaggor. Räkna med 60 till 90 minuter beroende på serverns kraft, kompileringssteget är det som tar mest tid. Tabellen nedan visar exakt vilka versioner den här guiden är testad mot, hämtade direkt från respektive projekts officiella utgåvor.

KomponentVersion i denna guideKälla
OperativsystemUbuntu 24.04 LTS eller Debian 12Officiella LTS-utgåvor
Nginx (stabil gren)1.30.5nginx.org
Nginx (mainline, valfri)1.31.6nginx.org
libmodsecurity (motor)3.0.17GitHub, ModSecurity-projektet
ModSecurity-nginx-kopplingsenaste versionen från main-grenenGitHub, owasp-modsecurity
OWASP Core Rule Set4.29.0GitHub, coreruleset
Byggverktyggcc, g++, make, autoconfDistributionens paketförråd

Utöver detta behöver du minst 2 GB RAM för kompileringen (den kan svälta på mindre instanser), ett domännamn som redan pekar mot servern om du vill testa med riktig trafik, och grundläggande vana vid terminalen. Du behöver inte kunna C eller reguljära uttryck för att följa guiden, men det hjälper när du senare ska skriva egna undantagsregler.

Ta en säkerhetskopia av din nuvarande Nginx-konfiguration innan du börjar, oavsett hur självklart det låter. Om du kompilerar om Nginx från källkod på en server som redan kör trafik i produktion, planera arbetet till ett fönster med lägre belastning, och testa hela kedjan i en separat miljö först om du har möjlighet. Ett misslyckat nginx -t mitt i en pågående kompilering ska aldrig komma som en överraskning, det ska vara ett steg du redan räknat med.

Steg 1: Installera Nginx och byggverktyg

Standardpaketen för Nginx i Ubuntus och Debians förråd stöder inte dynamiska moduler på samma sätt som en källkodsbyggd version gör, så vi bygger Nginx själva. Börja med att installera beroenden och hämta källkoden för den version du valde i tabellen ovan.

sudo apt update
sudo apt install -y build-essential libpcre2-dev libpcre3-dev \
  libssl-dev zlib1g-dev libtool automake autoconf \
  libyajl-dev doxygen libcurl4-openssl-dev libgeoip-dev \
  liblmdb-dev libfuzzy-dev libxml2-dev git wget

wget https://nginx.org/download/nginx-1.30.5.tar.gz
tar -xzvf nginx-1.30.5.tar.gz
cd nginx-1.30.5

Konfigurera inte och bygg inte Nginx ännu. Vi behöver kompilera ModSecurity-motorn och kopplingsmodulen först, för att sedan peka Nginx-bygget mot dem i samma kommando. Om du redan kör Nginx i produktion från ett paket kan du behöva avinstallera det paketet innan du installerar den egenbyggda versionen, annars krockar init-scripten.

Både Ubuntu 24.04 LTS och Debian 12 fungerar lika bra som bas, skillnaden ligger mest i paketnamnen och hur länge respektive distribution garanterar säkerhetsuppdateringar. Ubuntu 24.04 LTS får stöd till april 2029, medan Debian 12 följer sin egen livscykel. Välj den distribution ditt team redan är van vid att drifta, det spelar mindre roll för själva ModSecurity-installationen än för det löpande underhållet av servern i stort.

Steg 2: Hämta och kompilera libmodsecurity 3.0.17

libmodsecurity är själva motorn som tolkar reglerna och granskar trafiken. Klona projektet, checka ut den senaste taggade utgåvan och bygg det som ett delat bibliotek. Det här steget tar längst tid på en liten server, ibland över tio minuter.

cd ~
git clone --depth 1 -b v3.0.17 https://github.com/owasp-modsecurity/ModSecurity
cd ModSecurity
git submodule init
git submodule update
./build.sh
./configure
make -j$(nproc)
sudo make install

Om ./build.sh klagar på saknade autotools, kör sudo apt install autoconf automake libtool igen och försök på nytt. Installationen läggs som standard i /usr/local/modsecurity, vilket är den sökväg vi refererar till i nästa steg.

Steg 3: Bygg ModSecurity-nginx-anslutningen som dynamisk modul

Kopplingsmodulen är den bit kod som gör att Nginx kan prata med libmodsecurity. Den byggs som en separat, dynamisk modul, vilket betyder att du kan uppdatera den utan att bygga om hela Nginx. Alternativet hade varit att kompilera in kopplingen statiskt direkt i Nginx-binären, men det tvingar dig att bygga om och starta om hela webbservern varje gång antingen Nginx eller ModSecurity uppdateras. Med den dynamiska modulen räcker det att bygga om just modulfilen och köra nginx -t följt av en omladdning, utan driftstopp för resten av trafiken.

cd ~
git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git

cd ~/nginx-1.30.5
./configure --with-compat \
  --add-dynamic-module=../ModSecurity-nginx \
  --with-http_ssl_module --with-http_v2_module

make modules

Kommandot skapar filen objs/ngx_http_modsecurity_module.so. Om bygget avbryts med ett fel om saknad pcre2.h, kontrollera att du installerade libpcre2-dev i steg 1, det är den vanligaste orsaken till avbrutna Nginx-bygg med anpassade moduler.

Steg 4: Ladda modulen i Nginx-konfigurationen

Slutför Nginx-installationen och kopiera den nybyggda modulen till rätt plats, sedan lägger du in en load_module-rad överst i nginx.conf, innan events-blocket.

sudo make install
sudo mkdir -p /etc/nginx/modules
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/

# Lägg till högst upp i /etc/nginx/nginx.conf
load_module modules/ngx_http_modsecurity_module.so;

Testa konfigurationen med sudo nginx -t innan du går vidare. Ett vanligt fel här är att modulen laddas efter events-blocket istället för före, vilket ger ett syntaxfel direkt.

Steg 5: Installera OWASP Core Rule Set 4.29.0

Nu har du en motor men inga regler att köra. Hämta CRS 4.29.0 från GitHub och byt namn på exempelkonfigurationerna så de blir aktiva. Klona alltid från det officiella repositoryt under organisationen coreruleset på GitHub, och peka mot en specifik taggad version istället för main-grenen i produktion, så att en oplanerad uppdatering aldrig sker automatiskt vid en omstart av byggprocessen.

sudo mkdir -p /etc/nginx/modsec
cd /etc/nginx/modsec

sudo git clone --depth 1 -b v4.29.0 \
  https://github.com/coreruleset/coreruleset.git crs

sudo cp crs/crs-setup.conf.example crs/crs-setup.conf
sudo cp -r ~/ModSecurity/unicode.mapping /etc/nginx/modsec/
sudo wget https://raw.githubusercontent.com/owasp-modsecurity/ModSecurity/v3.0.17/modsecurity.conf-recommended \
  -O /etc/nginx/modsec/modsecurity.conf

I modsecurity.conf ändrar du raden SecRuleEngine DetectionOnly till SecRuleEngine On när du är redo att faktiskt blockera trafik. Låt den stå på DetectionOnly under de första dagarna i produktion, så du hinner se vad som skulle ha blockerats innan du slår på full spärrning.

Steg 6: Koppla in modsecurity.conf och CRS i din site

Skapa en huvudfil som binder ihop grundkonfigurationen och regelsamlingen, och referera till den från din serverkonfiguration för den domän du vill skydda.

# /etc/nginx/modsec/main.conf
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf

# I server-blocket för din site, /etc/nginx/sites-available/dinsajt.conf
server {
    listen 443 ssl;
    server_name dinsajt.se;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Kör sudo nginx -t igen, och starta om tjänsten med sudo systemctl restart nginx om testet går igenom. Om du redan kör mTLS mellan Nginx och backend går ModSecurity att kombinera direkt med den uppsättningen, se vår guide om mTLS med Nginx för certifikatdelen.

Om servern hostar flera domäner, upprepa modsecurity-direktivet i varje enskilt server-block du vill skydda. Det går även att aktivera skyddet globalt genom att lägga modsecurity on; i http-blocket istället, men då gäller samma regeluppsättning för alla sajter på servern. Har du en blandning av en gammal WordPress-installation och ett nytt API på samma maskin är det ofta bättre att hålla konfigurationen per server-block, eftersom de två applikationerna med stor sannolikhet behöver helt olika undantag.

Steg 7: Testa att brandväggen faktiskt blockerar attacker

Innan du litar på installationen, skicka ett par klassiska attackmönster mot servern och bekräfta att CRS reagerar. Det enklaste testet är ett SQL-injektionsförsök i en query-parameter.

curl -i "https://dinsajt.se/?id=1' OR '1'='1"

Med SecRuleEngine On och standardinställningarna i CRS ska du få ett svar ungefär så här, istället för din vanliga sida:

HTTP/1.1 403 Forbidden
Server: nginx
Content-Type: text/html

<html><head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body></html>

Kör samma test med ett enkelt XSS-försök, till exempel ?search=<script>alert(1)</script>, och kontrollera att det också blockeras. Om du fortfarande får igenom trafiken, dubbelkolla att SecRuleEngine verkligen står på On och att sökvägarna i main.conf pekar rätt, det är den vanligaste orsaken till att steg 7 misslyckas.

Testa även ett path traversal-försök, till exempel curl -i "https://dinsajt.se/../../etc/passwd", och ett försök att missbruka ett filuppladdningsfält med en dubbel filändelse som skript.php.jpg. Skriv gärna ner resultatet av alla tre testerna i en liten testlogg innan du går vidare, så har du ett facit att jämföra mot nästa gång du uppdaterar CRS eller lägger till egna undantag. Om ett tidigare blockerat test plötsligt släpps igenom efter en uppdatering vet du direkt att något i regelkedjan har brutits.

Steg 8: Ställ in paranoia level och anomaly scoring

CRS arbetar med poängsättning snarare än enkel blockering per regel. Varje matchande regel adderar poäng till en förfrågan, och när summan överstiger ett tröskelvärde blockeras den. Paranoia level styr hur aggressivt reglerna letar efter avvikelser, från nivå 1 (minst brus) till nivå 4 (mest brus, flest falska positiva).

Paranoia levelTräffbildRekommenderas för
PL1Färre falska positiva, fångar de tydligaste attackernaProduktion vid första driftsättning
PL2Bredare täckning, kräver viss tuningProduktion efter några veckors granskning
PL3Fångar mer obskyra mönster, fler falska positivaApplikationer med höga krav på skydd
PL4Mest aggressiv, hög andel falska positivaTestmiljöer och hårt exponerade endpoints

Sätt nivån i crs-setup.conf genom att ändra värdet på tx.paranoia_level. Börja alltid på PL1 i produktion, oavsett hur känslig applikationen är. Att hoppa direkt till PL3 eller PL4 utan att först granska loggarna på PL1 är en av de vanligaste anledningarna till att team stänger av ModSecurity helt efter en vecka, för att de dränks i falska positiva istället för att gradvis höja nivån.

Utöver paranoia level styr två tröskelvärden när en förfrågan faktiskt blockeras: tx.inbound_anomaly_score_threshold för inkommande trafik och tx.outbound_anomaly_score_threshold för svar från applikationen. Standardvärdet är 5 för inkommande trafik på PL1, vilket betyder att en enda kritisk regelträff (vanligtvis värd 5 poäng) räcker för att blockera. Höjer du tröskeln till exempelvis 10 krävs det två separata regelträffar innan förfrågan nekas, vilket ger dig ett sätt att mjuka upp känsligheten utan att sänka hela paranoia level.

# I crs-setup.conf
SecAction \
 "id:900000,\
  phase:1,\
  pass,\
  t:none,\
  nolog,\
  setvar:tx.paranoia_level=1,\
  setvar:tx.inbound_anomaly_score_threshold=7,\
  setvar:tx.outbound_anomaly_score_threshold=5"

Steg 9: Skapa undantag för falska positiva

Generiska regler känner inte till din applikations dataformat. JSON-API:er, filuppladdning och sökfält med specialtecken triggar ofta falska larm. Lösningen är riktade undantag, inte att sänka paranoia level globalt eller stänga av motorn.

# Exempel: ta bort en specifik regel för en viss URL och parameter
SecRule REQUEST_URI "@beginsWith /api/upload" \
    "id:1000,phase:1,pass,nolog,\
    ctl:ruleRemoveById=942100"

# Alternativt: höj tröskeln för en enda parameter istället för att ta bort regeln helt
SecRuleUpdateTargetById 942100 "!ARGS:beskrivning"

Spara egna undantag i en separat fil, till exempel /etc/nginx/modsec/custom-exclusions.conf, och inkludera den efter CRS-reglerna i main.conf. Då slipper du att dina ändringar försvinner nästa gång du uppdaterar CRS till en ny version.

Innan du sparar ett nytt undantag permanent, kör om testerna från steg 7 för att bekräfta att du inte råkat öppna dörren bredare än tänkt. Ett vanligt misstag är att skriva ett undantag som tystar loggningen (nolog) istället för att faktiskt släppa igenom trafiken korrekt, vilket gör att attacker senare passerar helt spårlöst. Testa alltid både att den legitima trafiken du ville tillåta går igenom, och att ett attackförsök mot samma endpoint fortfarande blockeras.

Steg 10: Aktivera loggning, granska audit-loggen och skicka vidare till SIEM

Audit-loggen är din viktigaste källa för att förstå vad som faktiskt händer. Aktivera den i modsecurity.conf och peka den mot en egen fil.

SecAuditEngine RelevantOnly
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.log

En blockerad förfrågan ger en loggpost ungefär så här, med regel-id, matchande mönster och den poäng som utlöste blockeringen:

--abc123-A--
[27/Sep/2026:14:02:11 +0200] Vabc123 10.0.0.5 51422 10.0.0.10 443
--abc123-H--
Message: Warning. detected SQLi using libinjection
[file "REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [id "942100"]
[msg "SQL Injection Attack Detected via libinjection"]
Stopwatch2: total=142
Producer: ModSecurity for Nginx (STABLE)

I ett produktionsläge räcker det inte att bara logga lokalt. Skicka modsec_audit.log vidare till din SIEM eller loggpipeline med Filebeat, Fluentd eller motsvarande, så att blockerade attacker syns i samma dashboard som resten av din incidenthantering, inte i en isolerad fil som ingen läser förrän det redan gått snett.

Glöm inte heller loggrotation. En aktiv sajt med SecAuditEngine RelevantOnly genererar betydligt mindre loggvolym än On (som loggar allt, även godkänd trafik), men en obevakad audit-logg kan ändå växa sig stor på några veckor. Lägg in /var/log/modsec_audit.log i din vanliga logrotate-konfiguration med daglig rotation och komprimering, annars riskerar du att disken tar slut i tysthet mitt i natten, långt innan någon hinner reagera på larmet.

Vanliga fallgropar vid driftsättning av ModSecurity

De flesta misslyckade ModSecurity-driftsättningar följer samma mönster, oavsett bransch eller applikationstyp: teamet skyndar sig genom installationen, slår på full blockering direkt, och stänger av allt igen så fort första supportärendet om en blockerad kund dyker upp. Nedan är de vanligaste felstegen, i ungefär den ordning de brukar upptäckas.

  • Slå på full blockering direkt i produktion. Kör alltid SecRuleEngine DetectionOnly i minst tre till fem dagar först och granska loggarna, annars riskerar du att blockera legitima kunder utan förvarning.
  • Hoppa direkt till hög paranoia level. PL3 eller PL4 utan tuning ger en flod av falska positiva som gör att teamet till slut stänger av hela skyddet i frustration.
  • Glömma att uppdatera CRS regelbundet. Nya attackmönster täcks in i nya utgåvor, en regelsamling som står stilla i ett år missar täckning för nyare tekniker.
  • Tro att WAF ersätter säker kodning. ModSecurity fångar kända mönster i HTTP-trafiken, den förstår inte din applikations affärslogik och stoppar inte behörighetsfel eller stulna sessionstoken.
  • Tro att WAF stoppar DDoS. Som ENISA:s siffror visar är DDoS ett eget hot som kräver egna skyddslager, till exempel rate limiting eller ett CDN, inte en regelmotor på applikationslagret.
  • Skriva undantag som är för breda. Ett undantag som tar bort en regel för hela sajten istället för en specifik URL öppnar samma lucka som att inte ha regeln alls.
  • Testa bara på en enda sida. En sajt har ofta flera olika typer av endpoints (formulär, API, filuppladdning, sökfunktion), och att bara verifiera skyddet på startsidan missar de mest sårbara delarna.

Felsökning: vanliga problem och lösningar

De flesta problem med en ny ModSecurity-installation hör till en av tre kategorier: modulen laddas inte alls, modulen laddas men fungerar inte som väntat, eller modulen fungerar men blockerar fel saker. Innan du felsöker djupare, kontrollera alltid sudo nginx -t och sudo tail -f /var/log/nginx/error.log samtidigt som du skickar en testförfrågan, det avslöjar oftast vilken av de tre kategorierna du hamnat i. Tabellen nedan täcker de åtta vanligaste felen vi ser i produktionsmiljöer.

ProblemTrolig orsakLösning
Nginx startar inte efter modulinstallationload_module placerad fel i konfigurationenFlytta raden till toppen av nginx.conf, före events-blocket
./build.sh misslyckas i ModSecuritySaknade autotools-paketInstallera autoconf automake libtool och kör om
Modulen laddas men gör ingentingmodsecurity on; saknas i server-blocketLägg till direktivet i rätt server– eller location-block
Legitima förfrågningar blockeras (falska positiva)För hög paranoia level eller för generisk regelSänk paranoia level till PL1 och lägg riktade undantag per regel-id
Ingen loggning trots att SecAuditEngine är aktiveratFel filrättigheter på loggfilenKontrollera att Nginx-processens användare har skrivrättighet till loggkatalogen
Prestandaproblem, ökad svarstidHela request-body granskas på stora uppladdningarSätt SecRequestBodyLimit och undanta stora filuppladdningsendpoints
CRS-uppdatering krockar med egna undantagUndantag skrivna direkt i CRS-filernaFlytta alla anpassningar till en separat include-fil som laddas efter CRS
nginx -t ger syntaxfel efter uppgraderingNy CRS-version har bytt filnamn eller strukturLäs release notes för CRS-versionen och uppdatera include-sökvägarna

Om inget av dessa åtta stämmer på ditt problem, isolera felet genom att tillfälligt inkludera bara modsecurity.conf utan CRS, bekräfta att grundmotorn fungerar, och lägg sedan tillbaka regelfilerna en katalog i taget tills felet återkommer. Den metoden är långsammare än att gissa, men den pekar alltid ut exakt vilken fil eller regel som orsakar problemet, vilket sparar tid jämfört med att chansa sig igenom hela konfigurationen på nytt.

Avancerade tips: rate limiting, geoblockering och automatiska uppdateringar

ModSecurity gör mest nytta som ett lager bland flera, inte som ensamt skydd. Kombinera det med Nginx inbyggda limit_req för att bromsa brute-force-försök mot inloggningssidor, på samma sätt som beskrivs i vår guide om rate limiting mot brute force i Node.js. För applikationer som bara har kunder i Norden går det att lägga till geoblockering med GeoIP-modulen och neka trafik från regioner du aldrig förväntar dig legitima besökare från, vilket minskar bruset i loggarna avsevärt.

load_module modules/ngx_http_geoip2_module.so;

geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
    $geoip2_data_country_code country iso_code;
}

map $geoip2_data_country_code $allowed_country {
    default no;
    SE yes;
    NO yes;
    DK yes;
    FI yes;
}

server {
    if ($allowed_country = no) {
        return 403;
    }
}

Testa geoblockering noga innan den går i produktion, eftersom den slår hårt och blockerar all trafik från listade länder, inklusive legitima besökare som råkar använda en utländsk VPN-tjänst. Många team kör geoblockering som en separat regel före ModSecurity, snarare än inbakad i CRS, för att kunna slå av den snabbt om något går fel. Tänk också på att geoblockering är trubbigt mot riktade angripare, eftersom en angripare som vet att du blockerar utländsk trafik enkelt kan routa sina anrop via en server i ett tillåtet land. Se det som brusreducering för loggarna, inte som ett skydd mot en motiverad motståndare.

Virtual patching är ett annat användningsområde värt att känna till. Om en sårbarhet upptäcks i ett ramverk du använder men patchen ännu inte är testad i din miljö, kan du skriva en tillfällig ModSecurity-regel som blockerar det specifika anropsmönstret tills den riktiga patchen är på plats. Det köper tid utan att du behöver stressa fram en osäker uppgradering samma dag som en CVE publiceras. Ett typiskt exempel är en sårbarhet som kräver en specifik URL-sökväg eller ett specifikt parameternamn för att triggas, exempelvis en okontrollerad deserialisering i ett populärt bibliotek. Du kan då skriva en riktad regel som blockerar just den sökvägen och det parameternamnet under de dagar det tar att testa och rulla ut den officiella patchen, istället för att stänga ner hela endpointen eller chansa på att ingen hinner utnyttja luckan.

Håll CRS uppdaterat automatiskt med ett enkelt cron-jobb som hämtar senaste taggen, testar konfigurationen och bara laddar om Nginx om testet går igenom.

#!/bin/bash
# /usr/local/bin/update-crs.sh
cd /etc/nginx/modsec/crs
git fetch --tags
LATEST=$(git tag --sort=-v:refname | head -n1)
git checkout "$LATEST"
nginx -t && systemctl reload nginx || echo "CRS-uppdatering misslyckades, granska manuellt"

Schemalägg skriptet en gång i veckan, till exempel via crontab -e med 0 3 * * 1 /usr/local/bin/update-crs.sh, och läs alltid release notes innan en större versionsuppgradering, eftersom nya major-versioner ibland byter ut regel-id:n som dina egna undantag pekar på.

Komplett fungerande projekt: färdig konfiguration att kopiera

Nedan är en sammanslagen, minimal men fungerande uppsättning som binder ihop allt från stegen ovan. Klistra in filerna på rätt plats, kör nginx -t, och starta om tjänsten.

# /etc/nginx/modsec/main.conf
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecResponseBodyAccess Off
SecAuditEngine RelevantOnly
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.log

Include /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf
Include /etc/nginx/modsec/custom-exclusions.conf

# /etc/nginx/sites-available/dinsajt.conf
server {
    listen 443 ssl http2;
    server_name dinsajt.se;

    ssl_certificate     /etc/letsencrypt/live/dinsajt.se/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dinsajt.se/privkey.pem;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

    location /login {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Den här uppsättningen ger dig ModSecurity på PL1 (standardnivån i crs-setup.conf), fullständig audit-loggning, ett tomt utrymme för egna undantag, och rate limiting på inloggningsvägen. Det är en rimlig startpunkt för de flesta mindre till medelstora webbapplikationer, och du bygger vidare från den utan att behöva börja om.

Kör gärna den här uppsättningen i en separat stagingmiljö innan produktion, med samma trafikmönster som applikationen faktiskt tar emot. Många team klonar sin produktionsdatabas till en testmiljö, speglar en dags verklig trafik mot stagingservern med ett enkelt replay-verktyg, och granskar audit-loggen där innan de rullar ut samma konfiguration skarpt. Det fångar de flesta applikationsspecifika falska positiva innan de hinner påverka riktiga kunder, och gör att du kan höja paranoia level i lugn och ro utan att chansa i produktion.

Begränsningar och alternativ: Coraza, NAXSI och Cloudflare WAF

ModSecurity med CRS är inte en universallösning. Motorn granskar HTTP-trafik mot kända mönster, men den förstår inte affärslogik, den stoppar inte stulna inloggningsuppgifter, och den skyddar inte mot sårbara beroenden längre ner i din kodstack. Drift kräver löpande underhåll: regelavstämning, granskning av audit-loggen och testning innan varje uppgradering. Om du inte har resurser för det löpande arbetet passar en hanterad tjänst ofta bättre. Se WAF-lagret som en av flera dörrvakter, inte den enda, och kombinera det alltid med patchhantering, autentisering och behörighetskontroller längre in i applikationen.

Kostnadsbilden skiljer sig också åt rejält mellan alternativen. En självhostad lösning som ModSecurity kostar inget i licens, men den kostar arbetstid, både vid driftsättning och löpande. En hanterad tjänst som Cloudflare WAF har istället en löpande avgift men kräver i princip ingen egen regelunderhållstid, eftersom leverantören sköter uppdateringarna. Vilket som är billigast i praktiken beror helt på hur mycket din organisation redan betalar för den interna driften, och hur ofta ni faktiskt hinner granska loggarna på egen hand.

AlternativTypBäst för
ModSecurity + CRSSjälvhostad, öppen källkodTeam som vill ha full kontroll och redan driftar egen infrastruktur
Coraza WAFSjälvhostad, öppen källkod (Go)Molnnativa arkitekturer och Go-baserade tjänster som vill ha ModSecurity-kompatibla regler
NAXSISjälvhostad, Nginx-specifikEnklare Nginx-installationer som vill ha en lättviktig lösning utan CRS komplexitet
Cloudflare WAFHanterad, molnbaseradTeam utan tid för regelunderhåll, som redan kör trafik genom Cloudflare

Ingen av dessa gör en Kubernetes-driftsättning säker på egen hand heller. Om din applikation körs i kluster, komplettera med runtime-skydd som beskrivs i vår guide om Kubernetes-säkerhet, så att du har försvar både på applikationslagret och i själva orkestreringen.

Vanliga frågor

Är ModSecurity gratis att använda i produktion?
Ja. Både libmodsecurity och OWASP Core Rule Set är öppen källkod utan licensavgift, oavsett hur mycket trafik du kör genom dem.

Sänker ModSecurity prestandan mätbart?
Kostnaden varierar kraftigt med trafikvolym, paranoia level och om hela request-body granskas. En enskild siffra går inte att ge rättvist, men att börja på PL1 och undanta stora uppladdningsendpoints håller overheaden hanterbar för de flesta applikationer. Om du märker en tydlig ökning i svarstid efter installationen, kontrollera först SecRequestBodyLimit och om SecResponseBodyAccess är påslaget i onödan, det är de två inställningarna som brukar ge störst mätbar påverkan.

Fungerar guiden med Apache istället för Nginx?
Grundprinciperna med CRS är desamma, men Apache-integrationen sker via en annan modul (mod_security2) och andra konfigurationsdirektiv. Kommandona i den här guiden gäller specifikt Nginx-kopplingen.

Hur ofta bör jag uppdatera OWASP Core Rule Set?
Kontrollera för nya utgåvor minst en gång i månaden, och skriv gärna ett automatiskt kontrollskript som steget om avancerade tips visar. Nya versioner täcker in nyare attackmönster som saknas i äldre regelsamlingar.

Kan ModSecurity ersätta ett CDN med inbyggt WAF-skydd?
Nej, de löser delvis olika problem. Ett CDN-baserat WAF filtrerar trafik redan vid nätverkskanten och hjälper även mot DDoS, medan en självhostad ModSecurity-installation ger dig full kontroll över reglerna men inget skydd mot volymetriska attacker.

Vad gör jag om en kritisk kund blockeras av misstag?
Sätt tillfälligt SecRuleEngine till DetectionOnly för att släppa igenom trafiken direkt, identifiera regel-id:t i audit-loggen, och skriv ett riktat undantag innan du slår på full blockering igen.

Behöver jag ModSecurity om jag redan följer OWASP Top 10 i min egen kod?
Säker kodning minskar risken men eliminerar den inte helt, särskilt inte i tredjepartsbibliotek du inte kontrollerar. En WAF fångar dessutom nya sårbarheter i beroenden innan du hunnit patcha, vilket gör den till ett värdefullt komplement snarare än en överflödig dubblering.

Går det att köra ModSecurity i en Docker-container?
Ja. Bygg samma steg som i den här guiden i en Dockerfile baserad på en Debian- eller Ubuntu-image, eller använd en färdig image som redan innehåller Nginx, libmodsecurity och kopplingsmodulen förkompilerade. Principen med main.conf, CRS och egna undantag är identisk oavsett om Nginx körs bare-metal eller i en container.

Kan jag testa reglerna automatiskt i en CI-pipeline innan driftsättning?
Ja, CRS-projektet levererar ett eget testramverk (ftw, “framework for testing WAFs”) som skickar kända attack- och legitima-payloads mot en testinstans och rapporterar om regeln reagerar som förväntat. Att köra det testet i CI innan varje uppdatering av dina egna undantag minskar risken för att en ändring öppnar ett hål du inte upptäcker förrän i produktion.