MOD_security: Firewall voor je webserver
- May 5, 2015
- 0
Vroeg of laat wordt elke webserver het slachtoffer van een inbraakpoging. Via een botnet test men bekende kwetsbaarheden één voor één uit, in de hoop dat jouw server kwetsbare software bevat. Dergelijke aanvallen hou je niet tegen met iptables: daarvoor heb je een Web Application Firewall nodig. mod_security is het bekendste voorbeeld van zo’n firewall.
Iptables werkt op een redelijk laag niveau in de netwerkstack. Je kunt wel specifieke rules definiëren om binnenkomend of uitgaand netwerkverkeer te blokkeren dan wel toe te laten, maar echt intelligent zijn die regels meestal niet. Met parameters zoals source-IP-adres en -poort of TCP-flags kun je nu niet bepaald een legitieme HTTP-request onderscheiden van een potentieel gevaarlijke request. Dat is enkel mogelijk als de firewall de volledige HTTP-communicatie interpreteert. Men spreekt dan van een Web Application Firewall of WAF, in tegenstelling tot een general purpose network firewall. Een WAF is dus specifiek ontwikkeld om al het HTTP-netwerkverkeer te monitoren en -indien nodig- in te grijpen. De bekendste open source WAF voor Linux is mod_security, een extra Apache-module. Maar mod_security is ook beschikbaar voor Nginx en -we vermelden het even voor de volledigheid- IIS.
Firewall rules
Net zoals iptables filtert mod_security inkomend en uitgaand verkeer: in dit geval zijn dat http-requests en http-responses. Een firewall rule kan tot verschillende acties leiden: de request of response toelaten, blokkeren, redirecten, loggen, etcetera. Uiteraard wordt een firewall pas effectief zodra je rules geconfigureerd hebt. Het is wel een pak lastiger om zelf rules te schrijven voor mod_security dan voor iptables. mod_security werkt immers met het high-level HTTP-protocol in plaats van het low-level IP-protocol en dat vertaalt zich in extra complexiteit. De rule syntax is weliswaar goed gedocumenteerd (zie /usr/share/doc/libapache2-modsecurity/doc), maar je zal toch meer dan een snelle blik op de manpage nodig hebben om je eigen rules te schrijven. We geven verderop enkele concrete voorbeelden, zonder té diep in te gaan op alle details. Gelukkig wordt mod_security geleverd met een hele reeks rules voor de meest courante bedreigingen: de Core Rule Set of CRS. De CRS is onderverdeeld in tientallen categorieën die je naar wens kan activeren. De precieze syntax van alle rules hoef je niet te begrijpen. Wél belangrijk is dat je geactiveerde rules grondig test alvorens ze definitief in te schakelen.
Installatie
De installatieprocedure voor mod_security hangt sterk af van de gebruikte distributie. Nadat je de nodige pakketten gelokaliseerd en geïnstalleerd hebt, kijk je dus best even in /usr/share/doc/<pakketnaam> voor verdere instructies. Onder Debian en afgeleiden installeer je libapache2-modsecurity voor mod_security en modsecurity-crs voor de CRS, die in feite optioneel is. mod_security zelf configureer je in het netjes gedocumenteerde bestand /etc/modsecurity/modsecurity.conf. Dat bevat een aantal algemene instellingen in verband met logging, geheugengebruik, enzovoorts: de standaardwaardes zijn prima om te beginnen. De belangrijkste instelling staat helemaal bovenaan: SecRuleEngine DetectionOnly. Met die waarde verwerkt mod_security de geactiveerde rules en logt het verdachte transacties. Er wordt nog niets geblokkeerd: een veilige waarde om te controleren hoe mod_security zich bij jouw websites gedraagt. Andere waardes zijn Off om mod_security volledig uit te schakelen (ook al is de Apache-module geladen) en On om de rules effectief toe te passen. In de praktijk host een webserver vaak meerdere websites. Het is zelden haalbaar om mod_security dan globaal in- of uit te schakelen. De SecRuleEngine-directive plaats je dus best binnen een <VirtualHost>-blok in je Apache-configuratie. Zo kies je voor elke vhost hoe mod_security zich moet gedragen.
CRS
Met bovenstaande configuratie zijn er nog geen rules geladen. Wil je alle rules van de CRS activeren met de standaardinstellingen? Dan voeg je gewoon de regels uit listing 1 toe aan /etc/modsecurity/modsecurity.conf. Wil je de CRS nog verder configureren of zelf kiezen welke rules je al dan niet laadt? Gebruik dan de configuratie uit listing 2 en kopieer het bestand /usr/share/modsecurity-crs/modsecurity_crs_10_setup.conf naar /etc/apache/conf.d/modsecurity_crs_10_setup.conf. Dat laatste bestand pas je aan als je de CRS wilt configureren.
<IfModule security2_module>
Include /usr/share/modsecurity-crs/*.conf
Include /usr/share/modsecurity-crs/base_rules/*.conf
</IfModule>
<IfModule security2_module>
Include /usr/share/modsecurity-crs/activated_rules/*.conf
</IfModule>
De directory /usr/share/modsecurity-crs/activated_rules is initieel leeg. Individuele rules activeer je door in die directory een symlink te maken naar een .conf-bestand in één van de andere *_rules-directories: base_rules, optional_rules en experimental_rules. Sommige rules hebben nog .data-bestanden nodig: vergeet dan niet om ook daarvoor een symlink aan te maken.
Ook de CRS-configuratie in modsecurity_crs_10_setup.conf kun je het beste even nakijken. Lees vooral het stukje over Self-Contained vs. Collaborative Detection. Dat zijn twee verschillende modus waarin de CRS kan werken. In de eerste mode wordt elke transactie meteen geblokkeerd als een bepaalde rule dat vraagt, terwijl in de tweede mode elke rule een score toekent aan een transactie. Pas nadat alle rules verwerkt zijn, beslist mod_security op basis van de totale score wat er met de transactie moet gebeuren. De Collaborative Detection Mode is een recente uitbreiding van de Core Rule Set. Standaard werkt de CRS dus in Self-Contained Mode, die voor beginners ook eenvoudiger te debuggen is. Blokkeert mod_security een bepaalde request, dan is één welbepaalde rule daarvoor verantwoordelijk. Apache’s error log bevat dan een verwijzing naar die rule. Bovendien is het vrij eenvoudig om de rule in kwestie in de CRS te vinden en te bekijken wat die precies doet. Nadelen zijn er ook. Omdat elke rule op zich staat, bestaat het risico dat bepaalde problemen steeds onder de radar blijven. Zo hoeft een overtreding op één minder belangrijke rule niet meteen geblokkeerd te worden, maar een combinatie van meerdere overtredingen is wél verdacht. En wanneer een bepaalde rule resulteert in te veel false positives (legitieme requests die tóch geblokkeerd worden door mod_security), dan wil je ‘m wellicht aanpassen. Dat doe je best door de rule in kwestie te kopiëren in een eigen configuratiebestand, de nieuwe rule aan te passen en de oorspronkelijke rule uit te schakelen. Maar dan moet je bij elke upgrade van mod_security wel al jouw aanpassingen aan bestaande rules steeds overzetten op de nieuwe rules…
CRS tweaken
De Collaborative Detection Mode lost bovenstaande problemen op. Elke overtreding van een rule resulteert in een bepaalde score: 5 voor een critical, 4 voor een error, etc. De precieze scores kun je trouwens aanpassen met de variabelen tx.critical_anomaly_score, tx.error_anomaly_score, etc. De variabelen tx.inbound_anomaly_score_level en tx.outbound_anomaly_score_level bepalen nu vanaf welke score inkomend respectievelijk uitgaand HTTP-verkeer geblokkeerd moet worden. Standaard hebben die variabelen de waardes 5 en 4. Dat betekent dat één overtreding van een critical rule volstaat om de request te blokkeren. Resulteert dat in te veel false positives, dan kun je experimenteren met de *-anomaly_score- en *-anomaly_score_level-variabelen om mod_security’s gedrag globaal aan te passen.
mod_security kan ook een hele reeks beperkingen opleggen aan POST-requests: maximum aantal argumenten, maximumlengte van argumentnamen of -waardes, maximale bestandsgrootte voor uploads, etc. Het merendeel van die regels zijn standaard uitgeschakeld. Niet alle webapplicaties kunnen immers even goed overweg met dergelijke beperkingen. Ook bescherming tegen brute force attacks op login-pagina’s en denial of service attacks zijn standaard uitgeschakeld. Voor beide rules moet je een burst_time_slice instellen (bijvoorbeeld 60 seconden) waarbinnen meer dan counter_threshold request (bijvoorbeeld 10) leiden tot het blokkeren van verdere requests gedurende block_timeout (bijvoorbeeld 300 seconden). Specifiek voor de brute force attack rule stel je ook nog de URL’s in van de login-pagina’s die je wilt beschermen. Ook die rules moet je érg goed testen alvorens ze definitief in te schakelen. Een incorrecte configuratie kun je webapplicatie immers onbruikbaar maken!
Rule syntax
In listing 3 zie je een CRS-rule uit de categorie SQL Injection Attacks. Elke rule, hoe eenvoudig of complex ook, is als volgt opgebouwd:
SecRule Target Operator Actions
Het target duidt aan welk deel van de HTTP-request of response onderzocht moet worden. In dit geval zijn dat onder andere de argumenten (ARGS) en argumentnamen (ARGS_NAMES). De operator specificeert hoe en waarmee mod_security de request/response data vergelijkt om te bepalen of er een match is of niet voor de rule. De meeste rules gebruiken de operator @rx, gevolgd door een reguliere expressie. @rx is mod_security’s standaardoperator en mag bijgevolg weggelaten worden, zoals in dit voorbeeld. De actions zorgen er tenslotte voor dat mod_security gepast ingrijpt. Hier wordt de rule uitgevoerd op de request body (phase:2), wordt de anomaly_score van de request verhoogd en krijg je een duidelijke foutmelding in de logfile te zien. De actie “block” wordt vervangen door de standaardactie die is ingesteld in modsecurity_crs_10_setup.conf. In Self-Contained Mode is dat een onmiddellijke deny, terwijl in Collaborative Detection Mode de eigenlijke deny pas later wordt uitgevoerd (zie het bestand /usr/share/modsecurity-crs/base_rules/modsecurity_crs_49_inbound_blocking.conf). In listing 4 zie je (een deel van) de Apache errors bij een SQL Injection Attack.
$ grep 981247 /usr/share/modsecurity-crs/base_rules/modsecurity_crs_41_sql_injection_attacks.conf
SecRule REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* “(?i:(?:[\d\W]\s+as\s*?[\”‘`´’‘\w]+\s*?from)|(?:^[\W\d]+\s*?(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc))|(?:(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s+(?:(?:group_)concat|char|load_file)\s?\(?)|(?:end\s*?\);)|([\”‘`´’‘]\s+regexp\W)|(?:[\s(]load_file\s*?\())” “phase:2,capture,t:none,t:urlDecodeUni,block,msg:’Detects concatenated basic SQL injection and SQLLFI attempts’,id:’981247′,tag:’WEB_ATTACK/SQLI’,logdata:’%{TX.0}’,severity:’2′,setvar:’tx.msg=%{rule.id}-%{rule.msg}’,setvar:tx.sql_injection_score=+1,setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:’tx.%{tx.msg}-WEB_ATTACK/SQLI-%{matched_var_name}=%{tx.0}'”
[Sun Aug 25 15:53:55 2013] [error] [client 94.224.127.72] ModSecurity: Warning. Pattern match “(?i:(?:[\\\\d\\\\W]\\\\s+as\\\\s*?[\\”‘`\\xc2\\xb4\\xe2\\x80\\x99\\xe2\\x80\\x98\\\\w]+\\\\s*?from)|(?:^[\\\\W\\\\d]+\\\\s*?(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc))|(?:(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\\\\s+ …” at ARGS:_pass. [file “/usr/share/modsecurity-crs/activated_rules/modsecurity_crs_41_sql_injection_attacks.conf”] [line “255”] [id “981247”] [msg “Detects concatenated basic SQL injection and SQLLFI attempts”] [data “‘ UNION”] [severity “CRITICAL”] [tag “WEB_ATTACK/SQLI”] [hostname “webmail.filipvervloesem.com”] [uri “/”] [unique_id “UhoMcyX8fHEAABYwBA0AAAAH”]
[Sun Aug 25 15:53:55 2013] [error] [client 94.224.127.72] ModSecurity: Access denied with code 403 (phase 2). Pattern match “(.*)” at TX:981318-WEB_ATTACK/SQL_INJECTION-ARGS:_pass. [file “/usr/share/modsecurity-crs/activated_rules/modsecurity_crs_49_inbound_blocking.conf”] [line “26”] [id “981176”] [msg “Inbound Anomaly Score Exceeded (Total Score: 15, SQLi=9, XSS=): Last Matched Message: 981247-Detects concatenated basic SQL injection and SQLLFI attempts”] [data “Last Matched Data: ‘”] [hostname “webmail.filipvervloesem.com”] [uri “/”] [unique_id “UhoMcyX8fHEAABYwBA0AAAAH”]
De CRS bestaat eigenlijk uit een lange reeks algemene rules om bepaalde types attacks te herkennen. Echt 100% sluitend is die beveiliging niet: de Core Rule Set implementeert een immers een negative security model. Dat omschrijft erg precies wat niet is toegelaten en blokkeert die requests. Maar het is niet zo evident om voor alle webapplicaties in één algemene ruleset de grens te trekken tussen kwaadaardige en legitieme requests. In de Collaborative Detection Mode kun je experimenteren met de anomaly_score_levels. Ook dat blijft steeds een afweging tussen gebruiksgemak (minder false positives) of beveiliging. Een positive security model is eigenlijk een betere optie. Daarin beschrijf je erg nauwkeurig welke requests vereist zijn voor de goede werking van de applicatie. Requests die niet voldoen aan die omschrijving blokkeer je gewoon. Het nadeel is dat elke applicatie zijn eigen ruleset nodig heeft én dat het uitwerken van die rules behoorlijk tijdrovend is. In de praktijk is dat enkel haalbaar voor de ontwikkelaars van de applicatie.
Uitzonderingen
Te veel false positives is een klassiek probleem bij nieuwe mod_security-installaties. Krijg je bepaalde false positives onmogelijk weggewerkt door de Collaborative Detection Mode settings te tweaken? Dan kun je individuele rules gewoon uitschakelen op basis van het rule id. De rule uit listing 3 schakel je bijvoorbeeld uit met de directive “SecRuleRemoveById 981247”. Of je maakt een specifieke uitzonderingsregel voor de requests waarin de false positive getriggerd wordt. Zo konden we na het inschakelen van de CRS geen e-mails met SQL- of shell-code meer versturen vanuit de Roundcube webmail client. Onze uitzonderingsregel in listing 5 reset daarom de tx.anomaly_score van een POST-request waarvan de Host:-header ‘webmail.filipvervloesem’ bevat en de Referer:-header ‘&_action=compose’. Let wel op dat je jouw mod_security-uitzonderingen enkel toepast op de vhost in kwestie. Zo schakel je niet meer CRS-rules uit dan benodigd!
$ cat modsecurity_crs_48_local_exceptions.conf
SecRule REQUEST_HEADERS:Host “@contains webmail.filipvervloesem” “chain,phase:2,t:none,pass,nolog,auditlog,msg:’Skipping inbound anomaly detection for mail sent from Roundcube'”
SecRule REQUEST_METHOD “POST” “chain,t:none”
SecRule REQUEST_HEADERS:Referer “@endswith &_action=compose” “t:none,setvar:!tx.anomaly_score”
Virtual patching
Afsluiten doen we met een voorbeeld om het principe van “virtual patching” te illustreren. Een applicatie “virtueel” patchen doe je door een mod_security-rule te schrijven in plaats van de code van de applicatie zélf aan te passen. Virtual patching biedt twee voordelen: het is snel (je hoeft niet te wachten op de ontwikkelaar om het lek te dichten) en het werkt ook in omstandigheden waarin je de code niet kan of mag aanpassen. Zo installeerden we eens een webapplicatie die afbeeldingen van een externe server steeds via het http-protocol benaderde. Bied je die webapplicatie via https aan, dan blokkeren recente versies van Firefox en Chrome de (insecure) http-content. Maar de meeste gebruikers zien die foutmelding niet en krijgen de indruk dat de applicatie niet naar behoren werkt! Met de @rsub-operator (nieuw in mod_security 2.6) kun je reguliere expressies gebruiken om de response body van een webpagina on-the-fly aan te passen. Het voorbeeld uit listing 6 verandert alle http:-links in de response body naar https:-links, waardoor het probleem opgelost is. Uiteraard is dit maar een eenvoudig voorbeeld: virtual patching is vooral interessant om nieuwe kwetsbaarheden zo snel mogelijk te dichten.
SecStreamOutBodyInspection On
SecContentInjection On
SecRule STREAM_OUTPUT_BODY “@rsub s/http:/https:/” “phase:4,t:none,nolog,pass”
De moeite waard
Geen enkele beveiligingsmethode is 100% sluitend, ook mod_security niet. Maar met de Core Rule Set kun je toch vrij eenvoudig heel wat potentiële beveiligingsgaten dichten. Vergeet alleen niet om jouw webapplicaties goed te testen! Je kan ook je eigen rule set aanmaken per applicatie. Hoewel dat erg tijdrovend is, biedt het wel de beste bescherming tegen onbekende aanvallen. Specifieke vulnerabilities los je dan weer op met virtual patching, in afwachting van een officiële patch voor de webapplicatie. Al bij al loont het dus zeker de moeite om je te verdiepen in mod_security!