WordPress mit naxsi

Naxsi (Logo von MARE system)

Changelog

  • 24.10.2014 – Merged latest rules from github
  • 29.12.2013 – Merged latest rules from github
  • 29.12.2013 – Fix for „wp-admin/customize.php“

Ich habe mittlerweile einen Beitrag zum erstellen eigener Whitelists erstellt.

Naxsi ist die Web Application Firewall von nginx. Sie läuft als Modul mit nginx und ist im Gegensatz zu anderen Lösungen sehr einfach zu konfigurieren. Ebenfalls gibt es einen „Lernmodus“, um Regeln quasi anzulernen. Hierzu erschien im Admin Magazin ein schöner Artikel, der sich auch zu kaufen lohnt.

Mehr oder weniger durch Zufall bin ich auf ein fertiges Regelwerk gestoßen, um WordPress mit naxsi abzusichern. Die Quelle befindet sich hier.

Voraussetzung ist natürlich, dass nginx bereits installiert ist. Das Meta-Paket „nginx“ beinhaltet naxsi nicht, jedoch kann nginx mit dem Modul naxsi einfach nachinstalliert werden, die Konfigurationen bleiben erhalten und werden nicht überschrieben:

sudo apt-get install nginx-naxsi

In der Datei /etc/nginx/nginx.conf befand sich bereits ein Parameter für naxsi, der nun aktivieret werden kann:

/etc/nginx/nginx.conf:

http {
...
        include /etc/nginx/naxsi_core.rules;
...
}

Naxsi sieht vor, für jede Location im Server-Abschnitt ein eigenes Regelwerk zu verwenden. Dazu wird nun die jeweilige Site Konfiguration geöffnet, die WordPress beinhaltet. Zusätzlich wird eine neue Location angelegt, in die der Besucher im Falle weitergeleitet wird.

/etc/nginx/sites-available/default (Beispiel!):

server {
...
        location / {
                include    /etc/nginx/naxsi_wp.rules;
                ...
        }

        location /Denied {
                return 500;
        }
...
}

In diesem Fall befinden sich die Regeln – logischerweise – in der Datei „/etc/nginx/naxsi_wp.rules“.
Die Datei herunterladen:

wget http://debinux.de/wp-content/uploads/naxsi.rules -O /etc/nginx/naxsi_wp.rules

Zur Übersicht/Vollständigkeit hier der Code:

# Sample rules file for default vhost.
 
#LearningMode;
SecRulesEnabled;
#SecRulesDisabled;
DeniedUrl "/Denied";
 
## check rules
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
 
# WordPress naxsi rules

### HEADERS
BasicRule wl:1000,1001,1005,1007,1010,1011,1013,1100,1200,1308,1309,1310,1311,1315 "mz:$HEADERS_VAR:cookie";
# xmlrpc
BasicRule wl:1402 "mz:$HEADERS_VAR:content-type";

### simple BODY (POST)
# comments
BasicRule wl:1000,1010,1011,1013,1015,1200,1310,1311 "mz:$BODY_VAR:post_title";
BasicRule wl:1000 "mz:$BODY_VAR:original_publish";
BasicRule wl:1000 "mz:$BODY_VAR:save";
BasicRule wl:1008,1010,1011,1013,1015 "mz:$BODY_VAR:sk2_my_js_payload";
BasicRule wl:1001,1009,1005,1016,1100,1310 "mz:$BODY_VAR:url";
BasicRule wl:1009,1100 "mz:$BODY_VAR:referredby";
BasicRule wl:1009,1100 "mz:$BODY_VAR:_wp_original_http_referer";
BasicRule wl:1000,1001,1005,1008,1007,1009,1010,1011,1013,1015,1016,1100,1200,1302,1303,1310,1311,1315,1400 "mz:$BODY_VAR:comment";
BasicRule wl:1100 "mz:$BODY_VAR:redirect_to";
BasicRule wl:1000,1009,1315 "mz:$BODY_VAR:_wp_http_referer";
BasicRule wl:1000 "mz:$BODY_VAR:action";
BasicRule wl:1001,1013 "mz:$BODY_VAR:blogname";
BasicRule wl:1015,1013 "mz:$BODY_VAR:blogdescription";
BasicRule wl:1015 "mz:$BODY_VAR:date_format_custom";
BasicRule wl:1015 "mz:$BODY_VAR:date_format";
BasicRule wl:1015 "mz:$BODY_VAR:tax_input%5bpost_tag%5d";
BasicRule wl:1015 "mz:$BODY_VAR:tax_input[post_tag]";
BasicRule wl:1100 "mz:$BODY_VAR:siteurl";
BasicRule wl:1100 "mz:$BODY_VAR:home";
BasicRule wl:1000,1015 "mz:$BODY_VAR:submit";
# news content matches pretty much everything
BasicRule wl:0 "mz:$BODY_VAR:content";
BasicRule wl:1000 "mz:$BODY_VAR:delete_option";
BasicRule wl:1000 "mz:$BODY_VAR:prowl-msg-message";
BasicRule wl:1100 "mz:$BODY_VAR:_url";
BasicRule wl:1001,1009 "mz:$BODY_VAR:c2c_text_replace%5btext_to_replace%5d";
BasicRule wl:1200 "mz:$BODY_VAR:ppn_post_note";
BasicRule wl:1100 "mz:$BODY_VAR:author";
BasicRule wl:1001,1015 "mz:$BODY_VAR:excerpt";
BasicRule wl:1015 "mz:$BODY_VAR:catslist";
BasicRule wl:1005,1008,1009,1010,1011,1015,1315 "mz:$BODY_VAR:cookie";
BasicRule wl:1101 "mz:$BODY_VAR:googleplus";
BasicRule wl:1007 "mz:$BODY_VAR:name";
BasicRule wl:1007 "mz:$BODY_VAR:action";
BasicRule wl:1100 "mz:$BODY_VAR:attachment%5burl%5d";
BasicRule wl:1100 "mz:$BODY_VAR:attachment_url";
BasicRule wl:1001,1009,1100,1302,1303,1310,1311 "mz:$BODY_VAR:html";
BasicRule wl:1015 "mz:$BODY_VAR:title";
BasicRule wl:1001,1009,1015 "mz:$BODY_VAR:recaptcha_challenge_field";
BasicRule wl:1011 "mz:$BODY_VAR:pwd";
BasicRule wl:1000 "mz:$BODY_VAR:excerpt";

### BODY|NAME
BasicRule wl:1000 "mz:$BODY_VAR:delete_option|NAME";
BasicRule wl:1000 "mz:$BODY_VAR:from|NAME";

### Simple ARGS (GET)
# WP login screen
BasicRule wl:1100 "mz:$ARGS_VAR:redirect_to";
BasicRule wl:1000,1009 "mz:$ARGS_VAR:_wp_http_referer";
BasicRule wl:1000 "mz:$ARGS_VAR:wp_http_referer";
BasicRule wl:1000 "mz:$ARGS_VAR:action";
BasicRule wl:1000 "mz:$ARGS_VAR:action2";
# load and load[] GET variable
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load";
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load[]";
BasicRule wl:1015 "mz:$ARGS_VAR:q";
BasicRule wl:1000,1015 "mz:$ARGS_VAR:load%5b%5d";

### URL
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update-core.php";
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update.php";
# URL|BODY
BasicRule wl:1009,1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_http_referer";
BasicRule wl:1016 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect";
BasicRule wl:11 "mz:$URL:/xmlrpc.php|BODY";
BasicRule wl:11 "mz:$URL:/wp-cron.php|BODY";
BasicRule wl:2 "mz:$URL:/wp-admin/async-upload.php|BODY";
# URL|BODY|NAME
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_original_http_referer|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/user-edit.php|$BODY_VAR:from|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:attachment%5burl%5d|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:attachment_url|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/plugins.php|$BODY_VAR:verify-delete|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:post_category[]|NAME";
BasicRule wl:1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:post_category|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:tax_input[post_tag]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/post.php|$BODY_VAR:newtag[post_tag]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/users.php|$BODY_VAR:users[]|NAME";
# URL|ARGS|NAME
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/load-scripts.php|$ARGS_VAR:load[]|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:delete_count|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:update|NAME";

# plain WP site
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update-core.php";
BasicRule wl:1000 "mz:URL|$URL:/wp-admin/update.php";
# URL|BODY
BasicRule wl:1009,1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_http_referer";
BasicRule wl:1016 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect";
BasicRule wl:11 "mz:$URL:/xmlrpc.php|BODY";
BasicRule wl:11 "mz:$URL:/wp-cron.php|BODY";
# URL|BODY|NAME
BasicRule wl:1100 "mz:$URL:/wp-admin/post.php|$BODY_VAR:_wp_original_http_referer|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/post.php|$BODY_VAR:metakeyselect|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/user-edit.php|$BODY_VAR:from|NAME";
BasicRule wl:1100 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:attachment%5burl%5d|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR:data[wp-auth-check]|NAME";
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/update-core.php|$BODY_VAR:checked[]|NAME";

# URL|ARGS|NAME
BasicRule wl:1310,1311 "mz:$URL:/wp-admin/load-scripts.php|$ARGS_VAR:load[]|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:delete_count|NAME";
BasicRule wl:1000 "mz:$URL:/wp-admin/users.php|$ARGS_VAR:update|NAME";

# WP 3.6/8 Fix
BasicRule wl:1100 "mz:$URL:/|$ARGS_VAR:statify_referrer";
BasicRule wl:1001,1015,1009,1311,1310,1101,1016 "mz:$URL:/|$BODY_VAR:customized";
### Plugins
#WP Minify
BasicRule wl:1015 "mz:$URL:/wp-content/plugins/bwp-minify/min/|$ARGS_VAR:f";

Via „naxsi_wp.rules“ wird /Denied als Umleitung für geblockte Anfragen definiert. „return 500“ würde ergo auf die Fehlerseite 500 verweisen. Selbstverständlich sind diese Pfade und Parameter nach Belieben einzustellen. Wer witzig ist, darf auch 418 zurückgeben. ;)

Zur Aktivierung den Dienst neustarten:

service nginx restart

Achtung: Bei WordPress Updates kann es passieren, dass die Regeln geändert werden müssen! Ich habe die obigen Regeln erfolgreich auf WordPress 3.8 getestet. Sollte etwas nicht mehr funktionieren, empfehle ich den Lernmodus („LearningMode“) zu aktivieren und das Wiki zu naxsi zu studieren. Ein Blick in die Funktionsweise schadet generell nicht und sollte in jedem Fall schon geschehen sein.

Hat naxsi etwas gefiltert, so wird dies in der Fehler-Logdatei (via „ErrorLog“) des jeweiligen Servers festgehalten.
Getestet werden kann die Konfiguration und Wirksamkeit, indem „?a=<>“ an eine WordPress URL angehängt wird, z.B. www.domain.tld/?a=<>
Der Fehler wird protokolliert:

NAXSI_FMT: ip=X.X.X.X&server=www.domain.tld&uri=/&learning=0&total_processed=8&total_blocked=2&zone0=ARGS&id0=1302&var_name0=a, client: X.X.X.X, server: debinux.de, request: „GET /?a=%3C%3E HTTP/1.1“, host: „www.domain.tld“


Das naxsi-Logo ist Eigentum von MARE system.

7 Antworten auf “WordPress mit naxsi

    1. André P. Autor

      Guten Morgen,

      das tut mir Leid, ich war zu Beginn noch etwas schlampig mit den Credits.
      Habe es im Tag hinterlegt und unter dem Artikel einen Hinweis angebracht.
      Ich hoffe, so ist es annehmbar für dich/euch. Ansonsten einfach Bescheid geben, dann kommt es sofort runter.
      Gutes Projekt übrigens, schaue ich mir gleich mal genauer an… :-)

      Viele Grüße
      André

  1. Markus

    Hallo André,

    danke für die Ausführung!
    Bei mir blockt naxsi seit neuestem immer den Google-Bot. Siehe hier:
    2014/05/14 18:20:51 [error] 16515#0: *25060 NAXSI_FMT: ip=66.249.65.74&server=www.test.de&uri=/ip..&total_processed=2556&total_blocked=2&zone0=URL&id0=1200&var_name0=, client: 66.249.65.74, server: http://www.test.de, request: „GET /ip.. HTTP/1.1“, host: „www.test.de“

    Ist auch klar, warum er blockt. Der Google Bot versucht einen Dir-Traversal zu machen, was in den naxsi_core.rules als Angriff definiert ist.

    Rule ID 1200:
    MainRule „str:..“ „msg:double dot“ „mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie“ „s:$TRAVERSAL:4“ id:1200;

    Ich musste die ID daraufhin entfernen bzw. kommentieren. Hast du eine ähnliche Erfahrung gemacht?

    Grüße
    Markus

  2. James Monroe

    Are these rules updated for the latest WordPress version. We have servers running nginx with mod security and nginx page speed .

    Had to disable mod security , was giving me hell. But, I can’t find much text on naxsi. Eventhough. , the github page has a rule set for WordPress .

    1. André P. Autor

      Hi James,
      these rules work fine for the latest WordPress (updated the text). But I think it also depends on the complexity of your site, especially on the plug-ins you are using. Well, I have 8 plug-ins running and did not run into problems.
      „pagespeed“ is fine to use. It just minifies some files and does not add any content.
      You can activate the rule set, browse your site, try all functions and check the error.log of your nginx against NAXSI_FMT filters. If you find any feel free to tell me.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.