debian/0000755000000000000000000000000013370361752007175 5ustar debian/nginx-extras.postinst0000644000000000000000000000142012305451330013413 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-naxsi-ui.manpages0000644000000000000000000000006612305451330013557 0ustar debian/naxsi-ui-intercept.1 debian/naxsi-ui-extract.1 debian/nginx-core.dirs0000644000000000000000000000001112310175122012104 0ustar usr/sbin debian/nginx-common.nginx.service0000644000000000000000000000064212305451330014301 0ustar [Unit] Description=A high performance web server and a reverse proxy server After=network.target [Service] Type=forking PIDFile=/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload ExecStop=/usr/sbin/nginx -s quit [Install] WantedBy=multi-user.target debian/nginx-common.templates0000644000000000000000000000103613001364150013510 0ustar Template: nginx/log-symlinks Type: note _Description: Possible insecure nginx log files The following log files under /var/log/nginx directory are symlinks owned by www-data: . ${logfiles} . Since nginx 1.4.4-4 /var/log/nginx was owned by www-data. As a result www-data could symlink log files to sensitive locations, which in turn could lead to privilege escalation attacks. Although /var/log/nginx permissions are now fixed it is possible that such insecure links already exist. So, please make sure to check the above locations. debian/nginx-naxsi.dirs0000644000000000000000000000001112305451330012300 0ustar usr/sbin debian/nginx-core.install0000644000000000000000000000004612310176053012625 0ustar debian/build-core/objs/nginx usr/sbin debian/nginx-full.dirs0000644000000000000000000000001112305451330012120 0ustar usr/sbin debian/README.Debian0000644000000000000000000000053712305451330011230 0ustar README for Debian ----------------- Files under /var/www/ are not supported as per Debian Policy. Please see: http://lintian.debian.org/tags/dir-or-file-in-var-www.html and, http://www.pathname.com/fhs/pub/fhs-2.3.html#THEVARHIERARCHY for more details and explanations. -- Kartik Mistry Fri, 05 Mar 2010 13:31:15 +0530 debian/nginx-extras.prerm0000644000000000000000000000050412305451330012657 0ustar #!/bin/sh set -e case "$1" in remove|remove-in-favour|deconfigure|deconfigure-in-favour) if [ -x /etc/init.d/nginx ]; then invoke-rc.d nginx stop || exit $? fi ;; upgrade|failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/rules0000755000000000000000000001574012310175701010252 0ustar #!/usr/bin/make -f debian_cflags:=$(shell dpkg-buildflags --get CFLAGS | sed 's/-O3/-O2/') $(shell dpkg-buildflags --get CPPFLAGS) debian_ldflags:=$(shell dpkg-buildflags --get LDFLAGS) # export necessary for (hardening) flags for perl # (src/http/modules/perl/Makefile.PL). DEBIAN_NGINX_PERL_LDFLAGS:= $(debian_ldflags) export DEBIAN_NGINX_PERL_LDFLAGS FLAVOURS:=core full light extras naxsi BUILDDIR_core = $(CURDIR)/debian/build-core BUILDDIR_full = $(CURDIR)/debian/build-full BUILDDIR_light = $(CURDIR)/debian/build-light BUILDDIR_extras = $(CURDIR)/debian/build-extras BUILDDIR_naxsi = $(CURDIR)/debian/build-naxsi MODULESDIR = $(CURDIR)/debian/modules BASEDIR = $(CURDIR) DEB_BUILD_ARCH ?=$(shell dpkg-architecture -qDEB_BUILD_ARCH) ifeq ($(DEB_BUILD_ARCH),sparc) debian_cflags += -m32 -mcpu=ultrasparc endif ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) ifeq (${NUMJOBS}, 0) NUMJOBS = 1 endif else NUMJOBS = 1 endif # configure flags common_configure_flags := \ --with-cc-opt="$(debian_cflags)" \ --with-ld-opt="$(debian_ldflags)" \ --prefix=/usr/share/nginx \ --conf-path=/etc/nginx/nginx.conf \ --http-log-path=/var/log/nginx/access.log \ --error-log-path=/var/log/nginx/error.log \ --lock-path=/var/lock/nginx.lock \ --pid-path=/run/nginx.pid \ --http-client-body-temp-path=/var/lib/nginx/body \ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ --http-proxy-temp-path=/var/lib/nginx/proxy \ --http-scgi-temp-path=/var/lib/nginx/scgi \ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ --with-debug \ --with-pcre-jit \ --with-ipv6 \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_realip_module \ config.env.%: dh_testdir mkdir -p $(BUILDDIR_$*) cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/conf $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/contrib $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/man $(BUILDDIR_$*)/ config.status.core: config.env.core cd $(BUILDDIR_core) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ >$@ touch $@ config.status.full: config.env.full cd $(BUILDDIR_full) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ --add-module=$(MODULESDIR)/nginx-auth-pam \ --add-module=$(MODULESDIR)/nginx-dav-ext-module \ --add-module=$(MODULESDIR)/nginx-echo \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ --add-module=$(MODULESDIR)/ngx_http_substitutions_filter_module \ >$@ touch $@ config.status.light: config.env.light cd $(BUILDDIR_light) && ./configure \ $(common_configure_flags) \ --with-http_gzip_static_module \ --without-http_browser_module \ --without-http_geo_module \ --without-http_limit_req_module \ --without-http_limit_zone_module \ --without-http_memcached_module \ --without-http_referer_module \ --without-http_scgi_module \ --without-http_split_clients_module \ --without-http_ssi_module \ --without-http_userid_module \ --without-http_uwsgi_module \ --add-module=$(MODULESDIR)/nginx-echo \ >$@ touch $@ config.status.extras: config.env.extras cd $(BUILDDIR_extras) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_mp4_module \ --with-http_perl_module \ --with-http_random_index_module \ --with-http_secure_link_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ --add-module=$(MODULESDIR)/headers-more-nginx-module \ --add-module=$(MODULESDIR)/nginx-auth-pam \ --add-module=$(MODULESDIR)/nginx-cache-purge \ --add-module=$(MODULESDIR)/nginx-dav-ext-module \ --add-module=$(MODULESDIR)/nginx-development-kit \ --add-module=$(MODULESDIR)/nginx-echo \ --add-module=$(MODULESDIR)/ngx-fancyindex \ --add-module=$(MODULESDIR)/nginx-http-push \ --add-module=$(MODULESDIR)/nginx-lua \ --add-module=$(MODULESDIR)/nginx-upload-progress \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ --add-module=$(MODULESDIR)/ngx_http_substitutions_filter_module \ >$@ touch $@ config.status.naxsi: config.env.naxsi cd $(BUILDDIR_naxsi) && ./configure \ $(common_configure_flags) \ --without-mail_pop3_module \ --without-mail_smtp_module \ --without-mail_imap_module \ --without-http_uwsgi_module \ --without-http_scgi_module \ --add-module=$(MODULESDIR)/naxsi/naxsi_src \ --add-module=$(MODULESDIR)/nginx-cache-purge \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ >$@ touch $@ config.status.%: echo "configuration for flavour $* not yet defined." build-arch.%: config.status.% dh_testdir dh_prep $(MAKE) -j$(NUMJOBS) -C $(BUILDDIR_$*) build build-arch: $(foreach flavour,$(FLAVOURS),build-arch.$(flavour)) dh_testdir touch $@ build-dbg.%: install dh_testdir dh_strip --package=nginx-$(*) --dbg-package=nginx-$(*)-dbg build-dbg: $(foreach flavour,$(FLAVOURS),build-dbg.$(flavour)) dh_testdir touch $@ build-indep: build: build-indep build-arch dh_testdir touch $@ clean: dh_testdir dh_testroot dh_clean rm -rf $(CURDIR)/debian/build-* install: dh_testdir dh_testroot dh_prep dh_installdirs dh_install binary-indep: build install dh_testdir dh_testroot dh_installman -i dh_installchangelogs -i -k CHANGES dh_installdocs -i dh_installdebconf dh_installexamples -i dh_systemd_enable --name=nginx dh_installinit -i --no-restart-on-upgrade --no-start --name=nginx dh_installinit -i --no-restart-on-upgrade --no-start --name=nginx-naxsi-ui dh_systemd_start dh_installlogrotate -i -pnginx-common --name=nginx dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i dh_md5sums -i dh_builddeb -i binary-arch: install build-dbg dh_testdir dh_testroot dh_installchangelogs -a -k CHANGES dh_installdocs -a dh_lintian -a dh_link -aA dh_compress -a dh_perl -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a dh_md5sums -a dh_builddeb -a binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install debian/nginx-light.postinst0000644000000000000000000000142012305451330013214 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-common.config0000644000000000000000000000131713004411171012760 0ustar #!/bin/sh set -e . /usr/share/debconf/confmodule logdir="/var/log/nginx" log_symlinks_check() { # Skip new installations [ -z "$1" ] && return # Skip unaffected installations dpkg --compare-versions "$1" lt-nl "1.4.6-1ubuntu3.6" || return 0 # Check for unsecure symlinks linked_logfiles="` find "$logdir" -type l -user www-data -name '*.log' `" # Skip if nothing is found [ -z "$linked_logfiles" ] && return db_subst nginx/log-symlinks logfiles $linked_logfiles db_input high nginx/log-symlinks || true db_go || true } case "$1" in configure|reconfigure) log_symlinks_check "$2" ;; *) ;; esac # vim: set ts=4 sts=4 sw=4 et sta ai : debian/control0000644000000000000000000003240713001363666010603 0ustar Source: nginx Section: httpd Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Kartik Mistry Uploaders: Jose Parrella , Fabio Tranchitella , Michael Lustfield , Dmitry E. Oboukhov , Cyril Lavier , Christos Trochalakis Build-Depends: autotools-dev, debhelper (>= 9), po-debconf, dh-systemd (>= 1.5), dpkg-dev (>= 1.15.5), libexpat-dev, libgd2-dev | libgd2-noxpm-dev, libgeoip-dev, liblua5.1-dev, libmhash-dev, libpam0g-dev, libpcre3-dev, libperl-dev, libssl-dev, libxslt1-dev, po-debconf, zlib1g-dev Standards-Version: 3.9.5 Homepage: http://nginx.net Vcs-Git: git://anonscm.debian.org/collab-maint/nginx.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/nginx.git;a=summary Package: nginx Architecture: all Depends: nginx-core (>= ${source:Version}) | nginx-full (>= ${source:Version}) | nginx-light (>= ${source:Version}) | nginx-extras (>= ${source:Version}) | nginx-naxsi (>= ${source:Version}) , nginx-core (<< ${source:Version}.1~) | nginx-full (<< ${source:Version}.1~) | nginx-light (<< ${source:Version}.1~) | nginx-extras (<< ${source:Version}.1~) | nginx-naxsi (<< ${source:Version}.1~) , ${misc:Depends} Description: small, powerful, scalable web/proxy server Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This is a dependency package to install either nginx-core (by default), nginx-full, nginx-light, nginx-extras, or nginx-naxsi. Package: nginx-doc Architecture: all Section: doc Depends: lsb-base (>= 3.2-14), ${misc:Depends} Description: small, powerful, scalable web/proxy server - documentation Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides extra documentation to help unleash the power of Nginx. Package: nginx-common Architecture: all Depends: lsb-base (>= 3.2-14), ${misc:Depends} Replaces: nginx (<< 0.8.54-4), nginx-extras (<< 0.8.54-4), nginx-full (<< 0.8.54-4), nginx-light (<< 0.8.54-4) Breaks: nginx (<< 0.8.54-4), nginx-extras (<< 0.8.54-4), nginx-full (<< 0.8.54-4), nginx-light (<< 0.8.54-4) Suggests: fcgiwrap, nginx-doc Description: small, powerful, scalable web/proxy server - common files Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package contains base configuration files used by all versions of nginx. Package: nginx-core Architecture: any Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-light, nginx-naxsi, nginx-full Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (core version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the complete set of standard modules included (but omitting some of those included in nginx-extras). . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, GeoIP, Gzip Precompression, HTTP Sub, Image Filter, IPv6, Real IP, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . NOTE: This is identical to the -full build, but without any third party modules built in. Package: nginx-core-dbg Architecture: any Section: debug Priority: extra Depends: nginx-core (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-full-dbg Description: nginx web/proxy server (core version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-core, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-full Architecture: any Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-light, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (standard version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the complete set of standard modules included (but omitting some of those included in nginx-extra). . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, GeoIP, Gzip Precompression, HTTP Sub, Image Filter, IPv6, Real IP, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . THIRD PARTY MODULES: Auth PAM, DAV Ext, Echo, HTTP Substitution Filter, Upstream Fair Queue. Package: nginx-full-dbg Architecture: any Section: debug Priority: extra Depends: nginx-full (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (standard version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-full, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-light Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-full, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (basic version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a very light version of nginx with only the minimal set of features and modules. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Charset, Empty GIF, FastCGI, Gzip, Headers, Index, Log, Map, Proxy, Rewrite, Upstream. . OPTIONAL HTTP MODULES: Debug, Gzip Precompression, IPv6, Real Ip, SSL, Stub Status. . THIRD PARTY MODULES: Echo. Package: nginx-light-dbg Architecture: any Section: debug Priority: extra Depends: nginx-light (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-full-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (basic version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-light, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-extras Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-full, nginx-light, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (extended version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the standard modules, plus extra features and modules such as the Perl module, which allows the addition of Perl in configuration files. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, Embedded Perl, FLV, GeoIP, Gzip Precompression, Image Filter, IPv6, MP4, Random Index, Real IP, Secure Link, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . THIRD PARTY MODULES: Auth PAM, Chunkin, DAV Ext, Echo, Embedded Lua, Fancy Index, HttpHeadersMore, HTTP Substitution Filter, http push, Nginx Development Kit, Upload Progress, Upstream Fair Queue. Package: nginx-extras-dbg Architecture: any Section: debug Priority: extra Depends: nginx-extras (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-full-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (extended version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-extras, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-naxsi Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1), nginx-naxsi-ui (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-full, nginx-light, nginx-core Description: nginx web/proxy server (version with naxsi) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the basic modules, plus the naxsi Web Application Firewall module. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Core, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, Split Clients, SSI, Upstream, User ID. . OPTIONAL HTTP MODULES: Debug, IPv6, Real IP, SSL, Stub Status. . THIRD PARTY MODULES: Naxsi, Cache Purge, Upstream Fair. Package: nginx-naxsi-dbg Architecture: any Section: debug Priority: extra Depends: nginx-naxsi (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-full-dbg, nginx-light-dbg, nginx-core-dbg Description: nginx web/proxy server (version with naxsi) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-naxsi, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-naxsi-ui Architecture: all Priority: extra Depends: daemon, dbconfig-common, nginx-naxsi (>= ${source:Version}), nginx-naxsi (<< ${source:Version}.1~), python-twisted-web, ${misc:Depends} Description: nginx web/proxy server - naxsi configuration front-end Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides the autolearning daemon and web user interface for nginx's naxsi module. . It includes an interceptor (listening on TCP port 8080), which monitors HTTP requests from naxsi, and an extractor (running on TCP port 8081), which reads the database and prints reports about blocked requests. debian/watch0000644000000000000000000000014712305451330010215 0ustar version=3 opts=pgpsigurlmangle=s/$/.asc/ \ http://nginx.org/download/nginx-(\d\.[02468]\.\d+)\.tar\.gz debian/naxsi-ui-intercept.10000644000000000000000000000123012305451330012770 0ustar .TH naxsi-ui-intercept 1 "2012-05-09" .SH NAME naxsi-ui-intercept \- Wrapper script to launch the interceptor daemon for the naxsi-ui tool. .SH SYNOPSIS .B naxsi-ui-intercept .SH DESCRIPTION .PP naxsi-ui-intercept is the interceptor part of naxsi-ui which monitors HTTP requests from naxsi. .PP .SH USAGE naxsi-ui-intercept .SH SEE ALSO Website: .SH "AUTHORS" naxsi was written by Thibault Koechlin . naxsi-ui-intercept was written by Cyril Lavier . .PP This manual page was written by Cyril Lavier for the Debian project (but may be used by others). debian/conf/0000755000000000000000000000000012305451341010111 5ustar debian/conf/sites-available/0000755000000000000000000000000012305451341013156 5ustar debian/conf/sites-available/default0000644000000000000000000000504112305451340014524 0ustar # You may add here your # server { # ... # } # statements for each of your virtual hosts to this file ## # You should look at the following URL's in order to grasp a solid understanding # of Nginx configuration files in order to fully unleash the power of Nginx. # http://wiki.nginx.org/Pitfalls # http://wiki.nginx.org/QuickStart # http://wiki.nginx.org/Configuration # # Generally, you will want to move this file somewhere, and start with a clean # file but keep this around for reference. Or just disable in sites-enabled. # # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. ## server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; root /usr/share/nginx/html; index index.html index.htm; # Make site accessible from http://localhost/ server_name localhost; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; # Uncomment to enable naxsi on this location # include /etc/nginx/naxsi.rules } # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests #location /RequestDenied { # proxy_pass http://127.0.0.1:8080; #} #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # #error_page 500 502 503 504 /50x.html; #location = /50x.html { # root /usr/share/nginx/html; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # fastcgi_split_path_info ^(.+\.php)(/.+)$; # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini # # # With php5-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php5-fpm: # fastcgi_pass unix:/var/run/php5-fpm.sock; # fastcgi_index index.php; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # root html; # index index.html index.htm; # # location / { # try_files $uri $uri/ =404; # } #} # HTTPS server # #server { # listen 443; # server_name localhost; # # root html; # index index.html index.htm; # # ssl on; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # # ssl_session_timeout 5m; # # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; # ssl_prefer_server_ciphers on; # # location / { # try_files $uri $uri/ =404; # } #} debian/conf/naxsi-ui.conf.1.4.10000644000000000000000000000033612305451340013156 0ustar [nx_extract] username = naxsi_web password = test port = 8081 rules_path = /etc/nginx/naxsi_core.rules [nx_intercept] port = 8080 [sql] dbtype = sqlite username = root password = hostname = 127.0.0.1 dbname = naxsi_sig debian/conf/scgi_params0000644000000000000000000000072112305451340012323 0ustar scgi_param REQUEST_METHOD $request_method; scgi_param REQUEST_URI $request_uri; scgi_param QUERY_STRING $query_string; scgi_param CONTENT_TYPE $content_type; scgi_param DOCUMENT_URI $document_uri; scgi_param DOCUMENT_ROOT $document_root; scgi_param SCGI 1; scgi_param SERVER_PROTOCOL $server_protocol; scgi_param REMOTE_ADDR $remote_addr; scgi_param REMOTE_PORT $remote_port; scgi_param SERVER_PORT $server_port; scgi_param SERVER_NAME $server_name; debian/conf/koi-win0000644000000000000000000000341512305451340011413 0ustar charset_map koi8-r windows-1251 { 80 88; # euro 95 95; # bullet 9A A0; #   9E B7; # · A3 B8; # small yo A4 BA; # small Ukrainian ye A6 B3; # small Ukrainian i A7 BF; # small Ukrainian yi AD B4; # small Ukrainian soft g AE A2; # small Byelorussian short u B0 B0; # ° B3 A8; # capital YO B4 AA; # capital Ukrainian YE B6 B2; # capital Ukrainian I B7 AF; # capital Ukrainian YI B9 B9; # numero sign BD A5; # capital Ukrainian soft G BE A1; # capital Byelorussian short U BF A9; # (C) C0 FE; # small yu C1 E0; # small a C2 E1; # small b C3 F6; # small ts C4 E4; # small d C5 E5; # small ye C6 F4; # small f C7 E3; # small g C8 F5; # small kh C9 E8; # small i CA E9; # small j CB EA; # small k CC EB; # small l CD EC; # small m CE ED; # small n CF EE; # small o D0 EF; # small p D1 FF; # small ya D2 F0; # small r D3 F1; # small s D4 F2; # small t D5 F3; # small u D6 E6; # small zh D7 E2; # small v D8 FC; # small soft sign D9 FB; # small y DA E7; # small z DB F8; # small sh DC FD; # small e DD F9; # small shch DE F7; # small ch DF FA; # small hard sign E0 DE; # capital YU E1 C0; # capital A E2 C1; # capital B E3 D6; # capital TS E4 C4; # capital D E5 C5; # capital YE E6 D4; # capital F E7 C3; # capital G E8 D5; # capital KH E9 C8; # capital I EA C9; # capital J EB CA; # capital K EC CB; # capital L ED CC; # capital M EE CD; # capital N EF CE; # capital O F0 CF; # capital P F1 DF; # capital YA F2 D0; # capital R F3 D1; # capital S F4 D2; # capital T F5 D3; # capital U F6 C6; # capital ZH F7 C2; # capital V F8 DC; # capital soft sign F9 DB; # capital Y FA C7; # capital Z FB D8; # capital SH FC DD; # capital E FD D9; # capital SHCH FE D7; # capital CH FF DA; # capital hard sign } debian/conf/win-utf0000644000000000000000000000577712305451340011444 0ustar # This map is not a full windows-1251 <> utf8 map: it does not # contain Serbian and Macedonian letters. If you need a full map, # use contrib/unicode2nginx/win-utf map instead. charset_map windows-1251 utf-8 { 82 E2809A; # single low-9 quotation mark 84 E2809E; # double low-9 quotation mark 85 E280A6; # ellipsis 86 E280A0; # dagger 87 E280A1; # double dagger 88 E282AC; # euro 89 E280B0; # per mille 91 E28098; # left single quotation mark 92 E28099; # right single quotation mark 93 E2809C; # left double quotation mark 94 E2809D; # right double quotation mark 95 E280A2; # bullet 96 E28093; # en dash 97 E28094; # em dash 99 E284A2; # trade mark sign A0 C2A0; #   A1 D18E; # capital Byelorussian short U A2 D19E; # small Byelorussian short u A4 C2A4; # currency sign A5 D290; # capital Ukrainian soft G A6 C2A6; # borken bar A7 C2A7; # section sign A8 D081; # capital YO A9 C2A9; # (C) AA D084; # capital Ukrainian YE AB C2AB; # left-pointing double angle quotation mark AC C2AC; # not sign AD C2AD; # soft hypen AE C2AE; # (R) AF D087; # capital Ukrainian YI B0 C2B0; # ° B1 C2B1; # plus-minus sign B2 D086; # capital Ukrainian I B3 D196; # small Ukrainian i B4 D291; # small Ukrainian soft g B5 C2B5; # micro sign B6 C2B6; # pilcrow sign B7 C2B7; # · B8 D191; # small yo B9 E28496; # numero sign BA D194; # small Ukrainian ye BB C2BB; # right-pointing double angle quotation mark BF D197; # small Ukrainian yi C0 D090; # capital A C1 D091; # capital B C2 D092; # capital V C3 D093; # capital G C4 D094; # capital D C5 D095; # capital YE C6 D096; # capital ZH C7 D097; # capital Z C8 D098; # capital I C9 D099; # capital J CA D09A; # capital K CB D09B; # capital L CC D09C; # capital M CD D09D; # capital N CE D09E; # capital O CF D09F; # capital P D0 D0A0; # capital R D1 D0A1; # capital S D2 D0A2; # capital T D3 D0A3; # capital U D4 D0A4; # capital F D5 D0A5; # capital KH D6 D0A6; # capital TS D7 D0A7; # capital CH D8 D0A8; # capital SH D9 D0A9; # capital SHCH DA D0AA; # capital hard sign DB D0AB; # capital Y DC D0AC; # capital soft sign DD D0AD; # capital E DE D0AE; # capital YU DF D0AF; # capital YA E0 D0B0; # small a E1 D0B1; # small b E2 D0B2; # small v E3 D0B3; # small g E4 D0B4; # small d E5 D0B5; # small ye E6 D0B6; # small zh E7 D0B7; # small z E8 D0B8; # small i E9 D0B9; # small j EA D0BA; # small k EB D0BB; # small l EC D0BC; # small m ED D0BD; # small n EE D0BE; # small o EF D0BF; # small p F0 D180; # small r F1 D181; # small s F2 D182; # small t F3 D183; # small u F4 D184; # small f F5 D185; # small kh F6 D186; # small ts F7 D187; # small ch F8 D188; # small sh F9 D189; # small shch FA D18A; # small hard sign FB D18B; # small y FC D18C; # small soft sign FD D18D; # small e FE D18E; # small yu FF D18F; # small ya } debian/conf/koi-utf0000644000000000000000000000432212305451340011412 0ustar # This map is not a full koi8-r <> utf8 map: it does not contain # box-drawing and some other characters. Besides this map contains # several koi8-u and Byelorussian letters which are not in koi8-r. # If you need a full and standard map, use contrib/unicode2nginx/koi-utf # map instead. charset_map koi8-r utf-8 { 80 E282AC; # euro 95 E280A2; # bullet 9A C2A0; #   9E C2B7; # · A3 D191; # small yo A4 D194; # small Ukrainian ye A6 D196; # small Ukrainian i A7 D197; # small Ukrainian yi AD D291; # small Ukrainian soft g AE D19E; # small Byelorussian short u B0 C2B0; # ° B3 D081; # capital YO B4 D084; # capital Ukrainian YE B6 D086; # capital Ukrainian I B7 D087; # capital Ukrainian YI B9 E28496; # numero sign BD D290; # capital Ukrainian soft G BE D18E; # capital Byelorussian short U BF C2A9; # (C) C0 D18E; # small yu C1 D0B0; # small a C2 D0B1; # small b C3 D186; # small ts C4 D0B4; # small d C5 D0B5; # small ye C6 D184; # small f C7 D0B3; # small g C8 D185; # small kh C9 D0B8; # small i CA D0B9; # small j CB D0BA; # small k CC D0BB; # small l CD D0BC; # small m CE D0BD; # small n CF D0BE; # small o D0 D0BF; # small p D1 D18F; # small ya D2 D180; # small r D3 D181; # small s D4 D182; # small t D5 D183; # small u D6 D0B6; # small zh D7 D0B2; # small v D8 D18C; # small soft sign D9 D18B; # small y DA D0B7; # small z DB D188; # small sh DC D18D; # small e DD D189; # small shch DE D187; # small ch DF D18A; # small hard sign E0 D0AE; # capital YU E1 D090; # capital A E2 D091; # capital B E3 D0A6; # capital TS E4 D094; # capital D E5 D095; # capital YE E6 D0A4; # capital F E7 D093; # capital G E8 D0A5; # capital KH E9 D098; # capital I EA D099; # capital J EB D09A; # capital K EC D09B; # capital L ED D09C; # capital M EE D09D; # capital N EF D09E; # capital O F0 D09F; # capital P F1 D0AF; # capital YA F2 D0A0; # capital R F3 D0A1; # capital S F4 D0A2; # capital T F5 D0A3; # capital U F6 D096; # capital ZH F7 D092; # capital V F8 D0AC; # capital soft sign F9 D0AB; # capital Y FA D097; # capital Z FB D0A8; # capital SH FC D0AD; # capital E FD D0A9; # capital SHCH FE D0A7; # capital CH FF D0AA; # capital hard sign } debian/conf/naxsi_core.rules0000644000000000000000000001224712305451340013324 0ustar ################################## ## INTERNAL RULES IDS:1-10 ## ################################## #weird_request : 1 #big_body : 2 #no_content_type : 3 #MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; ################################## ## SQL Injections IDs:1000-1099 ## ################################## MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1001; MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; ## Hardcore rules MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; MainRule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; MainRule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; ## end of hardcore rules MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; MainRule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4" id:1008; MainRule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; MainRule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1010; MainRule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1011; MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1013; MainRule "str:\"" "msg:double quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1014; MainRule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; ############################### ## OBVIOUS RFI IDs:1100-1199 ## ############################### MainRule "str:http://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; MainRule "str:https://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; MainRule "str:ftp://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; MainRule "str:php://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; ####################################### ## Directory traversal IDs:1200-1299 ## ####################################### MainRule "str:.." "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; MainRule "str:/etc/passwd" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; MainRule "str:c:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; MainRule "str:cmd.exe" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; MainRule "str:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; #MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206; ######################################## ## Cross Site Scripting IDs:1300-1399 ## ######################################## MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; MainRule "str:'" "msg:simple quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1306; MainRule "str:\"" "msg:double quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1307; MainRule "str:(" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1308; MainRule "str:)" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1309; MainRule "str:[" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; MainRule "str:]" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; MainRule "str:~" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; MainRule "str:;" "msg:semi coma" "mz:ARGS|URL|BODY" "s:$XSS:8" id:1313; MainRule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; MainRule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; #################################### ## Evading tricks IDs: 1400-1500 ## #################################### MainRule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; MainRule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; ############################# ## File uploads: 1500-1600 ## ############################# MainRule "rx:.ph*|.asp*" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500; debian/conf/uwsgi_params0000644000000000000000000000102412305451340012531 0ustar uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param UWSGI_SCHEME $scheme; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name; debian/conf/proxy_params0000644000000000000000000000026412305451340012561 0ustar proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; debian/conf/naxsi.rules0000644000000000000000000000043712305451340012312 0ustar # Sample rules file for default vhost. LearningMode; SecRulesEnabled; #SecRulesDisabled; DeniedUrl "/RequestDenied"; ## check rules CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$EVADE >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; debian/conf/fastcgi_params0000644000000000000000000000161712305451340013023 0ustar fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; fastcgi_param HTTPS $https if_not_empty; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; debian/conf/nginx.conf0000644000000000000000000000310112305451340012075 0ustar user www-data; worker_processes 4; pid /run/nginx.pid; events { worker_connections 768; # multi_accept on; } http { ## # Basic Settings ## sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # server_tokens off; # server_names_hash_bucket_size 64; # server_name_in_redirect off; include /etc/nginx/mime.types; default_type application/octet-stream; ## # Logging Settings ## access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ## # Gzip Settings ## gzip on; gzip_disable "msie6"; # gzip_vary on; # gzip_proxied any; # gzip_comp_level 6; # gzip_buffers 16 8k; # gzip_http_version 1.1; # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; ## # nginx-naxsi config ## # Uncomment it if you installed nginx-naxsi ## #include /etc/nginx/naxsi_core.rules; ## # nginx-passenger config ## # Uncomment it if you installed nginx-passenger ## #passenger_root /usr; #passenger_ruby /usr/bin/ruby; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } #mail { # # See sample authentication script at: # # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript # # # auth_http localhost/auth.php; # # pop3_capabilities "TOP" "USER"; # # imap_capabilities "IMAP4rev1" "UIDPLUS"; # # server { # listen localhost:110; # protocol pop3; # proxy on; # } # # server { # listen localhost:143; # protocol imap; # proxy on; # } #} debian/conf/mime.types0000644000000000000000000000404512305451340012130 0ustar types { text/html html htm shtml; text/css css; text/xml xml rss; image/gif gif; image/jpeg jpeg jpg; application/x-javascript js; application/atom+xml atom; text/mathml mml; text/plain txt; text/vnd.sun.j2me.app-descriptor jad; text/vnd.wap.wml wml; text/x-component htc; image/png png; image/tiff tif tiff; image/vnd.wap.wbmp wbmp; image/x-icon ico; image/x-jng jng; image/x-ms-bmp bmp; image/svg+xml svg svgz; application/java-archive jar war ear; application/json json; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; application/postscript ps eps ai; application/rtf rtf; application/vnd.ms-excel xls; application/vnd.ms-powerpoint ppt; application/vnd.wap.wmlc wmlc; application/vnd.google-earth.kml+xml kml; application/vnd.google-earth.kmz kmz; application/x-7z-compressed 7z; application/x-cocoa cco; application/x-java-archive-diff jardiff; application/x-java-jnlp-file jnlp; application/x-makeself run; application/x-perl pl pm; application/x-pilot prc pdb; application/x-rar-compressed rar; application/x-redhat-package-manager rpm; application/x-sea sea; application/x-shockwave-flash swf; application/x-stuffit sit; application/x-tcl tcl tk; application/x-x509-ca-cert der pem crt; application/x-xpinstall xpi; application/xhtml+xml xhtml; application/zip zip; application/octet-stream bin exe dll; application/octet-stream deb; application/octet-stream dmg; application/octet-stream eot; application/octet-stream iso img; application/octet-stream msi msp msm; application/ogg ogx; audio/midi mid midi kar; audio/mpeg mpga mpega mp2 mp3 m4a; audio/ogg oga ogg spx; audio/x-realaudio ra; audio/webm weba; video/3gpp 3gpp 3gp; video/mp4 mp4; video/mpeg mpeg mpg mpe; video/ogg ogv; video/quicktime mov; video/webm webm; video/x-flv flv; video/x-mng mng; video/x-ms-asf asx asf; video/x-ms-wmv wmv; video/x-msvideo avi; } debian/nginx-full.postinst0000644000000000000000000000142012305451330013047 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-common.nginx.default0000644000000000000000000000036012305451330014262 0ustar # Note: You may want to look at the following page before setting the ULIMIT. # http://wiki.nginx.org/CoreModule#worker_rlimit_nofile # Set the ulimit variable if you need defaults to change. # Example: ULIMIT="-n 4096" #ULIMIT="-n 4096" debian/nginx-common.postinst0000644000000000000000000000225213001364114013376 0ustar #!/bin/sh set -e . /usr/share/debconf/confmodule case "$1" in configure) logdir="/var/log/nginx" # Allow local admin to override if ! dpkg-statoverride --list "$logdir" >/dev/null; then chown root:adm $logdir chmod 0755 $logdir fi # Secure default logfiles on fresh installations if [ -z "$2" ]; then access_log="$logdir/access.log" error_log="$logdir/error.log" if [ ! -e "$access_log" ]; then touch "$access_log" chmod 640 "$access_log" chown www-data:adm "$access_log" fi if [ ! -e "$error_log" ]; then touch "$error_log" chmod 640 "$error_log" chown www-data:adm "$error_log" fi fi # If a symlink doesn't exist and can be created, then create it. if [ -z $2 ] && [ ! -e /etc/nginx/sites-enabled/default ] && [ -d /etc/nginx/sites-enabled ] && [ -d /etc/nginx/sites-available ]; then ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/gbp.conf0000644000000000000000000000003712305451330010601 0ustar [DEFAULT] pristine-tar = True debian/nginx-light.lintian-overrides0000644000000000000000000000007512305451330014774 0ustar nginx-light: spelling-error-in-binary usr/sbin/nginx tEH the debian/naxsi-ui-extract0000644000000000000000000000025112305451330012310 0ustar #!/bin/bash cd /usr/share/nginx-naxsi/naxsi-ui/ python nx_extract.py /etc/nginx/naxsi-ui.conf & somepid=$! echo $somepid > /run/nginx-naxsi-ui_extract.pid wait $somepid debian/patches/0000755000000000000000000000000013370361736010626 5ustar debian/patches/CVE-2016-074x-2.patch0000644000000000000000000000445712654405572013524 0ustar Backport of: # HG changeset patch # User Ruslan Ermilov # Date 1453815991 -10800 # Node ID f63dd04c158062d73fcb6aff59124910fa1fae75 # Parent c36482d0a79fe0f2e1467f80ec2fbcd0a2d682c6 Resolver: fixed crashes in timeout handler. If one or more requests were waiting for a response, then after getting a CNAME response, the timeout event on the first request remained active, pointing to the wrong node with an empty rn->waiting list, and that could cause either null pointer dereference or use-after-free memory access if this timeout expired. If several requests were waiting for a response, and the first request terminated (e.g., due to client closing a connection), other requests were left without a timeout and could potentially wait indefinitely. This is fixed by introducing per-request independent timeouts. This change also reverts 954867a2f0a6 and 5004210e8c78. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 08:42:21.289411439 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 08:45:26.503579418 -0500 @@ -510,6 +510,20 @@ if (rn->waiting) { + if (ctx->event == NULL) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + return NGX_ERROR; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; @@ -674,6 +688,18 @@ if (rn->waiting) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + return NGX_ERROR; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; debian/patches/CVE-2016-074x-1.patch0000644000000000000000000000161512654402435013507 0ustar Backport of: # HG changeset patch # User Roman Arutyunyan # Date 1453815978 -10800 # Node ID c36482d0a79fe0f2e1467f80ec2fbcd0a2d682c6 # Parent e9a4531a2a5dabb9bee93cb8b41f24b8aeeba504 Resolver: fixed possible segmentation fault on DNS format error. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 08:42:21.289411439 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 08:45:26.503579418 -0500 @@ -1060,7 +1086,7 @@ times = 0; for (q = ngx_queue_head(&r->name_resend_queue); - q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; + q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100; q = ngx_queue_next(q)) { rn = ngx_queue_data(q, ngx_resolver_node_t, queue); debian/patches/series0000644000000000000000000000047313370361732012043 0ustar perl-use-dpkg-buildflags.patch guard-use-of-deprecated-openssl-definition.patch ubuntu-branding.patch CVE-2014-3616.patch CVE-2016-074x-1.patch CVE-2016-074x-2.patch CVE-2016-074x-3.patch CVE-2016-074x-4.patch CVE-2016-074x-5.patch CVE-2016-074x-6.patch cve-2016-4450.patch CVE-2017-7529.patch CVE-2018-16845.patch debian/patches/perl-use-dpkg-buildflags.patch0000644000000000000000000000131112305451337016426 0ustar Description: Use linker flags from environment for perl (dpkg-buildflags). Necessary for hardening flags. Author: Christos Trochalakis --- a/src/http/modules/perl/Makefile.PL +++ b/src/http/modules/perl/Makefile.PL @@ -3,6 +3,7 @@ # Copyright (C) Nginx, Inc. use 5.006001; +use Config; use ExtUtils::MakeMaker; WriteMakefile( @@ -14,6 +15,9 @@ WriteMakefile( AUTHOR => 'Igor Sysoev', CCFLAGS => "$ENV{NGX_PM_CFLAGS}", + # Pass link hardening flags + # $Config{lddlflags} is the default + LDDLFLAGS => "$Config{lddlflags} $ENV{DEBIAN_NGINX_PERL_LDFLAGS}", OPTIMIZE => '-O', INC => join(" ", map { debian/patches/CVE-2017-7529.patch0000644000000000000000000000144213131371363013250 0ustar Origin: http://nginx.org/download/patch.2017.ranges.txt Subject: Fix integer overflow in the range filter Upstream: yes CVE-2017-7529 --- src/http/modules/ngx_http_range_filter_module.c | 4 ++++ 1 file changed, 4 insertions(+) Index: b/src/http/modules/ngx_http_range_filter_module.c =================================================================== --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -377,6 +377,10 @@ ngx_http_range_parse(ngx_http_request_t range->start = start; range->end = end; + if (size > NGX_MAX_OFF_T_VALUE - (end - start)) { + return NGX_HTTP_RANGE_NOT_SATISFIABLE; + } + size += end - start; if (ranges-- == 0) { debian/patches/CVE-2014-3616.patch0000644000000000000000000000775212406302615013246 0ustar Description: fix incorrect cached SSL session reuse Origin: backport, http://trac.nginx.org/nginx/changeset/1ee1db30c9b96e9e43e85ab0bfba42140af24966/nginx Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/nginx/+bug/1370478 Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=761940 Index: nginx-1.4.6/src/event/ngx_event_openssl.c =================================================================== --- nginx-1.4.6.orig/src/event/ngx_event_openssl.c 2014-09-17 08:51:37.000000000 -0400 +++ nginx-1.4.6/src/event/ngx_event_openssl.c 2014-09-17 08:51:56.606172085 -0400 @@ -27,6 +27,8 @@ ngx_err_t err, char *text); static void ngx_ssl_clear_error(ngx_log_t *log); +static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl, + ngx_str_t *sess_ctx); ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess); @@ -1666,13 +1668,15 @@ { long cache_mode; + if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) { + return NGX_ERROR; + } + if (builtin_session_cache == NGX_SSL_NO_SCACHE) { SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF); return NGX_OK; } - SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len); - if (builtin_session_cache == NGX_SSL_NONE_SCACHE) { /* @@ -1731,6 +1735,96 @@ } +static ngx_int_t +ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx) +{ + int n, i; + X509 *cert; + X509_NAME *name; + EVP_MD_CTX md; + unsigned int len; + STACK_OF(X509_NAME) *list; + u_char buf[EVP_MAX_MD_SIZE]; + + /* + * Session ID context is set based on the string provided, + * the server certificate, and the client CA list. + */ + + EVP_MD_CTX_init(&md); + + if (EVP_DigestInit_ex(&md, EVP_sha1(), NULL) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestInit_ex() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, sess_ctx->data, sess_ctx->len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index); + + if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_digest() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + list = SSL_CTX_get_client_CA_list(ssl->ctx); + + if (list != NULL) { + n = sk_X509_NAME_num(list); + + for (i = 0; i < n; i++) { + name = sk_X509_NAME_value(list, i); + + if (X509_NAME_digest(name, EVP_sha1(), buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_NAME_digest() failed"); + goto failed; + } + + if (EVP_DigestUpdate(&md, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + } + } + + if (EVP_DigestFinal_ex(&md, buf, &len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "EVP_DigestUpdate() failed"); + goto failed; + } + + EVP_MD_CTX_cleanup(&md); + + if (SSL_CTX_set_session_id_context(ssl->ctx, buf, len) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_session_id_context() failed"); + return NGX_ERROR; + } + + return NGX_OK; + +failed: + + EVP_MD_CTX_cleanup(&md); + + return NGX_ERROR; +} + + ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) { debian/patches/CVE-2016-074x-5.patch0000644000000000000000000001500312654413703013507 0ustar Backport of: # HG changeset patch # User Roman Arutyunyan # Date 1453816019 -10800 # Node ID dac6eda40475f08b7372159d78dad1e13cd5bc7f # Parent 5557bf31e25da68d5cda19dbc91d86f47430df1f Resolver: fixed use-after-free memory accesses with CNAME. When several requests were waiting for a response, then after getting a CNAME response only the last request's context had the name updated. Contexts of other requests had the wrong name. This name was used by ngx_resolve_name_done() to find the node to remove the request context from. When the name was wrong, the request could not be properly cancelled, its context was freed but stayed linked to the node's waiting list. This happened e.g. when the first request was aborted or timed out before the resolving completed. When it completed, this triggered a use-after-free memory access by calling ctx->handler of already freed request context. The bug manifests itself by "could not cancel resolving" alerts in error_log. When a request was responded with a CNAME, the request context kept the pointer to the original node's rn->u.cname. If the original node expired before the resolving timed out or completed with an error, this would trigger a use-after-free memory access via ctx->name in ctx->handler(). The fix is to keep ctx->name unmodified. The name from context is no longer used by ngx_resolve_name_done(). Instead, we now keep the pointer to resolver node to which this request is linked. Keeping the original name intact also improves logging. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 09:16:30.321863983 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 09:16:30.289863602 -0500 @@ -54,7 +54,7 @@ static void ngx_resolver_cleanup(void *data); static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree); static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, - ngx_resolver_ctx_t *ctx); + ngx_resolver_ctx_t *ctx, ngx_str_t *name); static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue); static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, @@ -320,7 +320,7 @@ /* lock name mutex */ - rc = ngx_resolve_name_locked(r, ctx); + rc = ngx_resolve_name_locked(r, ctx, &ctx->name); if (rc == NGX_OK) { return NGX_OK; @@ -347,7 +347,6 @@ void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) { - uint32_t hash; ngx_resolver_t *r; ngx_resolver_ctx_t *w, **p; ngx_resolver_node_t *rn; @@ -369,9 +368,7 @@ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { - hash = ngx_crc32_short(ctx->name.data, ctx->name.len); - - rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + rn = ctx->node; if (rn) { p = &rn->waiting; @@ -414,18 +411,20 @@ /* NGX_RESOLVE_A only */ static ngx_int_t -ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) +ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx, + ngx_str_t *name) { uint32_t hash; in_addr_t addr, *addrs; ngx_int_t rc; + ngx_str_t cname; ngx_uint_t naddrs; ngx_resolver_ctx_t *next, *last; ngx_resolver_node_t *rn; - hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + hash = ngx_crc32_short(name->data, name->len); - rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + rn = ngx_resolver_lookup_name(r, name, hash); if (rn) { @@ -488,10 +487,10 @@ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { - ctx->name.len = rn->cnlen; - ctx->name.data = rn->u.cname; + cname.len = rn->cnlen; + cname.data = rn->u.cname; - return ngx_resolve_name_locked(r, ctx); + return ngx_resolve_name_locked(r, ctx, &cname); } last->next = rn->waiting; @@ -531,6 +530,11 @@ rn->waiting = ctx; ctx->state = NGX_AGAIN; + do { + ctx->node = rn; + ctx = ctx->next; + } while (ctx); + return NGX_AGAIN; } @@ -560,20 +564,20 @@ return NGX_ERROR; } - rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); + rn->name = ngx_resolver_dup(r, name->data, name->len); if (rn->name == NULL) { ngx_resolver_free(r, rn); return NGX_ERROR; } rn->node.key = hash; - rn->nlen = (u_short) ctx->name.len; + rn->nlen = (u_short) name->len; rn->query = NULL; ngx_rbtree_insert(&r->name_rbtree, &rn->node); } - rc = ngx_resolver_create_name_query(r, rn, &ctx->name); + rc = ngx_resolver_create_name_query(r, rn, name); if (rc == NGX_ERROR) { goto failed; @@ -631,6 +635,11 @@ ctx->state = NGX_AGAIN; + do { + ctx->node = rn; + ctx = ctx->next; + } while (ctx); + return NGX_AGAIN; failed: @@ -712,6 +721,7 @@ ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; + ctx->node = rn; /* unlock addr mutex */ @@ -773,6 +783,7 @@ /* unlock addr mutex */ ctx->state = NGX_AGAIN; + ctx->node = rn; return NGX_OK; @@ -821,7 +831,7 @@ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { - rn = ngx_resolver_lookup_addr(r, ctx->addr); + rn = ctx->node; if (rn) { p = &rn->waiting; @@ -1483,9 +1493,12 @@ rn->waiting = NULL; if (ctx) { - ctx->name = name; - (void) ngx_resolve_name_locked(r, ctx); + for (next = ctx; next; next = next->next) { + next->node = NULL; + } + + (void) ngx_resolve_name_locked(r, ctx, &name); } ngx_resolver_free(r, rn->query); Index: nginx-1.4.6/src/core/ngx_resolver.h =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.h 2016-02-03 09:16:30.321863983 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.h 2016-02-03 09:16:30.289863602 -0500 @@ -131,6 +131,8 @@ ngx_uint_t quick; /* unsigned quick:1; */ ngx_uint_t recursion; ngx_event_t *event; + + ngx_resolver_node_t *node; }; debian/patches/ubuntu-branding.patch0000644000000000000000000000105212307121073014734 0ustar Description: Add Ubuntu token to NGINX_VER Author: Adam Conrad Forwarded: not-needed Last-Update: 2014-03-09 Index: b/src/core/nginx.h =================================================================== --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -11,7 +11,7 @@ #define nginx_version 1004006 #define NGINX_VERSION "1.4.6" -#define NGINX_VER "nginx/" NGINX_VERSION +#define NGINX_VER "nginx/" NGINX_VERSION " (Ubuntu)" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" debian/patches/CVE-2016-074x-4.patch0000644000000000000000000001054612654420433013513 0ustar Backport of: # HG changeset patch # User Roman Arutyunyan # Date 1453816008 -10800 # Node ID 5557bf31e25da68d5cda19dbc91d86f47430df1f # Parent 838946300825379ccdd3acfb131cf66d6ae3cb85 Resolver: changed the ngx_resolver_create_*_query() arguments. No functional changes. This is needed by the following change. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 10:43:42.220407640 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 10:47:36.059502393 -0500 @@ -59,10 +59,10 @@ ngx_queue_t *queue); static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn); -static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn, - ngx_resolver_ctx_t *ctx); -static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, - ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn, ngx_str_t *name); +static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn, in_addr_t *addr); static void ngx_resolver_resend_handler(ngx_event_t *ev); static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue); @@ -573,7 +573,7 @@ ngx_rbtree_insert(&r->name_rbtree, &rn->node); } - rc = ngx_resolver_create_name_query(rn, ctx); + rc = ngx_resolver_create_name_query(r, rn, &ctx->name); if (rc == NGX_ERROR) { goto failed; @@ -735,7 +735,7 @@ ngx_rbtree_insert(&r->addr_rbtree, &rn->node); } - if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { + if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) { goto failed; } @@ -1829,7 +1829,8 @@ static ngx_int_t -ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +ngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, + ngx_str_t *name) { u_char *p, *s; size_t len, nlen; @@ -1837,11 +1838,11 @@ ngx_resolver_qs_t *qs; ngx_resolver_query_t *query; - nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1; + nlen = name->len ? (1 + name->len + 1) : 1; len = sizeof(ngx_resolver_query_t) + nlen + sizeof(ngx_resolver_qs_t); - p = ngx_resolver_alloc(ctx->resolver, len); + p = ngx_resolver_alloc(r, len); if (p == NULL) { return NGX_ERROR; } @@ -1853,8 +1854,8 @@ ident = ngx_random(); - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, - "resolve: \"%V\" %i", &ctx->name, ident & 0xffff); + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\" A %i", name, ident & 0xffff); query->ident_hi = (u_char) ((ident >> 8) & 0xff); query->ident_lo = (u_char) (ident & 0xff); @@ -1873,7 +1874,7 @@ qs = (ngx_resolver_qs_t *) p; /* query type */ - qs->type_hi = 0; qs->type_lo = (u_char) ctx->type; + qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_A; /* IP query class */ qs->class_hi = 0; qs->class_lo = 1; @@ -1884,11 +1885,11 @@ p--; *p-- = '\0'; - if (ctx->name.len == 0) { + if (name->len == 0) { return NGX_DECLINED; } - for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) { + for (s = name->data + name->len - 1; s >= name->data; s--) { if (*s != '.') { *p = *s; len++; @@ -1918,7 +1919,8 @@ /* AF_INET only */ static ngx_int_t -ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +ngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, + in_addr_t *addr) { u_char *p, *d; size_t len; @@ -1930,7 +1932,7 @@ + sizeof(".255.255.255.255.in-addr.arpa.") - 1 + sizeof(ngx_resolver_qs_t); - p = ngx_resolver_alloc(ctx->resolver, len); + p = ngx_resolver_alloc(r, len); if (p == NULL) { return NGX_ERROR; } @@ -1955,7 +1957,7 @@ p += sizeof(ngx_resolver_query_t); for (n = 0; n < 32; n += 8) { - d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff); + d = ngx_sprintf(&p[1], "%ud", (*addr >> n) & 0xff); *p = (u_char) (d - &p[1]); p = d; } debian/patches/CVE-2016-074x-6.patch0000644000000000000000000000273212654413726013522 0ustar Backport of: # HG changeset patch # User Ruslan Ermilov # Date 1453816034 -10800 # Node ID 93d70d87914c350948ab701cc99569680320e198 # Parent dac6eda40475f08b7372159d78dad1e13cd5bc7f Resolver: limited CNAME recursion. Previously, the recursion was only limited for cached responses. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 10:08:03.375992143 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 10:08:03.371992090 -0500 @@ -1490,11 +1490,30 @@ ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + ngx_resolver_free(r, rn->query); + rn->query = NULL; + ctx = rn->waiting; rn->waiting = NULL; if (ctx) { + if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) { + + /* unlock name mutex */ + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return; + } + for (next = ctx; next; next = next->next) { next->node = NULL; } @@ -1502,9 +1521,6 @@ (void) ngx_resolve_name_locked(r, ctx, &name); } - ngx_resolver_free(r, rn->query); - rn->query = NULL; - return; } debian/patches/CVE-2018-16845.patch0000644000000000000000000000273713370361736013352 0ustar # HG changeset patch # User Roman Arutyunyan # Date 1541510958 -10800 # Node ID e5069816039b75b1653a4c7c2f982a1ca8d9f9af # Parent bddacdaaec9ef174504899f1528155f84bf60e59 Mp4: fixed reading 64-bit atoms. Previously there was no validation for the size of a 64-bit atom in an mp4 file. This could lead to a CPU hog when the size is 0, or various other problems due to integer underflow when calculating atom data size, including segmentation fault or worker process memory disclosure. Index: nginx-1.4.6/src/http/modules/ngx_http_mp4_module.c =================================================================== --- nginx-1.4.6.orig/src/http/modules/ngx_http_mp4_module.c 2018-11-06 13:56:28.082277245 -0500 +++ nginx-1.4.6/src/http/modules/ngx_http_mp4_module.c 2018-11-06 13:56:28.078277238 -0500 @@ -836,6 +836,13 @@ ngx_http_mp4_read_atom(ngx_http_mp4_file atom_size = ngx_mp4_get_64value(atom_header + 8); atom_header_size = sizeof(ngx_mp4_atom_header64_t); + if (atom_size < sizeof(ngx_mp4_atom_header64_t)) { + ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, + "\"%s\" mp4 atom is too small:%uL", + mp4->file.name.data, atom_size); + return NGX_ERROR; + } + } else { ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, "\"%s\" mp4 atom is too small:%uL", debian/patches/guard-use-of-deprecated-openssl-definition.patch0000644000000000000000000000144712305451337022052 0ustar Description: guard use of SSL_OP_MSIE_SSLV2_RSA_PADDING This option had no effect since 0.9.7h / 0.9.8b and it was removed in recent OpenSSL Author: Piotr Sikora Origin: http://trac.nginx.org/nginx/changeset/a73678f5f96ffead0b616b2c03dfcfd5445d443b/nginx Index: src/event/ngx_event_openssl.c =================================================================== --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -186,6 +186,8 @@ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); +#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */ SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING); +#endif SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); debian/patches/cve-2016-4450.patch0000644000000000000000000000156312724041342013377 0ustar Description: Fix NULL Pointer Dereference in CVE-2016-4450 This patch fixes a NULL pointer dereference while writing client request body as described in CVE-2016-4450. Origin: upstream, http://nginx.org/download/patch.2016.write2.txt Bug-Ubuntu: https://bugs.launchpad.net/bugs/1587577 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ Index: b/src/os/unix/ngx_files.c =================================================================== --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -183,6 +183,12 @@ /* create the iovec and coalesce the neighbouring bufs */ while (cl && vec.nelts < IOV_MAX) { + + if (ngx_buf_special(cl->buf)) { + cl = cl->next; + continue; + } + if (prev == cl->buf->pos) { iov->iov_len += cl->buf->last - cl->buf->pos; debian/patches/CVE-2016-074x-3.patch0000644000000000000000000000447712654402702013517 0ustar Backport of: # HG changeset patch # User Ruslan Ermilov # Date 1453815998 -10800 # Node ID 838946300825379ccdd3acfb131cf66d6ae3cb85 # Parent f63dd04c158062d73fcb6aff59124910fa1fae75 Resolver: fixed CNAME processing for several requests. When several requests were waiting for a response, then after getting a CNAME response only the last request was properly processed, while others were left waiting. Index: nginx-1.4.6/src/core/ngx_resolver.c =================================================================== --- nginx-1.4.6.orig/src/core/ngx_resolver.c 2016-02-03 08:49:42.798640289 -0500 +++ nginx-1.4.6/src/core/ngx_resolver.c 2016-02-03 08:50:33.839247359 -0500 @@ -420,7 +420,7 @@ in_addr_t addr, *addrs; ngx_int_t rc; ngx_uint_t naddrs; - ngx_resolver_ctx_t *next; + ngx_resolver_ctx_t *next, *last; ngx_resolver_node_t *rn; hash = ngx_crc32_short(ctx->name.data, ctx->name.len); @@ -429,6 +429,9 @@ if (rn) { + /* ctx can be a list after NGX_RESOLVE_CNAME */ + for (last = ctx; last->next; last = last->next); + if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); @@ -457,7 +460,7 @@ addrs = NULL; } - ctx->next = rn->waiting; + last->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ @@ -491,7 +494,7 @@ return ngx_resolve_name_locked(r, ctx); } - ctx->next = rn->waiting; + last->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ @@ -524,7 +527,7 @@ ngx_add_timer(ctx->event, ctx->timeout); } - ctx->next = rn->waiting; + last->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; @@ -583,8 +586,14 @@ ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); - ctx->state = NGX_RESOLVE_NXDOMAIN; - ctx->handler(ctx); + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); return NGX_OK; } debian/nginx-core.prerm0000644000000000000000000000050412310175122012277 0ustar #!/bin/sh set -e case "$1" in remove|remove-in-favour|deconfigure|deconfigure-in-favour) if [ -x /etc/init.d/nginx ]; then invoke-rc.d nginx stop || exit $? fi ;; upgrade|failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-extras.install0000644000000000000000000000050312305451330013177 0ustar debian/build-extras/objs/nginx usr/sbin debian/build-extras/objs/src/http/modules/perl/blib/arch/auto/nginx/nginx.bs usr/lib/perl5/auto/nginx debian/build-extras/objs/src/http/modules/perl/blib/arch/auto/nginx/nginx.so usr/lib/perl5/auto/nginx debian/build-extras/objs/src/http/modules/perl/blib/lib/nginx.pm usr/lib/perl5 debian/nginx-common.manpages0000644000000000000000000000001712305451330013306 0ustar debian/nginx.1 debian/nginx-naxsi-ui.preinst0000644000000000000000000000050712305451330013450 0ustar #!/bin/sh set -e case "$1" in upgrade) if dpkg --compare-versions "$2" lt 1.4.1; then mv /etc/nginx/naxsi-ui.conf /etc/nginx/naxsi-ui.conf.before_upgrade_to_1.4.1 fi ;; install|abort-upgrade) ;; *) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/upstream/0000755000000000000000000000000012305451341011024 5ustar debian/upstream/signing-key.asc0000644000000000000000000000373112305375534013755 0ustar -----BEGIN PGP PUBLIC KEY BLOCK----- mQENBE7SKu8BCADQo6x4ZQfAcPlJMLmL8zBEBUS6GyKMMMDtrTh3Yaq481HB54oR 0cpKL05Ff9upjrIzLD5TJUCzYYM9GQOhguDUP8+ZU9JpSz3yO2TvH7WBbUZ8FADf hblmmUBLNgOWgLo3W+FYhl3mz1GFS2Fvid6Tfn02L8CBAj7jxbjL1Qj/OA/WmLLc m6BMTqI7IBlYW2vyIOIHasISGiAwZfp0ucMeXXvTtt14LGa8qXVcFnJTdwbf03AS ljhYrQnKnpl3VpDAoQt8C68YCwjaNJW59hKqWB+XeIJ9CW98+EOAxLAFszSyGanp rCqPd0numj9TIddjcRkTA/ZbmCWK+xjpVBGXABEBAAG0IU1heGltIERvdW5pbiA8 bWRvdW5pbkBtZG91bmluLnJ1PohGBBARAgAGBQJO01Y/AAoJEOzw6QssFyCDVyQA n3qwTZlcZgyyzWu9Cs8gJ0CXREaSAJ92QjGLT9DijTcbB+q9OS/nl16Z/IhGBBAR AgAGBQJO02JDAAoJEKk3YTmlJMU+P64AnjCKEXFelSVMtgefJk3+vpyt3QX1AKCH 9M3MbTWPeDUL+MpULlfdyfvjj4heBBARCAAGBQJRCTwgAAoJEFGFCWhsfl6CzF0B AJsQ3DJbtGcZ+0VIcM2a06RRQfBvIHqm1A/1WSYmObLGAP90lxWlNjSugvUUlqTk YEEgRTGozgixSyMWGJrNwqgMYokBOAQTAQIAIgUCTtIq7wIbAwYLCQgHAwIGFQgC CQoLBBYCAwECHgECF4AACgkQUgqZk6HAUvj+iwf/b4FS6zVzJ5T0v1vcQGD4ZzXe D5xMC4BJW414wVMU15rfX7aCdtoCYBNiApPxEd7SwiyxWRhRA9bikUq87JEgmnyV 0iYbHZvCvc1jOkx4WR7E45t1Mi29KBoPaFXA9X5adZkYcOQLDxa2Z8m6LGXnlF6N tJkxQ8APrjZsdrbDvo3HxU9muPcq49ydzhgwfLwpUs11LYkwB0An9WRPuv3jporZ /XgI6RfPMZ5NIx+FRRCjn6DnfHboY9rNF6NzrOReJRBhXCi6I+KkHHEnMoyg8XET 9lVkfHTOl81aIZqrAloX3/00TkYWyM2zO9oYpOg6eUFCX/Lw4MJZsTcT5EKVxLkB DQRO0irvAQgA0LjCc8S6oZzjiap2MjRNhRFA5BYjXZRZBdKF2VP74avt2/RELq8G W0n7JWmKn6vvrXabEGLyfkCngAhTq9tJ/K7LPx/bmlO5+jboO/1inH2BTtLiHjAX vicXZk3oaZt2Sotx5mMI3yzpFQRVqZXsi0LpUTPJEh3oS8IdYRjslQh1A7P5hfCZ wtzwb/hKm8upODe/ITUMuXeWfLuQj/uEU6wMzmfMHb+jlYMWtb+v98aJa2FODeKP mWCXLa7bliXp1SSeBOEfIgEAmjM6QGlDx5sZhr2Ss2xSPRdZ8DqD7oiRVzmstX1Y oxEzC0yXfaefC7SgM0nMnaTvYEOYJ9CH3wARAQABiQEfBBgBAgAJBQJO0irvAhsM AAoJEFIKmZOhwFL4844H/jo8icCcS6eOWvnen7lg0FcCo1fIm4wW3tEmkQdchSHE CJDq7pgTloN65pwB5tBoT47cyYNZA9eTfJVgRc74q5cexKOYrMC3KuAqWbwqXhkV s0nkWxnOIidTHSXvBZfDFA4Idwte94Thrzf8Pn8UESudTiqrWoCBXk2UyVsl03gJ blSJAeJGYPPeo+Yj6m63OWe2+/S2VTgmbPS/RObn0Aeg7yuff0n5+ytEt2KL51gO QE2uIxTCawHr12PsllPkbqPk/PagIttfEJqn9b0CrqPC3HREePb2aMJ/Ctw/76CO wn0mtXeIXLCTvBmznXfaMKllsqbsy2nCJ2P2uJjOntw= =ISka -----END PGP PUBLIC KEY BLOCK----- debian/nginx-naxsi-ui.postinst0000644000000000000000000000051712305451330013650 0ustar #!/bin/sh set -e case "$1" in configure) if dpkg --compare-versions "$2" lt 1.4.1; then cp /etc/nginx/naxsi-ui.conf.1.4.1 /etc/nginx/naxsi-ui.conf fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-doc.examples0000644000000000000000000000002712305451330012607 0ustar debian/help/examples/* debian/nginx-common.NEWS0000644000000000000000000000761613001370130012273 0ustar nginx-common (1.4.6-1ubuntu3.6) trusty-security; urgency=high In order to secure nginx against privilege escalation attacks, we are changing the way log file owners & permissions are handled so that www-data is not allowed to symlink a logfile. /var/log/nginx is now owned by root:adm and its permissions are changed to 0755. The package checks for such symlinks on existing installations and informs the admin using debconf. That unfortunately may come at a cost in terms of privacy. /var/log/nginx is now world-readable, and nginx hardcodes permissions of non-existing logs to 0644. On systems running logrotate log files are private after the first logrotate run, since the new log files are created with 0640 permissions. -- Christos Trochalakis Tue, 04 Oct 2016 15:20:33 +0300 nginx-common (1.4.4-2) unstable; urgency=low Per CVE-2013-0337 (bug #701112), we are changing /var/log/nginx permissions to root:adm 750. If you have manipulated these permissions in any way, you can add a dpkg-statoverride entry and the directory will not be touched. You also have to manually set the permissions once, as dpkg doesn't do that automatically for directories. e.g. chown root:adm /var/log/nginx chmod 0755 /var/log/nginx dpkg-statoverride --add root adm 0755 /var/log/nginx -- Michael Lustfield Sun, 24 Nov 2013 15:59:52 -0600 nginx (1.4.1-2) unstable; urgency=medium Started with nginx 1.4.1-2 upload, nginx-naxsi-ui switched backend from MySQL to SQLite. This was mostly caused by the future removal of MySQL support in naxsi-ui. Thus, the nginx maintainers decided to switch from MySQL to SQLite. All data contained in the MySQL database is not affected by this switch but you need to start with a clean SQLite database to ensure nginx-naxsi-ui reliability. -- Cyril Lavier Wed, 5 Jun 2013 09:45:03 +0200 nginx (0.8.53-1) unstable; urgency=low As stated by upstream, the 0.7.x branch is consedered legacy and 0.8.x will be the new stable branch. For this reason, the nginx maintainers decided to upload 0.8.53 to unstable. -- Kartik Mistry Fri, 26 Nov 2010 19:42:09 +0530 nginx (0.7.59-1) unstable; urgency=low As stated by upstream, the 0.6.x branch is consedered legacy and 0.7.x will be the new stable branch. For this reason, the nginx maintainers decided to upload 0.7.59 to unstable. Should you get the following error while starting nginx: could not build the server_names_hash, you should increase server_names_hash_bucket_size: 32 Please add the following parameter to your nginx.conf: server_names_hash_bucket_size 100; Where 100 is the size of your server names hash bucket. For more information about this option, please read the following resources: http://wiki.nginx.org/NginxHttpCoreModule#server_names_hash_bucket_size http://thread.gmane.org/gmane.comp.web.nginx.english/820/focus=821 http://thread.gmane.org/gmane.comp.web.nginx.english/985/focus=989 -- Fabio Tranchitella Sun, 31 May 2009 18:30:10 +0200 nginx (0.6.30-2) unstable; urgency=low As of May 4th., nginx 0.5.x branch is considered legacy and 0.6.x will be the new stable branch. The announcement was made by Igor Sysoev when releasing the last 0.5.x version, nginx 0.5.36. Debian, the universal operating system, has provided binary packages for both 0.5 and 0.6 branches in unstable and experimental, and will now offer only 0.6 packages in the unstable distribution, starting with the 0.6.30-1 package. In the future, Debian will also provide experimental packages for the next testing branch of nginx, at the moment upstream announces it. Should you have any problem with nginx in Debian, please file a bug in the Debian Bug Tracking System. -- Fabio Tranchitella Mon, 12 May 2008 14:24:53 +0200 debian/nginx-common.preinst0000644000000000000000000000161112305451330013200 0ustar #!/bin/sh set -e case "$1" in install) # If we are doing a fresh install, then these files are no longer needed. # They were around for a very short time and are best simply removed. rm -f /etc/logrotate.d/nginx-full rm -f /etc/logrotate.d/nginx-light rm -f /etc/logrotate.d/nginx-extras rm -f /etc/logrotate.d/nginx-common ;; upgrade) # If this is an upgrade, then they might have the UFW profile in the wrong spot. if [ -d /etc/ufw/applications.d/nginx ]; then rm -f /etc/ufw/applications.d/nginx/ufw.profile rmdir /etc/ufw/applications.d/nginx fi rm -f /etc/logrotate.d/nginx-full rm -f /etc/logrotate.d/nginx-light rm -f /etc/logrotate.d/nginx-extras rm -f /etc/logrotate.d/nginx-common ;; abort-upgrade) ;; *) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-core.lintian-overrides0000644000000000000000000000007412310176102014611 0ustar nginx-core: spelling-error-in-binary usr/sbin/nginx tEH the debian/nginx-light.install0000644000000000000000000000004712305451330013003 0ustar debian/build-light/objs/nginx usr/sbin debian/nginx.10000644000000000000000000000233512305451330010372 0ustar .TH "nginx" "1" "" "" "" .SH "NAME" nginx \- small, powerful, scalable web/proxy server .SH "SYNOPSIS" \fBnginx\fR [\fIoptions\fR] <\fIconfiguration file\fR> .SH "DESCRIPTION" .PP \fBNginx\fR ("engine X") is a high\-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back\-end HTTP or mail servers. .SH "OPTIONS" .TP A summary of options is included below: .TP \fB\-?\fR,\fB\-h\fR Show this help. .TP \fB\-v\fR Show version and exit. .TP \fB\-V\fR Show version and configure options then exit. .TP \fB\-s\fR \fIsignal\fR Send signal to a master process: stop, quit, reopen, reload. .TP \fB\-p\fR \fIprefix\fR Set prefix path. .TP \fB\-g\fR \fIdirectives\fR Set global directives out of configuration file. .TP \fB\-c\fR <\fIconfiguration file\fR> Specifies a particular configuration file for nginx to load. .TP \fB\-t\fR Tests nginx configuration and exit. .TP .SH "SEE ALSO" Website: .TP .SH "AUTHORS" \fBnginx\fR was written by Igor Sysoev . .TP This manual page was written by Jose Parrella and Kartik Mistry , for the Debian project (but may be used by others). debian/compat0000644000000000000000000000000212305451330010360 0ustar 9 debian/nginx-extras.lintian-overrides0000644000000000000000000000007612305451330015174 0ustar nginx-extras: spelling-error-in-binary usr/sbin/nginx tEH the debian/nginx-full.install0000644000000000000000000000004612305451330012635 0ustar debian/build-full/objs/nginx usr/sbin debian/nginx-common.nginx.logrotate0000644000000000000000000000053212305451330014637 0ustar /var/log/nginx/*.log { weekly missingok rotate 52 compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi \ endscript postrotate [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid` endscript } debian/nginx-common.install0000644000000000000000000000013512305451330013162 0ustar debian/conf/* etc/nginx debian/ufw/nginx etc/ufw/applications.d html/* usr/share/nginx/html/ debian/nginx-common.dirs0000644000000000000000000000022012305451330012450 0ustar etc/nginx etc/nginx/sites-available etc/nginx/sites-enabled etc/nginx/conf.d etc/ufw/applications.d usr/share/nginx var/log/nginx var/lib/nginx debian/copyright0000644000000000000000000001203412305451330011115 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Nginx Source: http://nginx.net/ Upstream-Contact: Igor Sysoev Files: * Copyright: 2002-2013 Igor Sysoev 2011-2013 Nginx, Inc. License: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: contrib/geo2nginx.pl Copyright: 2005, Andrei Nigmatulin License: Same as upstream license. Files: debian/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c Copyright: 2007, Igor Sysoev, Grzegorz Nosek License: Same as upstream license. Files: debian/modules/nginx-development-kit/* Copyright: Marcus Clyne License: Same as upstream license. Files: debian/modules/nginx-http-push/* Copyright: 2009, Leo Ponomarev License: Same as upstream license. Files: debian/modules/nginx-upload-progress/ngx_http_uploadprogress_module.c Copyright: 2007, Brice Figureau 2002-2007, Igor Sysoev License: Same as upstream license. Files: debian/modules/agentzh-chunkin-nginx-module-*/* debian/modules/agentzh-headers-more-nginx-module-*/* Copyright: 2009, Taobao Inc., Alibaba Group . 2009-2011, Zhang "agentzh" Yichun (章亦春) . License: Same as upstream license. Files: debian/modules/nginx-auth-pam/* Copyright: 2008-2010, Sergio Talens Oliag License: Same as upstream license. Files: debian/modules/nginx-cache-purge Copyright: 2009-2011, FRiCKLE , 2009-2011, Piotr Sikora License: Same as upstream license. Files: debian/modules/nginx-dav-ext-module Copyright: 2012, Arutyunyan Roman License: Same as upstream license. Files: debian/modules/naxsi Copyright: 2012, Thibault Koechlin License: GPL-2+ with OpenSSL exception Files: debian/modules/naxsi/contrib/fp-reporter/fp-reporter.php Copyright: 2011, Thibault 'bui' Koechlin, Didier Conchaudron License: GPL-2+ with OpenSSL exception License: GPL-2+ with OpenSSL exception This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. . You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU General Public Licenses can be found in "/usr/share/common-licenses/GPL-2" and "/usr/share/common-licenses/GPL-3" Files: debian/* Copyright: 2006-2010, Jose Parrella , Fabio Tranchitella , 2009-2013, Kartik Mistry , 2012-2013, Michael Lustfield , Dmitry E. Oboukhov , Cyril Lavier . License: Same as upstream license. debian/modules/0000755000000000000000000000000012305451341010634 5ustar debian/modules/nginx-http-push/0000755000000000000000000000000012305451341013711 5ustar debian/modules/nginx-http-push/tests/0000755000000000000000000000000012466164635015072 5ustar debian/modules/nginx-http-push/tests/test.rb0000644000000000000000000000245512305451331016364 0ustar require 'rubygems' require 'em-http' def subscribe(num, opts) puts "Start listener " + num.to_s listener = EventMachine::HttpRequest.new('http://127.0.0.1:8082/broadcast/sub?channel='+ opts[:channel]).get :head => opts[:head] listener.callback { # print recieved message, re-subscribe to channel with # the last-modified header to avoid duplicate messages puts "Listener " + num.to_s + " received: " + listener.response + "\n" modified = listener.response_header['LAST_MODIFIED'] subscribe(num, {:channel => opts[:channel], :head => {'If-Modified-Since' => modified}}) } end EventMachine.run { channel = "pub" numofem = 100 # Publish new message every 5 seconds EM.add_periodic_timer(5) do time = Time.now i = 0 numofem.times do i += 1 channel = i.to_s publisher = EventMachine::HttpRequest.new('http://127.0.0.1:8082/broadcast/pub?channel='+channel).post :body => "Hello @ #{time}" publisher.callback { #puts "Published message @ #{time}" #puts "Response code: " + publisher.response_header.status.to_s #puts "Headers: " + publisher.response_header.inspect #puts "Body: \n" + publisher.response #puts "\n" } end end i = 0 numofem.times do i += 1 subscribe(i, :channel => i.to_s) end } debian/modules/nginx-http-push/tests/nginx-cachemanager.conf0000644000000000000000000000270512466164635021464 0ustar worker_processes 4; #debug_points stop; #error_log /dev/stderr debug; error_log /dev/stderr notice; #error_log err.log notice; pid /tmp/nginx-debug.pid; events { worker_connections 90000; accept_mutex on; } http { access_log /dev/stdout; proxy_cache_path /tmp levels=1:2 keys_zone=cache:1m; server { listen 8000; location / { proxy_cache cache; } } server { listen 8082; # root ./; location ~ /pub/(\w+)$ { set $push_channel_id $1; push_publisher; push_min_message_buffer_length 5; push_max_message_buffer_length 20; push_message_timeout 5s; push_channel_group test; } location ~ /sub/broadcast/(\w+)$ { push_subscriber; push_channel_group test; set $push_channel_id $1; push_subscriber_concurrency broadcast; } location ~ /sub/first/(\w+)$ { push_subscriber; push_channel_group test; set $push_channel_id $1; push_subscriber_concurrency first; } location ~ /sub/last/(\w+)$ { push_subscriber; push_channel_group test; set $push_channel_id $1; push_subscriber_concurrency last; } #authorized channels only -- publishers must create the channel before subscribing location ~ /sub/authorized/(\w+)$ { push_authorized_channels_only on; push_subscriber; push_channel_group test; set $push_channel_id $1; } } } debian/modules/nginx-http-push/tests/test.lua0000644000000000000000000001023612305451331016536 0ustar print("The test 'framework' used here is still a touch wonky. A test failure need not mean that things are actually broken...") require "httpest" --fyi, the test "framework" used here is still wonky. math.randomseed(os.time()) local request=httpest.request local sendurl, listenurl = "http://localhost:8082/broadcast/pub?channel=%s", "http://localhost:8082/broadcast/sub?channel=%s" local function send(channel, message, callback) assert(request{ url=sendurl:format(channel), method="post", data=message, headers={['content-type']="text/foo"}, complete=function(r, status, sock) assert(not status, "fail: " .. (status or "?")) assert(r.status==201 or r.status==202, tostring(r)) if callback then callback(r, status, sock) end end }) end local channeltags = {} local function listen(channel, callback, timeout, headers) if not channeltags[channel] then channeltags[channel] = {} end local s local subscriber_timeout = function() return callback(nil, "timeout", s) end s = request{ url=listenurl:format(channel), method="get", headers = headers or { ['if-none-match']=channeltags[channel]['etag'], ['if-modified-since']=channeltags[channel]['last-modified'] }, complete = function(r, status, s) httpest.killtimer(subscriber_timeout) if not r then channeltags[channel]=nil return callback(nil, status, s) end channeltags[channel].etag=r:getheader("etag") channeltags[channel]['last-modified']=r:getheader("last-modified") if callback then return callback(r, status, s) else return channel end end } httpest.timer(timeout or 500, subscriber_timeout) return s end local function batchsend(channel, times, msg, callback, done) send(channel, type(msg)=="function" and msg() or msg, function(...) if callback then callback(...) end if times>1 then return batchsend(channel, times-1, msg, callback, done) else return done and done() end end) end local function batchlisten(channel, callback, timeout) local function subscriber(r, err, s) local result if r or err then result = callback(r, err, s) end if (not r and not err) or result then return listen(channel, subscriber, timeout) end end return subscriber() end local function shortmsg(base) return (tostring(base)):rep(3) end local function testqueuing(channel, done) local s, i, messages = nil, 0, {} local function subscriber(resp, status, s) if resp then table.insert(messages, 1, resp:getbody()) end if status=="timeout" then httpest.abort_request(s) print(" message buffer length is " .. #messages) for j, v in ipairs(messages) do assert(v==shortmsg(i-j+1), #v .. " " .. #shortmsg(i-j+1)) end return nil end return true end batchsend(channel, 10, function() i=i+1 return shortmsg(i) end, nil, function() return batchlisten(channel, subscriber, 100) end ) end --queuing for i=1, 5 do local channel = math.random() httpest.addtest("queuing " .. i .. "(10 messages)", function() testqueuing(channel) end) end --deleting local channel="deltest" httpest.addtest("delete", function() batchsend(channel, 20, "hey", nil, function() request{ url=sendurl:format(channel), method='delete', complete=function(r,st,s) assert(r.status==200, r.status) request{ url=sendurl:format(channel), method='delete', complete=function(r,st,s) assert(r.status==404) end } end } end) end) --broadcasting local num, reps, observed = 100, 5, 0 for i=0,reps do local channel=math.random() httpest.addtest(('broadcast to %s (%s)'):format(num, channel), function() local msg = math.random() .. "yesyes" for j=1, num do batchlisten(channel, function(resp, status, sock) if resp then httpest.abort_request(sock) assert(resp:getbody()==msg, "unexpected message: " .. resp:getbody() .. " (expected " .. msg) observed = observed + 1 print(observed) return nil end return true end) end httpest.timer(2000, function() send(channel, msg) end) end) end httpest.timer(5000*reps, function() local total = reps*num assert(responses==total, ("expected %d responses, got %d"):format(total, observed)) end) httpest.run()debian/modules/nginx-http-push/tests/nginx.conf0000644000000000000000000000162412305451331017047 0ustar #user nobody; worker_processes 2; debug_points stop; error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; pid logs/nginx.pid; #daemon off; #working_directory core/; events { worker_connections 1000; } http { default_type application/octet-stream; sendfile on; keepalive_timeout 65; push_authorized_channels_only off; server { listen 8082; root /home/leop/sandbox/nginx_push/tests; location /broadcast { location = /broadcast/sub { set $push_channel_id $arg_channel; push_subscriber; push_subscriber_concurrency broadcast; push_channel_group broadcast; } location = /broadcast/pub { set $push_channel_id $arg_channel; push_publisher; push_min_message_buffer_length 5; push_max_message_buffer_length 20; push_message_timeout 5s; push_channel_group broadcast; } } } } debian/modules/nginx-http-push/changelog.txt0000644000000000000000000000431412305451330016401 0ustar 0.692 (Feb. 3 2010) fix: error log reported failed close() for some publisher requests with large messages fix: occasional memory leak during message deletion fix: worker messages intended for dead worker processes were not deleted 0.691 (Feb. 2 2010) fix: server reload (via SIGHUP signal) was failing fix: segfault on messages longer than client_body_buffer_size (thanks wfelipe) change: removed push_min_message_recipients, added push_delete_oldest_received_message 0.69 (Nov. 17 2009) fix: publisher got a 201 Created response even if the channel had no subscribers at the time (should be 202 Accepted) fix: small memory leak after each message broadcast to a channel feature: optional push_max_channel_subscribers setting added fix: first-in concurrency setting wasn't responding to subscribers with a correct status code on conflict fix: reused subscriber connections sometimes failed to receive messages unfeature: no more nginx 0.6 support. not worth the hassle. 0.683 (Nov. 10 2009) change: default max. reserved memory size changed form 16MB to 32 MB change: unused node garbage collection made a little more aggressive (max. 3 unused channels per channel search instead of 1) fix: unused nodes were deleted only on channel id hash collision (very rare) fix: segmentation fault from allocating insufficient memory for interprocess messaging 0.681 (Nov. 6 2009) feature: added push_message_buffer_length setting, which sets push_message_max_buffer_length and push_message_min_buffer_length at once. fix: publisher channel info text/json response now uses double quotes instead of single. fix: interprocess messages were not removed from shared memory correctly, causing weird errors 0.68 (Nov. 5 2009) change: default push_subscriber_concurrency value is now "broadcast" fix: incorrect error messages for invalid push_pubscriber and push_subscriber_concurrency settings change: removed deprecated push_buffer_size and push_queue_messages settings feature: rudimentary content-type negotiation for publisher channel info response. support text/plain, text/json, text/yaml and application/xml (and mimetype equivalents) fix: publisher GET response has HTTP status 0 0.67beta (Nov. 4 2009) and older see git repositorydebian/modules/nginx-http-push/LICENCE0000644000000000000000000000212212305451331014672 0ustar This work is distributed under the MIT Licence. Copyright (c) 2009 Leo Ponomarev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. debian/modules/nginx-http-push/config0000644000000000000000000000062412305451330015101 0ustar ngx_feature="http_push_module" ngx_feature_name= ngx_feature_run=no ngx_feature_incs= ngx_feature_path= ngx_feature_libs= ngx_feature_test= ngx_addon_name=ngx_http_push_module HTTP_MODULES="$HTTP_MODULES ngx_http_push_module" CORE_INCS="$CORE_INCS \ $ngx_addon_dir/src" NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ ${ngx_addon_dir}/src/ngx_http_push_module.c" have=NGX_HTTP_HEADERS . auto/have . auto/featuredebian/modules/nginx-http-push/src/0000755000000000000000000000000012305451341014500 5ustar debian/modules/nginx-http-push/src/ngx_http_push_module_ipc.c0000644000000000000000000002346012305451331021742 0ustar //worker processes of the world, unite. static void ngx_http_push_channel_handler(ngx_event_t *ev); static ngx_inline void ngx_http_push_process_worker_message(void); static void ngx_http_push_ipc_exit_worker(ngx_cycle_t *cycle); #define NGX_CMD_HTTP_PUSH_CHECK_MESSAGES 49 ngx_socket_t ngx_http_push_socketpairs[NGX_MAX_PROCESSES][2]; static ngx_int_t ngx_http_push_init_ipc(ngx_cycle_t *cycle, ngx_int_t workers) { int i, s = 0, on = 1; ngx_int_t last_expected_process = ngx_last_process; /* here's the deal: we have no control over fork()ing, nginx's internal socketpairs are unusable for our purposes (as of nginx 0.8 -- check the code to see why), and the module initialization callbacks occur before any workers are spawned. Rather than futzing around with existing socketpairs, we populate our own socketpairs array. Trouble is, ngx_spawn_process() creates them one-by-one, and we need to do it all at once. So we must guess all the workers' ngx_process_slots in advance. Meaning the spawning logic must be copied to the T. */ for(i=0; i < workers; i++) { while (s < last_expected_process && ngx_processes[s].pid != -1) { //find empty existing slot s++; } //copypasta from os/unix/ngx_process.c (ngx_spawn_process) ngx_socket_t *socks = ngx_http_push_socketpairs[s]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "socketpair() failed on socketpair while initializing push module"); return NGX_ERROR; } if (ngx_nonblocking(socks[0]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_nonblocking_n " failed on socketpair while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } if (ngx_nonblocking(socks[1]) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_nonblocking_n " failed on socketpair while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } if (ioctl(socks[0], FIOASYNC, &on) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "ioctl(FIOASYNC) failed on socketpair while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } if (fcntl(socks[0], F_SETOWN, ngx_pid) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(F_SETOWN) failed on socketpair while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(FD_CLOEXEC) failed on socketpair while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } if (fcntl(socks[1], F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fcntl(FD_CLOEXEC) failed while initializing push module"); ngx_close_channel(socks, cycle->log); return NGX_ERROR; } s++; //NEXT!! } return NGX_OK; } static void ngx_http_push_ipc_exit_worker(ngx_cycle_t *cycle) { ngx_close_channel((ngx_socket_t *) ngx_http_push_socketpairs[ngx_process_slot], cycle->log); } //will be called many times static ngx_int_t ngx_http_push_init_ipc_shm(ngx_int_t workers) { ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) ngx_http_push_shm_zone->shm.addr; ngx_http_push_shm_data_t *d = (ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data; ngx_http_push_worker_msg_t *worker_messages; int i; ngx_shmtx_lock(&shpool->mutex); if(d->ipc!=NULL) { //already initialized... ngx_shmtx_unlock(&shpool->mutex); return NGX_OK; } //initialize worker message queues if((worker_messages = ngx_slab_alloc_locked(shpool, sizeof(*worker_messages)*workers))==NULL) { ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } for(i=0; iipc=worker_messages; ngx_shmtx_unlock(&shpool->mutex); return NGX_OK; } static ngx_int_t ngx_http_push_register_worker_message_handler(ngx_cycle_t *cycle) { if (ngx_add_channel_event(cycle, ngx_http_push_socketpairs[ngx_process_slot][1], NGX_READ_EVENT, ngx_http_push_channel_handler) == NGX_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "failed to register channel handler while initializing push module worker"); return NGX_ERROR; } return NGX_OK; } static void ngx_http_push_channel_handler(ngx_event_t *ev) { //copypasta from os/unix/ngx_process_cycle.c (ngx_channel_handler) ngx_int_t n; ngx_channel_t ch; ngx_connection_t *c; if (ev->timedout) { ev->timedout = 0; return; } c = ev->data; while(1) { n = ngx_read_channel(c->fd, &ch, sizeof(ch), ev->log); if (n == NGX_ERROR) { if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { ngx_del_conn(c, 0); } ngx_close_connection(c); return; } if ((ngx_event_flags & NGX_USE_EVENTPORT_EVENT) && (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR)) { return; } if (n == NGX_AGAIN) { return; } //ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "push module: channel command: %d", ch.command); if (ch.command==NGX_CMD_HTTP_PUSH_CHECK_MESSAGES) { ngx_http_push_process_worker_message(); } } } static ngx_int_t ngx_http_push_alert_worker(ngx_pid_t pid, ngx_int_t slot, ngx_log_t *log) { //seems ch doesn't need to have fd set. odd, but roll with it. pid and process slot also unnecessary. static ngx_channel_t ch = {NGX_CMD_HTTP_PUSH_CHECK_MESSAGES, 0, 0, -1}; return ngx_write_channel(ngx_http_push_socketpairs[slot][0], &ch, sizeof(ngx_channel_t), log); } static ngx_inline void ngx_http_push_process_worker_message(void) { ngx_http_push_worker_msg_t *prev_worker_msg, *worker_msg, *sentinel; const ngx_str_t *status_line = NULL; ngx_http_push_channel_t *channel; ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr; ngx_http_push_subscriber_t *subscriber_sentinel; ngx_shmtx_lock(&shpool->mutex); ngx_http_push_worker_msg_t *worker_messages = ((ngx_http_push_shm_data_t *)ngx_http_push_shm_zone->data)->ipc; ngx_int_t status_code; ngx_http_push_msg_t *msg; sentinel = &worker_messages[ngx_process_slot]; worker_msg = (ngx_http_push_worker_msg_t *)ngx_queue_next(&sentinel->queue); while(worker_msg != sentinel) { if(worker_msg->pid==ngx_pid) { //everything is okay. status_code = worker_msg->status_code; msg = worker_msg->msg; channel = worker_msg->channel; subscriber_sentinel = worker_msg->subscriber_sentinel; if(msg==NULL) { //just a status line, is all //status code only. switch(status_code) { case NGX_HTTP_CONFLICT: status_line=&NGX_HTTP_PUSH_HTTP_STATUS_409; break; case NGX_HTTP_GONE: status_line=&NGX_HTTP_PUSH_HTTP_STATUS_410; break; case 0: ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: worker message contains neither a channel message nor a status code"); //let's let the subscribers know that something went wrong and they might've missed a message status_code = NGX_HTTP_INTERNAL_SERVER_ERROR; //intentional fall-through default: status_line=NULL; } } ngx_shmtx_unlock(&shpool->mutex); ngx_http_push_respond_to_subscribers(channel, subscriber_sentinel, msg, status_code, status_line); ngx_shmtx_lock(&shpool->mutex); } else { //that's quite bad you see. a previous worker died with an undelivered message. //but all its subscribers' connections presumably got canned, too. so it's not so bad after all. ngx_http_push_pid_queue_t *channel_worker_sentinel = &worker_msg->channel->workers_with_subscribers; ngx_http_push_pid_queue_t *channel_worker_cur = channel_worker_sentinel; ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: worker %i intercepted a message intended for another worker process (%i) that probably died", ngx_pid, worker_msg->pid); //delete that invalid sucker. while((channel_worker_cur=(ngx_http_push_pid_queue_t *)ngx_queue_next(&channel_worker_cur->queue))!=channel_worker_sentinel) { if(channel_worker_cur->pid == worker_msg->pid) { ngx_queue_remove(&channel_worker_cur->queue); ngx_slab_free_locked(shpool, channel_worker_cur); break; } } } //It may be worth it to memzero worker_msg for debugging purposes. prev_worker_msg = worker_msg; worker_msg = (ngx_http_push_worker_msg_t *)ngx_queue_next(&worker_msg->queue); ngx_slab_free_locked(shpool, prev_worker_msg); } ngx_queue_init(&sentinel->queue); //reset the worker message sentinel ngx_shmtx_unlock(&shpool->mutex); return; } static ngx_int_t ngx_http_push_send_worker_message(ngx_http_push_channel_t *channel, ngx_http_push_subscriber_t *subscriber_sentinel, ngx_pid_t pid, ngx_int_t worker_slot, ngx_http_push_msg_t *msg, ngx_int_t status_code, ngx_log_t *log) { ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr; ngx_http_push_worker_msg_t *worker_messages = ((ngx_http_push_shm_data_t *)ngx_http_push_shm_zone->data)->ipc; ngx_http_push_worker_msg_t *thisworker_messages = worker_messages + worker_slot; ngx_http_push_worker_msg_t *newmessage; ngx_shmtx_lock(&shpool->mutex); if((newmessage=ngx_slab_alloc_locked(shpool, sizeof(*newmessage)))==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, log, 0, "push module: unable to allocate worker message"); return NGX_ERROR; } ngx_queue_insert_tail(&thisworker_messages->queue, &newmessage->queue); newmessage->msg = msg; newmessage->status_code = status_code; newmessage->pid = pid; newmessage->subscriber_sentinel = subscriber_sentinel; newmessage->channel = channel; ngx_shmtx_unlock(&shpool->mutex); return NGX_OK; } debian/modules/nginx-http-push/src/ngx_http_push_rbtree_util.c0000644000000000000000000001733112305451331022142 0ustar #include #include #include static ngx_http_push_channel_t * ngx_http_push_get_channel(ngx_str_t * id, ngx_log_t * log); static ngx_http_push_channel_t * ngx_http_push_find_channel(ngx_str_t * id, ngx_log_t * log); static ngx_int_t ngx_http_push_delete_channel_locked(ngx_http_push_channel_t *trash); static ngx_http_push_channel_t * ngx_http_push_clean_channel_locked(ngx_http_push_channel_t * channel); static void ngx_rbtree_generic_insert( ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)); static void ngx_http_push_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static int ngx_http_push_compare_rbtree_node(const ngx_rbtree_node_t *v_left, const ngx_rbtree_node_t *v_right); static ngx_int_t ngx_http_push_delete_node_locked(ngx_rbtree_t *tree, ngx_rbtree_node_t *trash, ngx_slab_pool_t *shpool); static ngx_http_push_channel_t * ngx_http_push_clean_channel_locked(ngx_http_push_channel_t * channel) { ngx_queue_t *sentinel = &channel->message_queue->queue; time_t now = ngx_time(); ngx_http_push_msg_t *msg=NULL; while(!ngx_queue_empty(sentinel)){ msg = ngx_queue_data(ngx_queue_head(sentinel), ngx_http_push_msg_t, queue); if (msg!=NULL && msg->expires != 0 && now > msg->expires) { ngx_http_push_delete_message_locked(channel, msg, ngx_http_push_shpool); } else { //definitely a message left to send return NULL; } } //at this point, the queue is empty return channel->subscribers==0 ? channel : NULL; //if no waiting requests, return this channel to be deleted } static ngx_int_t ngx_http_push_delete_channel_locked(ngx_http_push_channel_t *trash) { ngx_int_t res; res = ngx_http_push_delete_node_locked(&((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree, (ngx_rbtree_node_t *)trash, ngx_http_push_shpool); if(res==NGX_OK) { ((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->channels--; return NGX_OK; } return res; } static ngx_int_t ngx_http_push_delete_node_locked(ngx_rbtree_t *tree, ngx_rbtree_node_t *trash, ngx_slab_pool_t *shpool) { //assume the shm zone is already locked if(trash != NULL){ //take out the trash ngx_rbtree_delete(tree, trash); //delete the worker-subscriber queue ngx_queue_t *sentinel = (ngx_queue_t *)(&((ngx_http_push_channel_t *)trash)->workers_with_subscribers); ngx_queue_t *cur = ngx_queue_head(sentinel); ngx_queue_t *next; while(cur!=sentinel) { next = ngx_queue_next(cur); ngx_slab_free_locked(shpool, cur); cur = next; } ngx_slab_free_locked(shpool, trash); return NGX_OK; } return NGX_DECLINED; } static ngx_http_push_channel_t * ngx_http_push_find_channel(ngx_str_t *id, ngx_log_t *log) { ngx_rbtree_t *tree = &((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree; uint32_t hash; ngx_rbtree_node_t *node, *sentinel; ngx_int_t rc; ngx_http_push_channel_t *up = NULL; ngx_http_push_channel_t *trash[] = { NULL, NULL, NULL }; ngx_uint_t i, trashed=0; if (tree==NULL) { return NULL; } hash = ngx_crc32_short(id->data, id->len); node = tree->root; sentinel = tree->sentinel; while (node != sentinel) { //every search is responsible for deleting a couple of empty, if it comes across them if (trashed < (sizeof(trash) / sizeof(*trash))) { if((trash[trashed]=ngx_http_push_clean_channel_locked((ngx_http_push_channel_t *) node))!=NULL) { trashed++; } } if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ do { up = (ngx_http_push_channel_t *) node; rc = ngx_memn2cmp(id->data, up->id.data, id->len, up->id.len); if (rc == 0) { //found for(i=0; ileft : node->right; } while (node != sentinel && hash == node->key); break; } //not found for(i=0; idata)->tree; if((up = ngx_http_push_slab_alloc_locked(sizeof(*up) + id->len + sizeof(ngx_http_push_msg_t)))==NULL) { return NULL; } up->id.data = (u_char *) (up+1); //contiguous piggy up->message_queue = (ngx_http_push_msg_t *) (up->id.data + id->len); up->id.len = (u_char) id->len; ngx_memcpy(up->id.data, id->data, up->id.len); up->node.key = ngx_crc32_short(id->data, id->len); ngx_rbtree_insert(tree, (ngx_rbtree_node_t *) up); //initialize queues ngx_queue_init(&up->message_queue->queue); up->messages=0; ngx_queue_init(&up->workers_with_subscribers.queue); up->subscribers=0; ((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->channels++; return up; } static void ngx_rbtree_generic_insert( ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)) { for ( ;; ) { if (node->key < temp->key) { if (temp->left == sentinel) { temp->left = node; break; } temp = temp->left; } else if (node->key > temp->key) { if (temp->right == sentinel) { temp->right = node; break; } temp = temp->right; } else { /* node->key == temp->key */ if (compare(node, temp) < 0) { if (temp->left == sentinel) { temp->left = node; break; } temp = temp->left; } else { if (temp->right == sentinel) { temp->right = node; break; } temp = temp->right; } } } node->parent = temp; node->left = sentinel; node->right = sentinel; ngx_rbt_red(node); } #define ngx_http_push_walk_rbtree(apply) \ ngx_http_push_rbtree_walker(&((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree, (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr, apply, ((ngx_http_push_shm_data_t *) ngx_http_push_shm_zone->data)->tree.root) static void ngx_http_push_rbtree_walker(ngx_rbtree_t *tree, ngx_slab_pool_t *shpool, ngx_int_t (*apply)(ngx_http_push_channel_t * channel, ngx_slab_pool_t * shpool), ngx_rbtree_node_t *node) { ngx_rbtree_node_t *sentinel = tree->sentinel; if(node!=sentinel) { apply((ngx_http_push_channel_t *)node, shpool); if(node->left!=NULL) { ngx_http_push_rbtree_walker(tree, shpool, apply, node->left); } if(node->right!=NULL) { ngx_http_push_rbtree_walker(tree, shpool, apply, node->right); } } } static void ngx_http_push_rbtree_insert(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { ngx_rbtree_generic_insert(temp, node, sentinel, ngx_http_push_compare_rbtree_node); } static int ngx_http_push_compare_rbtree_node(const ngx_rbtree_node_t *v_left, const ngx_rbtree_node_t *v_right) { ngx_http_push_channel_t *left = (ngx_http_push_channel_t *) v_left, *right = (ngx_http_push_channel_t *) v_right; return ngx_memn2cmp(left->id.data, right->id.data, left->id.len, right->id.len); } debian/modules/nginx-http-push/src/ngx_http_push_module.c0000644000000000000000000013235012305451331021106 0ustar /* * Copyright 2009 Leo Ponomarev. */ #include #include #include #include #include #include #include #include //emergency garbage collecting goodness; ngx_http_push_channel_queue_t channel_gc_sentinel; static ngx_int_t ngx_http_push_channel_collector(ngx_http_push_channel_t * channel, ngx_slab_pool_t * shpool) { if((ngx_http_push_clean_channel_locked(channel))!=NULL) { //we're up for deletion ngx_http_push_channel_queue_t *trashy; if((trashy = ngx_alloc(sizeof(*trashy), ngx_cycle->log))!=NULL) { //yeah, i'm allocating memory during garbage collection. sue me. trashy->channel=channel; ngx_queue_insert_tail(&channel_gc_sentinel.queue, &trashy->queue); return NGX_OK; } return NGX_ERROR; } return NGX_OK; } //garbage-collecting slab allocator void * ngx_http_push_slab_alloc_locked(size_t size) { void *p; if((p = ngx_slab_alloc_locked(ngx_http_push_shpool, size))==NULL) { ngx_http_push_channel_queue_t *ccur, *cnext; ngx_uint_t collected = 0; //failed. emergency garbage sweep, then. //collect channels ngx_queue_init(&channel_gc_sentinel.queue); ngx_http_push_walk_rbtree(ngx_http_push_channel_collector); for(ccur=(ngx_http_push_channel_queue_t *)ngx_queue_next(&channel_gc_sentinel.queue); ccur != &channel_gc_sentinel; ccur=cnext) { cnext = (ngx_http_push_channel_queue_t *)ngx_queue_next(&ccur->queue); ngx_http_push_delete_channel_locked(ccur->channel); ngx_free(ccur); collected++; } //todo: collect worker messages maybe ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "push module: out of shared memory. emergency garbage collection deleted %ui unused channels.", collected); return ngx_slab_alloc_locked(ngx_http_push_shpool, size); } return p; } void * ngx_http_push_slab_alloc(size_t size) { void *p; ngx_shmtx_lock(&ngx_http_push_shpool->mutex); p = ngx_http_push_slab_alloc_locked(size); ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); return p; } //shpool is assumed to be locked. static ngx_http_push_msg_t *ngx_http_push_get_latest_message_locked(ngx_http_push_channel_t * channel) { ngx_queue_t *sentinel = &channel->message_queue->queue; if(ngx_queue_empty(sentinel)) { return NULL; } ngx_queue_t *qmsg = ngx_queue_last(sentinel); return ngx_queue_data(qmsg, ngx_http_push_msg_t, queue); } //shpool must be locked. No memory is freed. O(1) static ngx_http_push_msg_t *ngx_http_push_get_oldest_message_locked(ngx_http_push_channel_t * channel) { ngx_queue_t *sentinel = &channel->message_queue->queue; if(ngx_queue_empty(sentinel)) { return NULL; } ngx_queue_t *qmsg = ngx_queue_head(sentinel); return ngx_queue_data(qmsg, ngx_http_push_msg_t, queue); } static void ngx_http_push_reserve_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg) { msg->refcount++; //we need a refcount because channel messages MAY be dequed before they are used up. It thus falls on the IPC stuff to free it. } static void ngx_http_push_release_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg) { msg->refcount--; if(msg->queue.next==NULL && msg->refcount<=0) { //message had been dequeued and nobody needs it anymore ngx_http_push_free_message_locked(msg, ngx_http_push_shpool); } if(channel->messages > msg->delete_oldest_received_min_messages && ngx_http_push_get_oldest_message_locked(channel) == msg) { ngx_http_push_delete_message_locked(channel, msg, ngx_http_push_shpool); } } // remove a message from queue and free all associated memory. assumes shpool is already locked. static ngx_inline void ngx_http_push_general_delete_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t force, ngx_slab_pool_t *shpool) { if (msg==NULL) { return; } if(channel!=NULL) { ngx_queue_remove(&msg->queue); channel->messages--; } if(msg->refcount<=0 || force) { //nobody needs this message, or we were forced at integer-point to delete ngx_http_push_free_message_locked(msg, shpool); } } //free memory for a message. static ngx_inline void ngx_http_push_free_message_locked(ngx_http_push_msg_t *msg, ngx_slab_pool_t *shpool) { if(msg->buf->file!=NULL) { ngx_shmtx_unlock(&shpool->mutex); if(msg->buf->file->fd!=NGX_INVALID_FILE) { ngx_close_file(msg->buf->file->fd); } ngx_delete_file(msg->buf->file->name.data); //should I care about deletion errors? doubt it. ngx_shmtx_lock(&shpool->mutex); } ngx_slab_free_locked(shpool, msg->buf); //separate block, remember? ngx_slab_free_locked(shpool, msg); } /** find message with entity tags matching those of the request r. * @param r subscriber request */ static ngx_http_push_msg_t * ngx_http_push_find_message_locked(ngx_http_push_channel_t *channel, ngx_http_request_t *r, ngx_int_t *status) { //TODO: consider using an RBTree for message storage. ngx_queue_t *sentinel = &channel->message_queue->queue; ngx_queue_t *cur = ngx_queue_head(sentinel); ngx_http_push_msg_t *msg; ngx_int_t tag = -1; time_t time = (r->headers_in.if_modified_since == NULL) ? 0 : ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); //channel's message buffer empty? if(channel->messages==0) { *status=NGX_HTTP_PUSH_MESSAGE_EXPECTED; //wait. return NULL; } // do we want a future message? msg = ngx_queue_data(sentinel->prev, ngx_http_push_msg_t, queue); if(time <= msg->message_time) { //that's an empty check (Sentinel's values are zero) if(time == msg->message_time) { if(tag<0) { tag = ngx_http_push_subscriber_get_etag_int(r); } if(tag >= msg->message_tag) { *status=NGX_HTTP_PUSH_MESSAGE_EXPECTED; return NULL; } } } else { *status=NGX_HTTP_PUSH_MESSAGE_EXPECTED; return NULL; } while(cur!=sentinel) { msg = ngx_queue_data(cur, ngx_http_push_msg_t, queue); if (time < msg->message_time) { *status = NGX_HTTP_PUSH_MESSAGE_FOUND; return msg; } else if(time == msg->message_time) { if(tag<0) { tag = ngx_http_push_subscriber_get_etag_int(r); } while (tag >= msg->message_tag && time == msg->message_time && ngx_queue_next(cur)!=sentinel) { cur=ngx_queue_next(cur); msg = ngx_queue_data(cur, ngx_http_push_msg_t, queue); } if(time == msg->message_time && tag < msg->message_tag) { *status = NGX_HTTP_PUSH_MESSAGE_FOUND; return msg; } continue; } cur=ngx_queue_next(cur); } *status = NGX_HTTP_PUSH_MESSAGE_EXPIRED; //message too old and was not found. return NULL; } #define NGX_HTTP_PUSH_NO_CHANNEL_ID_MESSAGE "No channel id provided." static ngx_str_t * ngx_http_push_get_channel_id(ngx_http_request_t *r, ngx_http_push_loc_conf_t *cf) { ngx_http_variable_value_t *vv = ngx_http_get_indexed_variable(r, cf->index); ngx_str_t *group = &cf->channel_group; size_t group_len = group->len; size_t var_len; size_t len; ngx_str_t *id; if (vv == NULL || vv->not_found || vv->len == 0) { ngx_buf_t *buf = ngx_create_temp_buf(r->pool, sizeof(NGX_HTTP_PUSH_NO_CHANNEL_ID_MESSAGE)); ngx_chain_t *chain; if(buf==NULL) { return NULL; } buf->pos=(u_char *)NGX_HTTP_PUSH_NO_CHANNEL_ID_MESSAGE; buf->last=buf->pos + sizeof(NGX_HTTP_PUSH_NO_CHANNEL_ID_MESSAGE)-1; chain = ngx_http_push_create_output_chain(buf, r->pool, r->connection->log); buf->last_buf=1; r->headers_out.content_length_n=ngx_buf_size(buf); r->headers_out.status=NGX_HTTP_NOT_FOUND; r->headers_out.content_type.len = sizeof("text/plain") - 1; r->headers_out.content_type.data = (u_char *) "text/plain"; ngx_http_send_header(r); ngx_http_output_filter(r, chain); ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "push module: the $push_channel_id variable is required but is not set"); return NULL; } //maximum length limiter for channel id var_len = vv->len <= cf->max_channel_id_length ? vv->len : cf->max_channel_id_length; len = group_len + 1 + var_len; if((id = ngx_palloc(r->pool, sizeof(*id) + len))==NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for $push_channel_id string"); return NULL; } id->len=len; id->data=(u_char *)(id+1); ngx_memcpy(id->data, group->data, group_len); id->data[group_len]='/'; ngx_memcpy(id->data + group_len + 1, vv->data, var_len); return id; } #define NGX_HTTP_PUSH_MAKE_ETAG(message_tag, etag, alloc_func, pool) \ etag = alloc_func(pool, sizeof(*etag) + NGX_INT_T_LEN); \ if(etag!=NULL) { \ etag->data = (u_char *)(etag+1); \ etag->len = ngx_sprintf(etag->data,"%ui", message_tag)- etag->data; \ } #define NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, pool) \ if(((content_type) = ngx_palloc(pool, sizeof(*content_type)+content_type_len))!=NULL) { \ (content_type)->len=content_type_len; \ (content_type)->data=(u_char *)((content_type)+1); \ ngx_memcpy(content_type->data, (msg)->content_type.data, content_type_len); \ } static ngx_int_t ngx_http_push_subscriber_handler(ngx_http_request_t *r) { ngx_http_push_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_module); ngx_slab_pool_t *shpool = (ngx_slab_pool_t *)ngx_http_push_shm_zone->shm.addr; ngx_str_t *id; ngx_http_push_channel_t *channel; ngx_http_push_msg_t *msg; ngx_int_t msg_search_outcome; ngx_str_t *content_type=NULL; ngx_str_t *etag; if (r->method != NGX_HTTP_GET) { ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ALLOW, &NGX_HTTP_PUSH_ALLOW_GET); //valid HTTP for teh win return NGX_HTTP_NOT_ALLOWED; } if((id=ngx_http_push_get_channel_id(r, cf)) == NULL) { return r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR; } //get the channel and check channel authorization while we're at it. ngx_shmtx_lock(&shpool->mutex); channel = (cf->authorize_channel==1 ? ngx_http_push_find_channel : ngx_http_push_get_channel)(id, r->connection->log); if (channel==NULL) { //unable to allocate channel OR channel not found ngx_shmtx_unlock(&shpool->mutex); if(cf->authorize_channel) { return NGX_HTTP_FORBIDDEN; } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate shared memory for channel"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } msg = ngx_http_push_find_message_locked(channel, r, &msg_search_outcome); channel->last_seen = ngx_time(); ngx_shmtx_unlock(&shpool->mutex); switch(ngx_http_push_handle_subscriber_concurrency(r, channel, cf)) { case NGX_DECLINED: //this request was declined for some reason. //status codes and whatnot should have already been written. just get out of here quickly. return NGX_OK; case NGX_ERROR: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: error handling subscriber concurrency setting"); return NGX_ERROR; } switch(msg_search_outcome) { //for message-found: ngx_chain_t *chain; time_t last_modified; size_t content_type_len; case NGX_HTTP_PUSH_MESSAGE_EXPECTED: // ♫ It's gonna be the future soon ♫ switch(cf->subscriber_poll_mechanism) { //for NGX_HTTP_PUSH_MECHANISM_LONGPOLL ngx_http_push_pid_queue_t *sentinel, *cur, *found; ngx_http_push_subscriber_t *subscriber; ngx_http_push_subscriber_t *subscriber_sentinel; case NGX_HTTP_PUSH_MECHANISM_LONGPOLL: //long-polling subscriber. wait for a message. //subscribers are queued up in a local pool. Queue sentinels are separate and also local, but not in the pool. ngx_shmtx_lock(&shpool->mutex); sentinel = &channel->workers_with_subscribers; cur = (ngx_http_push_pid_queue_t *)ngx_queue_head(&sentinel->queue); found = NULL; ngx_http_push_subscriber_cleanup_t *clndata; ngx_pool_cleanup_t *cln; while(cur!=sentinel) { if(cur->pid==ngx_pid) { found = cur; break; } cur = (ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue); } if(found == NULL) { //found nothing if((found=ngx_http_push_slab_alloc_locked(sizeof(*found)))==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate worker subscriber queue marker in shared memory"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } //initialize ngx_queue_insert_tail(&sentinel->queue, &found->queue); found->pid=ngx_pid; found->slot=ngx_process_slot; found->subscriber_sentinel=NULL; } ngx_shmtx_unlock(&shpool->mutex); if((subscriber = ngx_palloc(ngx_http_push_pool, sizeof(*subscriber)))==NULL) { //unable to allocate request queue element return NGX_ERROR; } //attach a cleaner to remove the request from the channel. if ((cln=ngx_pool_cleanup_add(r->pool, sizeof(*clndata))) == NULL) { //make sure we can. return NGX_ERROR; } cln->handler = (ngx_pool_cleanup_pt) ngx_http_push_subscriber_cleanup; clndata = (ngx_http_push_subscriber_cleanup_t *) cln->data; clndata->channel=channel; clndata->subscriber=subscriber; subscriber->request = r; subscriber->clndata=clndata; ngx_shmtx_lock(&shpool->mutex); channel->subscribers++; // do this only when we know everything went okay. //figure out the subscriber sentinel subscriber_sentinel = ((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel; if(subscriber_sentinel==NULL) { //it's perfectly nornal for the sentinel to be NULL. if((subscriber_sentinel=ngx_palloc(ngx_http_push_pool, sizeof(*subscriber_sentinel)))==NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate channel subscriber sentinel"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_queue_init(&subscriber_sentinel->queue); ((ngx_http_push_pid_queue_t *)found)->subscriber_sentinel=subscriber_sentinel; } ngx_shmtx_unlock(&shpool->mutex); ngx_queue_insert_tail(&subscriber_sentinel->queue, &subscriber->queue); r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_request_empty_handler; r->discard_body = 1; r->keepalive=1; //stayin' alive!! return NGX_DONE; case NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL: //interval-polling subscriber requests get a 304 with their entity tags preserved. if (r->headers_in.if_modified_since != NULL) { r->headers_out.last_modified_time=ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); } if ((etag=ngx_http_push_subscriber_get_etag(r)) != NULL) { r->headers_out.etag=ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag); } return NGX_HTTP_NOT_MODIFIED; default: //if this ever happens, there's a bug somewhere else. probably config stuff. return NGX_HTTP_INTERNAL_SERVER_ERROR; } case NGX_HTTP_PUSH_MESSAGE_EXPIRED: //subscriber wants an expired message //TODO: maybe respond with entity-identifiers for oldest available message? return NGX_HTTP_NO_CONTENT; case NGX_HTTP_PUSH_MESSAGE_FOUND: //found the message ngx_shmtx_lock(&shpool->mutex); ngx_http_push_reserve_message_locked(channel, msg); NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_palloc, r->pool); if(etag==NULL) { //oh, nevermind... ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for Etag header"); return NGX_ERROR; } content_type_len = msg->content_type.len; if(content_type_len>0) { NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, r->pool); if(content_type==NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate memory for content-type header while responding to subscriber request"); ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } } //preallocate output chain. yes, same one for every waiting subscriber if((chain = ngx_http_push_create_output_chain_locked(msg->buf, r->pool, r->connection->log, shpool))==NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: unable to allocate buffer chain while responding to subscriber request"); ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } last_modified = msg->message_time; //is the message still needed? ngx_http_push_release_message_locked(channel, msg); ngx_shmtx_unlock(&shpool->mutex); if(chain->buf->file!=NULL) { //close file when we're done with it ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; if((cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)))==NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = chain->buf->file->fd; clnf->name = chain->buf->file->name.data; clnf->log = r->pool->log; } return ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified); default: //we shouldn't be here. return NGX_HTTP_INTERNAL_SERVER_ERROR; } } static ngx_int_t ngx_http_push_handle_subscriber_concurrency(ngx_http_request_t *r, ngx_http_push_channel_t *channel, ngx_http_push_loc_conf_t *loc_conf) { ngx_int_t max_subscribers = loc_conf->max_channel_subscribers; ngx_int_t current_subscribers; ngx_shmtx_lock(&ngx_http_push_shpool->mutex); current_subscribers = channel->subscribers; ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); if(current_subscribers==0) { //empty channels are always okay. return NGX_OK; } if(max_subscribers!=0 && current_subscribers >= max_subscribers) { //max_channel_subscribers setting ngx_http_push_respond_status_only(r, NGX_HTTP_FORBIDDEN, NULL); return NGX_DECLINED; } //nonzero number of subscribers present switch(loc_conf->subscriber_concurrency) { case NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_BROADCAST: return NGX_OK; case NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_LASTIN: ngx_shmtx_lock(&ngx_http_push_shpool->mutex); //send "everyone" a 409 Conflict response. //in most reasonable cases, there'll be at most one subscriber on the //channel. However, since settings are bound to locations and not //specific channels, this assumption need not hold. Hence this broadcast. ngx_int_t rc = ngx_http_push_broadcast_status_locked(channel, NGX_HTTP_NOT_FOUND, &NGX_HTTP_PUSH_HTTP_STATUS_409, r->connection->log, ngx_http_push_shpool); ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); return rc==NGX_OK ? NGX_OK : NGX_ERROR; case NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_FIRSTIN: ngx_http_push_respond_status_only(r, NGX_HTTP_NOT_FOUND, &NGX_HTTP_PUSH_HTTP_STATUS_409); return NGX_DECLINED; default: return NGX_ERROR; } } static ngx_int_t ngx_http_push_broadcast_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line, ngx_log_t *log, ngx_slab_pool_t *shpool) { //subscribers are queued up in a local pool. Queue heads, however, are located //in shared memory, identified by pid. ngx_http_push_pid_queue_t *sentinel = &channel->workers_with_subscribers; ngx_http_push_pid_queue_t *cur = sentinel; ngx_int_t received; received = channel->subscribers > 0 ? NGX_HTTP_PUSH_MESSAGE_RECEIVED : NGX_HTTP_PUSH_MESSAGE_QUEUED; if(msg!=NULL && received==NGX_HTTP_PUSH_MESSAGE_RECEIVED) { ngx_http_push_reserve_message_locked(channel, msg); } while((cur=(ngx_http_push_pid_queue_t *)ngx_queue_next(&cur->queue))!=sentinel) { pid_t worker_pid = cur->pid; ngx_int_t worker_slot = cur->slot; ngx_http_push_subscriber_t *subscriber_sentinel= cur->subscriber_sentinel; ngx_shmtx_unlock(&shpool->mutex); if(worker_pid == ngx_pid) { //my subscribers ngx_http_push_respond_to_subscribers(channel, subscriber_sentinel, msg, status_code, status_line); } else { //some other worker's subscribers //interprocess communication breakdown if(ngx_http_push_send_worker_message(channel, subscriber_sentinel, worker_pid, worker_slot, msg, status_code, log) != NGX_ERROR) { ngx_http_push_alert_worker(worker_pid, worker_slot, log); } else { ngx_log_error(NGX_LOG_ERR, log, 0, "push module: error communicating with some other worker process"); } } ngx_shmtx_lock(&shpool->mutex); /* each time all of a worker's subscribers are removed, so is the sentinel. this is done to make garbage collection easier. Assuming we want to avoid placing the sentinel in shared memory (for now -- it's a little tricky to debug), the owner of the worker pool must be the one to free said sentinel. But channels may be deleted by different worker processes, and it seems unwieldy (for now) to do IPC just to delete one stinkin' sentinel. Hence a new sentinel is used every time the subscriber queue is emptied. */ cur->subscriber_sentinel = NULL; //think about it it terms of garbage collection. it'll make sense. sort of. } return received; } #define NGX_HTTP_BUF_ALLOC_SIZE(buf) \ (sizeof(*buf) + \ (((buf)->temporary || (buf)->memory) ? ngx_buf_size(buf) : 0) + \ (((buf)->file!=NULL) ? (sizeof(*(buf)->file) + (buf)->file->name.len + 1) : 0)) #define NGX_HTTP_PUSH_PUBLISHER_CHECK(val, fail, r, errormessage) \ if (val == fail) { \ ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, errormessage); \ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); \ return; \ } #define NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(val, fail, r, errormessage, shpool) \ if (val == fail) { \ ngx_shmtx_unlock(&(shpool)->mutex); \ ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, errormessage); \ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); \ return; \ } static void ngx_http_push_publisher_body_handler(ngx_http_request_t * r) { ngx_str_t *id; ngx_http_push_loc_conf_t *cf = ngx_http_get_module_loc_conf(r, ngx_http_push_module); ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) ngx_http_push_shm_zone->shm.addr; ngx_buf_t *buf = NULL, *buf_copy; ngx_http_push_channel_t *channel; ngx_uint_t method = r->method; time_t last_seen = 0; ngx_uint_t subscribers = 0; ngx_uint_t messages = 0; if((id = ngx_http_push_get_channel_id(r, cf))==NULL) { ngx_http_finalize_request(r, r->headers_out.status ? NGX_OK : NGX_HTTP_INTERNAL_SERVER_ERROR); return; } ngx_shmtx_lock(&shpool->mutex); //POST requests will need a channel created if it doesn't yet exist. if(method==NGX_HTTP_POST || method==NGX_HTTP_PUT) { channel = ngx_http_push_get_channel(id, r->connection->log); NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(channel, NULL, r, "push module: unable to allocate memory for new channel", shpool); } //no other request method needs that. else { //just find the channel. if it's not there, NULL. channel = ngx_http_push_find_channel(id, r->connection->log); } if(channel!=NULL) { subscribers = channel->subscribers; last_seen = channel->last_seen; messages = channel->messages; } else { //404! ngx_shmtx_unlock(&shpool->mutex); r->headers_out.status=NGX_HTTP_NOT_FOUND; //just the headers, please. we don't care to describe the situation or //respond with an html page r->headers_out.content_length_n=0; r->header_only = 1; ngx_http_finalize_request(r, ngx_http_send_header(r)); return; } ngx_shmtx_unlock(&shpool->mutex); switch(method) { ngx_http_push_msg_t *msg, *previous_msg; size_t content_type_len; ngx_http_push_msg_t *sentinel; case NGX_HTTP_POST: //first off, we'll want to extract the body buffer //note: this works mostly because of r->request_body_in_single_buf = 1; //which, i suppose, makes this module a little slower than it could be. //this block is a little hacky. might be a thorn for forward-compatibility. if(r->headers_in.content_length_n == -1 || r->headers_in.content_length_n == 0) { buf = ngx_create_temp_buf(r->pool, 0); //this buffer will get copied to shared memory in a few lines, //so it does't matter what pool we make it in. } else if(r->request_body->bufs->buf!=NULL) { //everything in the first buffer, please buf=r->request_body->bufs->buf; } else if(r->request_body->bufs->next!=NULL) { buf=r->request_body->bufs->next->buf; } else { ngx_log_error(NGX_LOG_ERR, (r)->connection->log, 0, "push module: unexpected publisher message request body buffer location. please report this to the push module developers."); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } NGX_HTTP_PUSH_PUBLISHER_CHECK(buf, NULL, r, "push module: can't find or allocate publisher request body buffer"); content_type_len = (r->headers_in.content_type!=NULL ? r->headers_in.content_type->value.len : 0); ngx_shmtx_lock(&shpool->mutex); //create a buffer copy in shared mem msg = ngx_http_push_slab_alloc_locked(sizeof(*msg) + content_type_len); NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(msg, NULL, r, "push module: unable to allocate message in shared memory", shpool); previous_msg=ngx_http_push_get_latest_message_locked(channel); //need this for entity-tags generation buf_copy = ngx_http_push_slab_alloc_locked(NGX_HTTP_BUF_ALLOC_SIZE(buf)); NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(buf_copy, NULL, r, "push module: unable to allocate buffer in shared memory", shpool) //magic nullcheck ngx_http_push_copy_preallocated_buffer(buf, buf_copy); msg->buf=buf_copy; if(cf->store_messages) { ngx_queue_insert_tail(&channel->message_queue->queue, &msg->queue); channel->messages++; } //Stamp the new message with entity tags msg->message_time=ngx_time(); //ESSENTIAL TODO: make sure this ends up producing GMT time msg->message_tag=(previous_msg!=NULL && msg->message_time == previous_msg->message_time) ? (previous_msg->message_tag + 1) : 0; //store the content-type if(content_type_len>0) { msg->content_type.len=r->headers_in.content_type->value.len; msg->content_type.data=(u_char *) (msg+1); //we had reserved a contiguous chunk, myes? ngx_memcpy(msg->content_type.data, r->headers_in.content_type->value.data, msg->content_type.len); } else { msg->content_type.len=0; msg->content_type.data=NULL; } //set message expiration time time_t message_timeout = cf->buffer_timeout; msg->expires = (message_timeout==0 ? 0 : (ngx_time() + message_timeout)); msg->delete_oldest_received_min_messages = cf->delete_oldest_received_message ? (ngx_uint_t) cf->min_messages : NGX_MAX_UINT32_VALUE; //NGX_MAX_UINT32_VALUE to disable, otherwise = min_message_buffer_size of the publisher location from whence the message came //FMI (For My Information): shm is still locked. switch(ngx_http_push_broadcast_message_locked(channel, msg, r->connection->log, shpool)) { case NGX_HTTP_PUSH_MESSAGE_QUEUED: //message was queued successfully, but there were no //subscribers to receive it. r->headers_out.status = NGX_HTTP_ACCEPTED; r->headers_out.status_line.len =sizeof("202 Accepted")- 1; r->headers_out.status_line.data=(u_char *) "202 Accepted"; break; case NGX_HTTP_PUSH_MESSAGE_RECEIVED: //message was queued successfully, and it was already sent //to at least one subscriber r->headers_out.status = NGX_HTTP_CREATED; r->headers_out.status_line.len =sizeof("201 Created")- 1; r->headers_out.status_line.data=(u_char *) "201 Created"; //update the number of times the message was received. //in the interest of premature optimization, I assume all //current subscribers have received the message successfully. break; case NGX_ERROR: //WTF? ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: error broadcasting message to workers"); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; default: //for debugging, mostly. I don't expect this branch to be //hit during regular operation ngx_shmtx_unlock(&shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "push module: TOTALLY UNEXPECTED error broadcasting message to workers"); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } //shm is still locked I hope. if(buf->file!=NULL) { //future subscribers won't be able to use this file descriptor -- //it will be closed once the publisher request is finalized. //(That's about to happen a handful of lines below.) msg->buf->file->fd=NGX_INVALID_FILE; } //now see if the queue is too big if(channel->messages > (ngx_uint_t) cf->max_messages) { //exceeeds max queue size. force-delete oldest message ngx_http_push_force_delete_message_locked(channel, ngx_http_push_get_oldest_message_locked(channel), shpool); } if(channel->messages > (ngx_uint_t) cf->min_messages) { //exceeeds min queue size. maybe delete the oldest message ngx_http_push_msg_t *oldest_msg = ngx_http_push_get_oldest_message_locked(channel); NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(oldest_msg, NULL, r, "push module: oldest message not found", shpool); } messages = channel->messages; ngx_shmtx_unlock(&shpool->mutex); ngx_http_finalize_request(r, ngx_http_push_channel_info(r, messages, subscribers, last_seen)); return; case NGX_HTTP_PUT: case NGX_HTTP_GET: r->headers_out.status = NGX_HTTP_OK; ngx_http_finalize_request(r, ngx_http_push_channel_info(r, messages, subscribers, last_seen)); return; case NGX_HTTP_DELETE: ngx_shmtx_lock(&shpool->mutex); sentinel = channel->message_queue; msg = sentinel; while((msg=(ngx_http_push_msg_t *)ngx_queue_next(&msg->queue))!=sentinel) { //force-delete all the messages ngx_http_push_force_delete_message_locked(NULL, msg, shpool); } channel->messages=0; //410 gone NGX_HTTP_PUSH_PUBLISHER_CHECK_LOCKED(ngx_http_push_broadcast_status_locked(channel, NGX_HTTP_GONE, &NGX_HTTP_PUSH_HTTP_STATUS_410, r->connection->log, shpool), NGX_ERROR, r, "push module: unable to send current subscribers a 410 Gone response", shpool); ngx_http_push_delete_channel_locked(channel); ngx_shmtx_unlock(&shpool->mutex); //done. r->headers_out.status=NGX_HTTP_OK; ngx_http_finalize_request(r, ngx_http_push_channel_info(r, messages, subscribers, last_seen)); return; default: //some other weird request method ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ALLOW, &NGX_HTTP_PUSH_ALLOW_GET_POST_PUT_DELETE); ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); return; } } static ngx_int_t ngx_http_push_respond_to_subscribers(ngx_http_push_channel_t *channel, ngx_http_push_subscriber_t *sentinel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line) { ngx_slab_pool_t *shpool = ngx_http_push_shpool; ngx_http_push_subscriber_t *cur, *next; ngx_int_t responded_subscribers=0; if(sentinel==NULL) { return NGX_OK; } cur=(ngx_http_push_subscriber_t *)ngx_queue_head(&sentinel->queue); if(msg!=NULL) { //copy everything we need first ngx_str_t *content_type=NULL; ngx_str_t *etag=NULL; time_t last_modified_time; ngx_chain_t *chain; size_t content_type_len; ngx_http_request_t *r; ngx_buf_t *buffer; u_char *pos; ngx_shmtx_lock(&shpool->mutex); //etag NGX_HTTP_PUSH_MAKE_ETAG(msg->message_tag, etag, ngx_pcalloc, ngx_http_push_pool); if(etag==NULL) { //oh, nevermind... ngx_shmtx_unlock(&shpool->mutex); return NGX_ERROR; } //content-type content_type_len = msg->content_type.len; if(content_type_len>0) { NGX_HTTP_PUSH_MAKE_CONTENT_TYPE(content_type, content_type_len, msg, ngx_http_push_pool); if(content_type==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, etag); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: unable to allocate memory for content-type header while responding to several subscriber request"); return NGX_ERROR; } } //preallocate output chain. yes, same one for every waiting subscriber if((chain = ngx_http_push_create_output_chain_locked(msg->buf, ngx_http_push_pool, ngx_cycle->log, shpool))==NULL) { ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, etag); ngx_pfree(ngx_http_push_pool, content_type); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "push module: unable to create output chain while responding to several subscriber request"); return NGX_ERROR; } buffer = chain->buf; pos = buffer->pos; last_modified_time = msg->message_time; ngx_shmtx_unlock(&shpool->mutex); //now let's respond to some requests! while(cur!=sentinel) { next=(ngx_http_push_subscriber_t *)ngx_queue_next(&cur->queue); //in this block, nothing in shared memory should be dereferenced. r=cur->request; //cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter cur->clndata->subscriber=NULL; cur->clndata->channel=NULL; r->discard_body=0; //hacky hacky! ngx_http_finalize_request(r, ngx_http_push_prepare_response_to_subscriber_request(r, chain, content_type, etag, last_modified_time)); //BAM! responded_subscribers++; //done with this subscriber. free the sucker. ngx_pfree(ngx_http_push_pool, cur); //rewind the buffer, please buffer->pos = pos; buffer->last_buf=1; cur=next; } //free everything relevant ngx_pfree(ngx_http_push_pool, etag); ngx_pfree(ngx_http_push_pool, content_type); if(buffer->file) { ngx_close_file(buffer->file->fd); } ngx_pfree(ngx_http_push_pool, buffer); ngx_pfree(ngx_http_push_pool, chain); if(responded_subscribers) { ngx_shmtx_lock(&shpool->mutex); //message deletion ngx_http_push_release_message_locked(channel, msg); ngx_shmtx_unlock(&shpool->mutex); } } else { //headers only probably ngx_http_request_t *r; while(cur!=sentinel) { next=(ngx_http_push_subscriber_t *)ngx_queue_next(&cur->queue); r=cur->request; //cleanup oughtn't dequeue anything. or decrement the subscriber count, for that matter cur->clndata->subscriber=NULL; cur->clndata->channel=NULL; ngx_http_finalize_request(r, ngx_http_push_respond_status_only(r, status_code, status_line)); responded_subscribers++; ngx_pfree(ngx_http_push_pool, cur); cur=next; } } ngx_shmtx_lock(&shpool->mutex); channel->subscribers-=responded_subscribers; //is the message still needed? ngx_shmtx_unlock(&shpool->mutex); ngx_pfree(ngx_http_push_pool, sentinel); return NGX_OK; } static ngx_int_t ngx_http_push_publisher_handler(ngx_http_request_t * r) { ngx_int_t rc; /* Instruct ngx_http_read_subscriber_request_body to store the request body entirely in a memory buffer or in a file */ r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 0; r->request_body_file_log_level = 0; rc = ngx_http_read_client_request_body(r, ngx_http_push_publisher_body_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_DONE; } static void ngx_http_push_match_channel_info_subtype(size_t off, u_char *cur, size_t rem, u_char **priority, const ngx_str_t **format, ngx_str_t *content_type) { static ngx_http_push_content_subtype_t subtypes[] = { { "json" , 4, &NGX_HTTP_PUSH_CHANNEL_INFO_JSON }, { "yaml" , 4, &NGX_HTTP_PUSH_CHANNEL_INFO_YAML }, { "xml" , 3, &NGX_HTTP_PUSH_CHANNEL_INFO_XML }, { "x-json", 6, &NGX_HTTP_PUSH_CHANNEL_INFO_JSON }, { "x-yaml", 6, &NGX_HTTP_PUSH_CHANNEL_INFO_YAML } }; u_char *start = cur + off; ngx_uint_t i; for(i=0; i<(sizeof(subtypes)/sizeof(ngx_http_push_content_subtype_t)); i++) { if(ngx_strncmp(start, subtypes[i].subtype, remstart) { *format = subtypes[i].format; *priority = start; content_type->data=cur; content_type->len= off + 1 + subtypes[i].len; } } } } //print information about a channel static ngx_int_t ngx_http_push_channel_info(ngx_http_request_t *r, ngx_uint_t messages, ngx_uint_t subscribers, time_t last_seen) { ngx_buf_t *b; ngx_uint_t len; ngx_str_t content_type = ngx_string("text/plain"); const ngx_str_t *format = &NGX_HTTP_PUSH_CHANNEL_INFO_PLAIN; time_t time_elapsed = ngx_time() - last_seen; if(r->headers_in.accept) { //lame content-negotiation (without regard for qvalues) u_char *accept = r->headers_in.accept->value.data; size_t len = r->headers_in.accept->value.len; size_t rem; u_char *cur = accept; u_char *priority=&accept[len-1]; for(rem=len; (cur = ngx_strnstr(cur, "text/", rem))!=NULL; cur += sizeof("text/")-1) { rem=len - ((size_t)(cur-accept)+sizeof("text/")-1); if(ngx_strncmp(cur+sizeof("text/")-1, "plain", rem<5 ? rem : 5)==0) { if(priority) { format = &NGX_HTTP_PUSH_CHANNEL_INFO_PLAIN; priority = cur+sizeof("text/")-1; //content-type is already set by default } } ngx_http_push_match_channel_info_subtype(sizeof("text/")-1, cur, rem, &priority, &format, &content_type); } cur = accept; for(rem=len; (cur = ngx_strnstr(cur, "application/", rem))!=NULL; cur += sizeof("application/")-1) { rem=len - ((size_t)(cur-accept)+sizeof("application/")-1); ngx_http_push_match_channel_info_subtype(sizeof("application/")-1, cur, rem, &priority, &format, &content_type); } } r->headers_out.content_type.len = content_type.len; r->headers_out.content_type.data = content_type.data; len = format->len - 8 - 1 + 3*NGX_INT_T_LEN; //minus 8 sprintf if ((b = ngx_create_temp_buf(r->pool, len)) == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b->last = ngx_sprintf(b->last, (char *)format->data, messages, last_seen==0 ? -1 : (ngx_int_t) time_elapsed ,subscribers); //lastly, set the content-length, because if the status code isn't 200, nginx may not do so automatically r->headers_out.content_length_n = ngx_buf_size(b); if (ngx_http_send_header(r) > NGX_HTTP_SPECIAL_RESPONSE) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_output_filter(r, ngx_http_push_create_output_chain(b, r->pool, r->connection->log)); } static ngx_table_elt_t * ngx_http_push_add_response_header(ngx_http_request_t *r, const ngx_str_t *header_name, const ngx_str_t *header_value) { ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NULL; } h->hash = 1; h->key.len = header_name->len; h->key.data = header_name->data; h->value.len = header_value->len; h->value.data = header_value->data; return h; } static ngx_int_t ngx_http_push_subscriber_get_etag_int(ngx_http_request_t * r) { ngx_str_t *if_none_match = ngx_http_push_subscriber_get_etag(r); ngx_int_t tag; if(if_none_match==NULL || (if_none_match!=NULL && (tag = ngx_atoi(if_none_match->data, if_none_match->len))==NGX_ERROR)) { tag=0; } return ngx_abs(tag); } static ngx_str_t * ngx_http_push_subscriber_get_etag(ngx_http_request_t * r) { ngx_uint_t i; ngx_list_part_t *part = &r->headers_in.headers.part; ngx_table_elt_t *header= part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (header[i].key.len == NGX_HTTP_PUSH_HEADER_IF_NONE_MATCH.len && ngx_strncasecmp(header[i].key.data, NGX_HTTP_PUSH_HEADER_IF_NONE_MATCH.data, header[i].key.len) == 0) { return &header[i].value; } } return NULL; } //buffer is _copied_ //if shpool is provided, it is assumed that shm it is locked static ngx_chain_t * ngx_http_push_create_output_chain_general(ngx_buf_t *buf, ngx_pool_t *pool, ngx_log_t *log, ngx_slab_pool_t *shpool) { ngx_chain_t *out; ngx_file_t *file; if((out = ngx_pcalloc(pool, sizeof(*out)))==NULL) { return NULL; } ngx_buf_t *buf_copy; if((buf_copy = ngx_pcalloc(pool, NGX_HTTP_BUF_ALLOC_SIZE(buf)))==NULL) { return NULL; } ngx_http_push_copy_preallocated_buffer(buf, buf_copy); if (buf->file!=NULL) { file = buf_copy->file; file->log=log; if(file->fd==NGX_INVALID_FILE) { if(shpool) { ngx_shmtx_unlock(&shpool->mutex); file->fd=ngx_open_file(file->name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_OWNER_ACCESS); ngx_shmtx_lock(&shpool->mutex); } else { file->fd=ngx_open_file(file->name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_OWNER_ACCESS); } } if(file->fd==NGX_INVALID_FILE) { return NULL; } } buf_copy->last_buf = 1; out->buf = buf_copy; out->next = NULL; return out; } static void ngx_http_push_subscriber_cleanup(ngx_http_push_subscriber_cleanup_t *data) { if(data->subscriber!=NULL) { //still queued up ngx_queue_remove(&data->subscriber->queue); ngx_pfree(ngx_http_push_pool, data->subscriber); //was there an error? oh whatever. } if(data->channel!=NULL) { //we're expected to decrement the subscriber count ngx_shmtx_lock(&ngx_http_push_shpool->mutex); data->channel->subscribers--; ngx_shmtx_unlock(&ngx_http_push_shpool->mutex); } } static ngx_int_t ngx_http_push_respond_status_only(ngx_http_request_t *r, ngx_int_t status_code, const ngx_str_t *statusline) { r->headers_out.status=status_code; if(statusline!=NULL) { r->headers_out.status_line.len =statusline->len; r->headers_out.status_line.data=statusline->data; } r->headers_out.content_length_n = 0; r->header_only = 1; return ngx_http_send_header(r); } //allocates nothing static ngx_int_t ngx_http_push_prepare_response_to_subscriber_request(ngx_http_request_t *r, ngx_chain_t *chain, ngx_str_t *content_type, ngx_str_t *etag, time_t last_modified) { ngx_int_t res; if (content_type!=NULL) { r->headers_out.content_type.len=content_type->len; r->headers_out.content_type.data = content_type->data; } if(last_modified) { //if-modified-since header r->headers_out.last_modified_time=last_modified; } if(etag!=NULL) { //etag, if we need one if ((r->headers_out.etag=ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_ETAG, etag))==NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } //Vary header needed for proper HTTP caching. ngx_http_push_add_response_header(r, &NGX_HTTP_PUSH_HEADER_VARY, &NGX_HTTP_PUSH_VARY_HEADER_VALUE); r->headers_out.status=NGX_HTTP_OK; //we know the entity length, and we're using just one buffer. so no chunking please. r->headers_out.content_length_n=ngx_buf_size(chain->buf); if((res = ngx_http_send_header(r)) >= NGX_HTTP_SPECIAL_RESPONSE) { return res; } return ngx_http_output_filter(r, chain); } static void ngx_http_push_copy_preallocated_buffer(ngx_buf_t *buf, ngx_buf_t *cbuf) { if (cbuf!=NULL) { ngx_memcpy(cbuf, buf, sizeof(*buf)); //overkill? if(buf->temporary || buf->memory) { //we don't want to copy mmpapped memory, so no ngx_buf_in_momory(buf) cbuf->pos = (u_char *) (cbuf+1); cbuf->last = cbuf->pos + ngx_buf_size(buf); cbuf->start=cbuf->pos; cbuf->end = cbuf->start + ngx_buf_size(buf); ngx_memcpy(cbuf->pos, buf->pos, ngx_buf_size(buf)); cbuf->memory=ngx_buf_in_memory_only(buf) ? 1 : 0; } if (buf->file!=NULL) { cbuf->file = (ngx_file_t *) (cbuf+1) + ((buf->temporary || buf->memory) ? ngx_buf_size(buf) : 0); cbuf->file->fd=NGX_INVALID_FILE; cbuf->file->log=NULL; cbuf->file->offset=buf->file->offset; cbuf->file->sys_offset=buf->file->sys_offset; cbuf->file->name.len=buf->file->name.len; cbuf->file->name.data=(u_char *) (cbuf->file+1); ngx_memcpy(cbuf->file->name.data, buf->file->name.data, buf->file->name.len); } } } debian/modules/nginx-http-push/src/ngx_http_push_module.h0000644000000000000000000002413712305451331021116 0ustar #include #include #include #include #define NGX_HTTP_PUSH_DEFAULT_SHM_SIZE 33554432 //32 megs #define NGX_HTTP_PUSH_DEFAULT_BUFFER_TIMEOUT 3600 #define NGX_HTTP_PUSH_DEFAULT_MIN_MESSAGES 1 #define NGX_HTTP_PUSH_DEFAULT_MAX_MESSAGES 10 #define NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_LASTIN 0 #define NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_FIRSTIN 1 #define NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_BROADCAST 2 #define NGX_HTTP_PUSH_MECHANISM_LONGPOLL 0 #define NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL 1 #define NGX_HTTP_PUSH_MIN_MESSAGE_RECIPIENTS 0 #define NGX_HTTP_PUSH_MAX_CHANNEL_ID_LENGTH 1024 //bytes #ifndef NGX_HTTP_CONFLICT #define NGX_HTTP_CONFLICT 409 #endif #ifndef NGX_HTTP_GONE #define NGX_HTTP_GONE 410 #endif #ifndef NGX_HTTP_CREATED #define NGX_HTTP_CREATED 201 #endif #ifndef NGX_HTTP_ACCEPTED #define NGX_HTTP_ACCEPTED 202 #endif #define NGX_HTTP_PUSH_MESSAGE_RECEIVED 9000 #define NGX_HTTP_PUSH_MESSAGE_QUEUED 9001 #define NGX_HTTP_PUSH_MESSAGE_FOUND 1000 #define NGX_HTTP_PUSH_MESSAGE_EXPECTED 1001 #define NGX_HTTP_PUSH_MESSAGE_EXPIRED 1002 //on with the declarations typedef struct { size_t shm_size; } ngx_http_push_main_conf_t; typedef struct { ngx_int_t index; time_t buffer_timeout; ngx_int_t min_messages; ngx_int_t max_messages; ngx_int_t subscriber_concurrency; ngx_int_t subscriber_poll_mechanism; ngx_int_t authorize_channel; ngx_int_t store_messages; ngx_int_t delete_oldest_received_message; ngx_str_t channel_group; ngx_int_t max_channel_id_length; ngx_int_t max_channel_subscribers; } ngx_http_push_loc_conf_t; //message queue typedef struct { ngx_queue_t queue; //this MUST be first. ngx_str_t content_type; // ngx_str_t charset; ngx_buf_t *buf; time_t expires; ngx_uint_t delete_oldest_received_min_messages; //NGX_MAX_UINT32_VALUE for 'never' time_t message_time; //tag message by time ngx_int_t message_tag; //used in conjunction with message_time if more than one message have the same time. ngx_int_t refcount; } ngx_http_push_msg_t; typedef struct ngx_http_push_subscriber_cleanup_s ngx_http_push_subscriber_cleanup_t; //subscriber request queue typedef struct { ngx_queue_t queue; //this MUST be first. ngx_http_request_t *request; ngx_http_push_subscriber_cleanup_t *clndata; } ngx_http_push_subscriber_t; typedef struct { ngx_queue_t queue; pid_t pid; ngx_int_t slot; ngx_http_push_subscriber_t *subscriber_sentinel; } ngx_http_push_pid_queue_t; //our typecast-friendly rbtree node (channel) typedef struct { ngx_rbtree_node_t node; //this MUST be first. ngx_str_t id; ngx_http_push_msg_t *message_queue; ngx_uint_t messages; ngx_http_push_pid_queue_t workers_with_subscribers; ngx_uint_t subscribers; time_t last_seen; } ngx_http_push_channel_t; //cleaning supplies struct ngx_http_push_subscriber_cleanup_s { ngx_http_push_subscriber_t *subscriber; ngx_http_push_channel_t *channel; }; //garbage collecting goodness typedef struct { ngx_queue_t queue; ngx_http_push_channel_t *channel; } ngx_http_push_channel_queue_t; //messages to worker processes typedef struct { ngx_queue_t queue; ngx_http_push_msg_t *msg; //->shared memory ngx_int_t status_code; ngx_pid_t pid; ngx_http_push_channel_t *channel; //->shared memory ngx_http_push_subscriber_t *subscriber_sentinel; //->a worker's local pool } ngx_http_push_worker_msg_t; //shared memory typedef struct { ngx_rbtree_t tree; ngx_uint_t channels; //# of channels being used ngx_http_push_worker_msg_t *ipc; //interprocess stuff } ngx_http_push_shm_data_t; ngx_int_t ngx_http_push_worker_processes; ngx_pool_t *ngx_http_push_pool; ngx_slab_pool_t *ngx_http_push_shpool; ngx_shm_zone_t *ngx_http_push_shm_zone = NULL; //garbage-collecting shared memory slab allocation void * ngx_http_push_slab_alloc(size_t size); void * ngx_http_push_slab_alloc_locked(size_t size); //channel messages static ngx_http_push_msg_t *ngx_http_push_get_latest_message_locked(ngx_http_push_channel_t * channel); static ngx_http_push_msg_t *ngx_http_push_get_oldest_message_locked(ngx_http_push_channel_t * channel); static void ngx_http_push_reserve_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg); static void ngx_http_push_release_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg); static ngx_inline void ngx_http_push_general_delete_message_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t force, ngx_slab_pool_t *shpool); #define ngx_http_push_delete_message_locked(channel, msg, shpool) ngx_http_push_general_delete_message_locked(channel, msg, 0, shpool) #define ngx_http_push_force_delete_message_locked(channel, msg, shpool) ngx_http_push_general_delete_message_locked(channel, msg, 1, shpool) static ngx_inline void ngx_http_push_free_message_locked(ngx_http_push_msg_t *msg, ngx_slab_pool_t *shpool); static ngx_http_push_msg_t * ngx_http_push_find_message_locked(ngx_http_push_channel_t *channel, ngx_http_request_t *r, ngx_int_t *status); //channel static ngx_str_t * ngx_http_push_get_channel_id(ngx_http_request_t *r, ngx_http_push_loc_conf_t *cf); static ngx_int_t ngx_http_push_channel_info(ngx_http_request_t *r, ngx_uint_t message_queue_size, ngx_uint_t subscriber_queue_size, time_t last_seen); //subscriber static ngx_int_t ngx_http_push_subscriber_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_push_handle_subscriber_concurrency(ngx_http_request_t *r, ngx_http_push_channel_t *channel, ngx_http_push_loc_conf_t *loc_conf); static ngx_int_t ngx_http_push_broadcast_locked(ngx_http_push_channel_t *channel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line, ngx_log_t *log, ngx_slab_pool_t *shpool); #define ngx_http_push_broadcast_status_locked(channel, status_code, status_line, log, shpool) ngx_http_push_broadcast_locked(channel, NULL, status_code, status_line, log, shpool) #define ngx_http_push_broadcast_message_locked(channel, msg, log, shpool) ngx_http_push_broadcast_locked(channel, msg, 0, NULL, log, shpool) static ngx_int_t ngx_http_push_respond_to_subscribers(ngx_http_push_channel_t *channel, ngx_http_push_subscriber_t *sentinel, ngx_http_push_msg_t *msg, ngx_int_t status_code, const ngx_str_t *status_line); static ngx_int_t ngx_http_push_subscriber_get_etag_int(ngx_http_request_t * r); static ngx_str_t * ngx_http_push_subscriber_get_etag(ngx_http_request_t * r); static void ngx_http_push_subscriber_cleanup(ngx_http_push_subscriber_cleanup_t *data); static ngx_int_t ngx_http_push_prepare_response_to_subscriber_request(ngx_http_request_t *r, ngx_chain_t *chain, ngx_str_t *content_type, ngx_str_t *etag, time_t last_modified); //publisher static ngx_int_t ngx_http_push_publisher_handler(ngx_http_request_t * r); static void ngx_http_push_publisher_body_handler(ngx_http_request_t * r); //utilities //general request handling static void ngx_http_push_copy_preallocated_buffer(ngx_buf_t *buf, ngx_buf_t *cbuf); static ngx_table_elt_t * ngx_http_push_add_response_header(ngx_http_request_t *r, const ngx_str_t *header_name, const ngx_str_t *header_value); static ngx_int_t ngx_http_push_respond_status_only(ngx_http_request_t *r, ngx_int_t status_code, const ngx_str_t *statusline); static ngx_chain_t * ngx_http_push_create_output_chain_general(ngx_buf_t *buf, ngx_pool_t *pool, ngx_log_t *log, ngx_slab_pool_t *shpool); #define ngx_http_push_create_output_chain(buf, pool, log) ngx_http_push_create_output_chain_general(buf, pool, log, NULL) #define ngx_http_push_create_output_chain_locked(buf, pool, log, shpool) ngx_http_push_create_output_chain_general(buf, pool, log, shpool) //string constants //headers const ngx_str_t NGX_HTTP_PUSH_HEADER_ETAG = ngx_string("Etag"); const ngx_str_t NGX_HTTP_PUSH_HEADER_IF_NONE_MATCH = ngx_string("If-None-Match"); const ngx_str_t NGX_HTTP_PUSH_HEADER_VARY = ngx_string("Vary"); const ngx_str_t NGX_HTTP_PUSH_HEADER_ALLOW = ngx_string("Allow"); //header values const ngx_str_t NGX_HTTP_PUSH_CACHE_CONTROL_VALUE = ngx_string("no-cache"); //status strings const ngx_str_t NGX_HTTP_PUSH_HTTP_STATUS_409 = ngx_string("409 Conflict"); const ngx_str_t NGX_HTTP_PUSH_HTTP_STATUS_410 = ngx_string("410 Gone"); //other stuff const ngx_str_t NGX_HTTP_PUSH_ALLOW_GET_POST_PUT_DELETE= ngx_string("GET, POST, PUT, DELETE"); const ngx_str_t NGX_HTTP_PUSH_ALLOW_GET= ngx_string("GET"); const ngx_str_t NGX_HTTP_PUSH_VARY_HEADER_VALUE = ngx_string("If-None-Match, If-Modified-Since"); const ngx_str_t NGX_HTTP_PUSH_CHANNEL_INFO_PLAIN = ngx_string( "queued messages: %ui" CRLF "last requested: %d sec. ago (-1=never)" CRLF "active subscribers: %ui" "\0"); const ngx_str_t NGX_HTTP_PUSH_CHANNEL_INFO_JSON = ngx_string( "{\"messages\": %ui, " "\"requested\": %d, " "\"subscribers\": %ui }" "\0"); const ngx_str_t NGX_HTTP_PUSH_CHANNEL_INFO_XML = ngx_string( "" CRLF "" CRLF " %ui" CRLF " %d" CRLF " %ui" CRLF "" "\0"); const ngx_str_t NGX_HTTP_PUSH_CHANNEL_INFO_YAML = ngx_string( "---" CRLF "messages: %ui" CRLF "requested: %d" CRLF "subscribers %ui" CRLF "\0"); typedef struct { char *subtype; size_t len; const ngx_str_t *format; } ngx_http_push_content_subtype_t; debian/modules/nginx-http-push/src/ngx_http_push_module_setup.c0000644000000000000000000003514712466164574022356 0ustar ngx_module_t ngx_http_push_module; static ngx_int_t ngx_http_push_init_module(ngx_cycle_t *cycle) { ngx_core_conf_t *ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_http_push_worker_processes = ccf->worker_processes; //initialize subscriber queues //pool, please if((ngx_http_push_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, cycle->log))==NULL) { //I trust the cycle pool size to be a well-tuned one. return NGX_ERROR; } //initialize our little IPC return ngx_http_push_init_ipc(cycle, ngx_http_push_worker_processes); } static ngx_int_t ngx_http_push_init_worker(ngx_cycle_t *cycle) { if((ngx_http_push_init_ipc_shm(ngx_http_push_worker_processes))!=NGX_OK) { return NGX_ERROR; } else if (ngx_process != NGX_PROCESS_WORKER) { //not a worker, stop initializing stuff. return NGX_OK; } else { return ngx_http_push_register_worker_message_handler(cycle); } } // shared memory zone initializer static ngx_int_t ngx_http_push_init_shm_zone(ngx_shm_zone_t * shm_zone, void *data) { if(data) { /* zone already initialized */ shm_zone->data = data; return NGX_OK; } ngx_slab_pool_t *shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; ngx_rbtree_node_t *sentinel; ngx_http_push_shm_data_t *d; ngx_http_push_shpool = shpool; //we'll be using this a bit. if ((d = (ngx_http_push_shm_data_t *)ngx_slab_alloc(shpool, sizeof(*d))) == NULL) { //shm_data plus an array. return NGX_ERROR; } shm_zone->data = d; d->ipc=NULL; //initialize rbtree if ((sentinel = ngx_slab_alloc(shpool, sizeof(*sentinel)))==NULL) { return NGX_ERROR; } ngx_rbtree_init(&d->tree, sentinel, ngx_http_push_rbtree_insert); return NGX_OK; } //shared memory static ngx_str_t ngx_push_shm_name = ngx_string("push_module"); //shared memory segment name static ngx_int_t ngx_http_push_set_up_shm(ngx_conf_t *cf, size_t shm_size) { ngx_http_push_shm_zone = ngx_shared_memory_add(cf, &ngx_push_shm_name, shm_size, &ngx_http_push_module); if (ngx_http_push_shm_zone == NULL) { return NGX_ERROR; } ngx_http_push_shm_zone->init = ngx_http_push_init_shm_zone; ngx_http_push_shm_zone->data = (void *) 1; return NGX_OK; } static ngx_int_t ngx_http_push_postconfig(ngx_conf_t *cf) { ngx_http_push_main_conf_t *conf = ngx_http_conf_get_module_main_conf(cf, ngx_http_push_module); //initialize shared memory size_t shm_size; if(conf->shm_size==NGX_CONF_UNSET_SIZE) { conf->shm_size=NGX_HTTP_PUSH_DEFAULT_SHM_SIZE; } shm_size = ngx_align(conf->shm_size, ngx_pagesize); if (shm_size < 8 * ngx_pagesize) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The push_max_reserved_memory value must be at least %udKiB", (8 * ngx_pagesize) >> 10); shm_size = 8 * ngx_pagesize; } if(ngx_http_push_shm_zone && ngx_http_push_shm_zone->shm.size != shm_size) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); } ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "Using %udKiB of shared memory for push module", shm_size >> 10); return ngx_http_push_set_up_shm(cf, shm_size); } //main config static void * ngx_http_push_create_main_conf(ngx_conf_t *cf) { ngx_http_push_main_conf_t *mcf = ngx_pcalloc(cf->pool, sizeof(*mcf)); if(mcf == NULL) { return NGX_CONF_ERROR; } mcf->shm_size=NGX_CONF_UNSET_SIZE; return mcf; } //location config stuff static void * ngx_http_push_create_loc_conf(ngx_conf_t *cf) { ngx_http_push_loc_conf_t *lcf = ngx_pcalloc(cf->pool, sizeof(*lcf)); if(lcf == NULL) { return NGX_CONF_ERROR; } lcf->buffer_timeout=NGX_CONF_UNSET; lcf->max_messages=NGX_CONF_UNSET; lcf->min_messages=NGX_CONF_UNSET; lcf->subscriber_concurrency=NGX_CONF_UNSET; lcf->subscriber_poll_mechanism=NGX_CONF_UNSET; lcf->authorize_channel=NGX_CONF_UNSET; lcf->store_messages=NGX_CONF_UNSET; lcf->delete_oldest_received_message=NGX_CONF_UNSET; lcf->max_channel_id_length=NGX_CONF_UNSET; lcf->max_channel_subscribers=NGX_CONF_UNSET; lcf->channel_group.data=NULL; return lcf; } static char * ngx_http_push_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_push_loc_conf_t *prev = parent, *conf = child; ngx_conf_merge_sec_value(conf->buffer_timeout, prev->buffer_timeout, NGX_HTTP_PUSH_DEFAULT_BUFFER_TIMEOUT); ngx_conf_merge_value(conf->max_messages, prev->max_messages, NGX_HTTP_PUSH_DEFAULT_MAX_MESSAGES); ngx_conf_merge_value(conf->min_messages, prev->min_messages, NGX_HTTP_PUSH_DEFAULT_MIN_MESSAGES); ngx_conf_merge_value(conf->subscriber_concurrency, prev->subscriber_concurrency, NGX_HTTP_PUSH_SUBSCRIBER_CONCURRENCY_BROADCAST); ngx_conf_merge_value(conf->subscriber_poll_mechanism, prev->subscriber_poll_mechanism, NGX_HTTP_PUSH_MECHANISM_LONGPOLL); ngx_conf_merge_value(conf->authorize_channel, prev->authorize_channel, 0); ngx_conf_merge_value(conf->store_messages, prev->store_messages, 1); ngx_conf_merge_value(conf->delete_oldest_received_message, prev->delete_oldest_received_message, 0); ngx_conf_merge_value(conf->max_channel_id_length, prev->max_channel_id_length, NGX_HTTP_PUSH_MAX_CHANNEL_ID_LENGTH); ngx_conf_merge_value(conf->max_channel_subscribers, prev->max_channel_subscribers, 0); ngx_conf_merge_str_value(conf->channel_group, prev->channel_group, ""); //sanity checks if(conf->max_messages < conf->min_messages) { //min/max buffer size makes sense? ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "push_max_message_buffer_length cannot be smaller than push_min_message_buffer_length."); return NGX_CONF_ERROR; } return NGX_CONF_OK; } static ngx_str_t ngx_http_push_channel_id = ngx_string("push_channel_id"); //channel id variable //publisher and subscriber handlers now. static char *ngx_http_push_setup_handler(ngx_conf_t *cf, void * conf, ngx_int_t (*handler)(ngx_http_request_t *)) { ngx_http_core_loc_conf_t *clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); ngx_http_push_loc_conf_t *plcf = conf; clcf->handler = handler; clcf->if_modified_since = NGX_HTTP_IMS_OFF; plcf->index = ngx_http_get_variable_index(cf, &ngx_http_push_channel_id); if (plcf->index == NGX_ERROR) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } typedef struct { char *str; ngx_int_t val; } ngx_http_push_strval_t; static ngx_int_t ngx_http_push_strval(ngx_str_t string, ngx_http_push_strval_t strval[], ngx_int_t strval_len, ngx_int_t *val) { ngx_int_t i; for(i=0; ioffset); if (*field != NGX_CONF_UNSET) { return "is duplicate"; } ngx_str_t value = (((ngx_str_t *) cf->args->elts)[1]); if(ngx_http_push_strval(value, concurrency, 3, field)!=NGX_OK) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid push_subscriber_concurrency value: %V", &value); return NGX_CONF_ERROR; } return NGX_CONF_OK; } static char *ngx_http_push_publisher(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_http_push_setup_handler(cf, conf, &ngx_http_push_publisher_handler); } static char *ngx_http_push_subscriber(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { static ngx_http_push_strval_t mech[] = { { "interval-poll", NGX_HTTP_PUSH_MECHANISM_INTERVALPOLL }, { "long-poll" , NGX_HTTP_PUSH_MECHANISM_LONGPOLL } }; ngx_int_t *field = (ngx_int_t *) ((char *) conf + cmd->offset); if (*field != NGX_CONF_UNSET) { return "is duplicate"; } if(cf->args->nelts==1) { //no argument given *field = NGX_HTTP_PUSH_MECHANISM_LONGPOLL; //default } else { ngx_str_t value = (((ngx_str_t *) cf->args->elts)[1]); if(ngx_http_push_strval(value, mech, 2, field)!=NGX_OK) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "invalid push_subscriber value: %V", &value); return NGX_CONF_ERROR; } } return ngx_http_push_setup_handler(cf, conf, &ngx_http_push_subscriber_handler); } //great justice appears to be at hand static ngx_int_t ngx_http_push_movezig_channel_locked(ngx_http_push_channel_t * channel, ngx_slab_pool_t * shpool) { ngx_queue_t *sentinel = &channel->message_queue->queue; ngx_http_push_msg_t *msg=NULL; while(!ngx_queue_empty(sentinel)) { msg = ngx_queue_data(ngx_queue_head(sentinel), ngx_http_push_msg_t, queue); ngx_http_push_force_delete_message_locked(channel, msg, shpool); } return NGX_OK; } static void ngx_http_push_exit_master(ngx_cycle_t *cycle) { //destroy channel tree in shared memory ngx_http_push_walk_rbtree(ngx_http_push_movezig_channel_locked); } static void ngx_http_push_exit_worker(ngx_cycle_t *cycle) { ngx_http_push_ipc_exit_worker(cycle); } static char *ngx_http_push_set_message_buffer_length(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; ngx_int_t *min, *max; ngx_str_t *value; ngx_int_t intval; min = (ngx_int_t *) (p + offsetof(ngx_http_push_loc_conf_t, min_messages)); max = (ngx_int_t *) (p + offsetof(ngx_http_push_loc_conf_t, max_messages)); if(*min != NGX_CONF_UNSET || *max != NGX_CONF_UNSET) { return "is duplicate"; } value = cf->args->elts; if((intval = ngx_atoi(value[1].data, value[1].len))==NGX_ERROR) { return "invalid number"; } *min = intval; *max = intval; return NGX_CONF_OK; } static ngx_command_t ngx_http_push_commands[] = { { ngx_string("push_message_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_sec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, buffer_timeout), NULL }, { ngx_string("push_max_reserved_memory"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_push_main_conf_t, shm_size), NULL }, { ngx_string("push_min_message_buffer_length"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, min_messages), NULL }, { ngx_string("push_max_message_buffer_length"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, max_messages), NULL }, { ngx_string("push_message_buffer_length"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_push_set_message_buffer_length, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("push_delete_oldest_received_message"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, delete_oldest_received_message), NULL }, { ngx_string("push_publisher"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, ngx_http_push_publisher, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("push_subscriber"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, ngx_http_push_subscriber, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, subscriber_poll_mechanism), NULL }, { ngx_string("push_subscriber_concurrency"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_push_set_subscriber_concurrency, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, subscriber_concurrency), NULL }, { ngx_string("push_authorized_channels_only"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, authorize_channel), NULL }, { ngx_string("push_store_messages"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, store_messages), NULL }, { ngx_string("push_channel_group"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, channel_group), NULL }, { ngx_string("push_max_channel_id_length"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, max_channel_id_length), NULL }, { ngx_string("push_max_channel_subscribers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_push_loc_conf_t, max_channel_subscribers), NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_push_module_ctx = { NULL, /* preconfiguration */ ngx_http_push_postconfig, /* postconfiguration */ ngx_http_push_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_push_create_loc_conf, /* create location configuration */ ngx_http_push_merge_loc_conf, /* merge location configuration */ }; ngx_module_t ngx_http_push_module = { NGX_MODULE_V1, &ngx_http_push_module_ctx, /* module context */ ngx_http_push_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ ngx_http_push_init_module, /* init module */ ngx_http_push_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ ngx_http_push_exit_worker, /* exit process */ ngx_http_push_exit_master, /* exit master */ NGX_MODULE_V1_PADDING }; debian/modules/nginx-http-push/protocol.txt0000644000000000000000000001771112305451330016320 0ustar Basic HTTP Push Relay Protocol Rev. 2.23 1. Introduction 1.1. Purpose The primary purpose of this protocol is to enable a method of long-polling, transparent to the web client, where client connections idle only on the HTTP server and need not be forwarded. 1.2. Requirements The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119. An implementation is not compliant if it fails to satisfy one or more of the MUST or REQUIRED level requirements for the protocols it implements. An implementation that satisfies all the MUST or REQUIRED level and all the SHOULD level requirements for its protocols is said to be "unconditionally compliant"; one that satisfies all the MUST level requirements but not all the SHOULD level requirements for its protocols is said to be "conditionally compliant." 1.3. Terminology This specification uses a number of terms to refer to the roles played by participants in, and objects of, this protocol: server The HTTP server implementing this protocol. client A program that initiates TCP/IP connections with the HTTP server for the purpose of sending HTTP requests. message Application specific data, usually enclosed in a request or response body. channel A resource representing an isolated pathway for message transmission. Each channel has a single unique message queue. subscriber A client that sends HTTP requests to the server for the purposes of receiving messages via some channel. publisher A client that sends HTTP requests to the server in order to transmit messages to subscribers via a channel. channel id A unique identifier for a channel. location A url (or set of urls) on the server. 2. Requirements 2.1. Server Requirements The HTTP server MUST have a mechanism of specifying a url, or a set of urls as publisher and subscriber locations. All requests to the publisher location MUST be treated as publisher requests, all to the subscriber location as subscriber requests. The server MUST implement a mechanism for identifying channels with unique ids. This MAY, for example, be a url parameter (/foo/?id=123) or a cookie. Methods of channel identification other than those using the url MAY be used, but are strongly discouraged. The server MUST accept requests on publisher locations and respond to them immediately. It MUST also accept requests on subscriber locations, but need not respond immediately. 2.2. Client Requirements All clients must prodice valid HTTP requests. Subscriber clients must have a caching mechanism that appropriately reacts to Last-Modified and Etag response headers (web browsers, for example). 2.3. The Channel ID It is not the responsibility of the server to generate IDs. 3. Server Operation A publisher request functions as notification to the server of a message to send to some subscribers over some channel. A subscriber request notifies the server of the subscriber's intent to receive a message. 3.1. The Subscriber The server MUST accept all valid HTTP GET requests to the subscriber location. All other request methods SHOULD be responded to with a 405 Method Not Allowed status code. Subscriber requests are considered notifications of intent to receive some message. Subscribers may request existing messages, messages that are not yet available, and messages that are no longer available. The requested message is identified using the If-Modified-Since and If-None-Match request headers. A request with no If-Modified-Since header MUST be assumed to be requesting the oldest available message in a channel. Each 200 OK response containing a message MUST have its Last-Modified and Etag headers set so that a request using those headers will be interpreted as a request for the next available message. Additionally, said 200 OK MUST contain the Content-Type header of the message publisher request, unless no Content-Type header had been provided or it is explicitly overridden by server configuration. There are several common mechanisms for performing an HTTP server push. The rest of the behavior of the server in response to a subscriber request SHOULD be configurable and MUST be selected from the following list of mechanisms: Long-Polling Requests for existing messages will be responded to immediately; responses to requests for messages not yet available MUST be delayed until the message becomes available. Delayed responses MUST satisfy all of the following conditions: + A 200 OK response containing the message (and its Content-Type) MUST be sent immediately after the message becomes available. The entire response must be indistinguishable from a response to a request for an existing message. + If the channel the subscriber is waiting on is deleted or for some reason becomes unavailable, the server MUST immediately send a 410 Gone response. + If another subscriber has conflicted with this request, the server MUST immediately send a 409 Conflict response. Interval-Polling All requests will be responded to immediately. Requests for messages not yet available MUST produce a 304 Not Modified response code. In addition, when the server receives more than one concurrent subscriber request on the same channel, it MUST do one of the following: Broadcast No additional actions are performed Last-in, first-out All but the most recent long-held subscriber request on the channel are sent a 409 Conflict response. First-in, last-out All but the oldest request will be sent a 409 Conflict The server SHOULD make this selection configurable, and MUST default to broadcast behavior. 3.2. The Publisher The server MUST accept all valid HTTP requests to the publisher location. The server, when sent a publisher request, MUST satisfy all of the following conditions: * GET requests receive a 200 OK response for existing channels and a 404 Not Found otherwise. * PUT requests receive a 200 OK response. The request creates a channel if no channel with the given channel id exists. * DELETE requests receive a 200 OK if the channel identified by the channel id exists and has been completely deleted. All subscribers MUST have been sent a 410 Gone response. Requests for nonexistent channels MUST be responded to with a 404 Not Found. * POST requests are used to send messages. The request MAY contain a body in any encoding representing a message to be sent over the channel. The message MUST be immediately delivered to all currently long-held subscriber requests. Additionally, the message MAY be stored for future retrieval and the oldest message stored for the channel MAY be deleted. A POST request MUST be replied to with a 201 Created if there were any long-held subscribers that have been sent this message, and with a 202 Accepted otherwise. The Content-Type header of the request MUST be forwarded with the message. Message storage limits SHOULD be configurable. publisher locations SHOULD be configurable to allow foregoing message storage on POST requests. All 200-level responses MUST, in the response body, contain information about the applicable channel. This information MAY contain the number of stored messages and the number of subscribers' requests being long-held prior to this request. The server MAY implement a content-negotiation scheme for this information. debian/modules/nginx-http-push/README0000644000000000000000000001723012305451330014572 0ustar NGiNX HTTP push module - Turn NGiNX into an adept HTTP Push (Comet) server. This module takes care of all the connection juggling, and exposes a simple interface to broadcast messages to clients via plain old HTTP requests. This makes it possible to write live-updating applications without having to wait on idle connections via upstream proxies or making your code all asynchronous and concurrent. ---------------- Configuration Directives & Variables ------------------------ Variables: $push_channel_id A token uniquely identifying a communication channel. Must be present in the context of the push_subscriber and push_publisher directives. Example: set $push_channel_id $arg_id; #channel id is now the url query string parameter "id" #(/foo/bar?id=channel_id_string) Directives: ==Publisher/Subscriber== push_subscriber [ long-poll | interval-poll ] default: long-poll context: server, location Defines a server or location as a subscriber. This location represents a subscriber's interface to a channel's message queue. The queue is traversed automatically via caching information request headers (If-Modified-Since and If-None-Match), beginning with the oldest available message. Requests for upcoming messages are handled in accordance with the setting provided. See the protocol documentation for a detailed description. push_subscriber_concurrency [ last | first | broadcast ] default: broadcast context: http, server, location Controls how multiple subscriber requests to a channel (identified by some common ID) are handled. The values work as follows: - broadcast: any number of concurrent subscriber requests may be held. - last: only the most recent subscriber request is kept, all others get a 409 Conflict response. - first: only the oldest subscriber request is kept, all others get a 409 Conflict response. push_publisher default: none context: server, location Defines a server or location as a message publisher. Requests to a publisher location are treated as messages to be sent to subscribers. See the protocol documentation for a detailed description. == Message storage == push_store_messages [ on | off ] default: on context: http, server, location Whether or not message queuing is enabled. "Off" is equivalent to the setting push_channel_buffer_length 0; push_max_reserved_memory [ size ] default: 32M context: http The size of the memory chunk this module will use for all message queuing and buffering. push_min_message_buffer_length [ number ] default: 1 context: http, server, location The minimum number of messages to store per channel. A channel's message buffer will retain at least this many most recent messages. push_max_message_buffer_length [ number ] default: 10 context: http, server, location The maximum number of messages to store per channel. A channel's message buffer will retain at most this many most recent messages. push_message_buffer_length [ number ] default: none context: http, server, location The exact number of messages to store per channel. Sets both push_max_message_buffer_length and push_min_message_buffer_length to this value. push_delete_oldest_received_message [ on | off ] default: off context: http, server, location When enabled, as soon as the oldest message in a channel's message queue has been received by a subscriber, it is deleted -- provided there are more than push_min_message_buffer_length messages in the channel's message buffer. Recommend avoiding this directive as it violates subscribers' assumptions of GET request idempotence. push_message_timeout [ time ] default: 1h context: http, server, location The length of time a message may be queued before it is considered expired. If you do not want messages to expire, set this to 0. Applicable only if a push_publisher is present in this or a child context. == Security == push_authorized_channels_only [ on | off ] default: off context: http, server, location Whether or not a subscriber may create a channel by making a request to a push_subscriber location. If set to on, a publisher must send a POST or PUT request before a subscriber can request messages on the channel. Otherwise, all subscriber requests to nonexistent channels will get a 403 Forbidden response. push_channel_group [ string ] default: (none) context: server, location Because settings are bound to locations and not individual channels, it is useful to be able to have channels that can be reached only from some locations and never others. That's where this setting comes in. Think of it as a prefix string for the channel id. push_max_channel_id_length [ number ] default: 512 context: main, server, location Maximum permissible channel id length (number of characters). Longer ids will be truncated. push_max_channel_subscribers [ number ] default: 0 (unlimited) context: main, server, location Maximum concurrent subscribers. Pretty self-explanatory. --------------------------- Example Config ----------------------------------- http { #maximum amount of memory the push module is allowed to use #for buffering and stuff push_max_reserved_memory 12M; #default is 3M # internal publish endpoint (keep it private / protected) location /publish { set $push_channel_id $arg_channel; #/?channel=239aff3 or some-such push_publisher; push_message_timeout 2h; # expire buffered messages after 2 hours push_max_message_buffer_length 10; # store absolutely at most 10 messages push_min_message_recipients 0; # minimum recipients before purge } # public long-polling endpoint location /activity { push_subscriber; # how multiple subscriber requests to the same channel id are handled # - last: only the most recent subscriber request is kept, 409 for others. # - first: only the oldest subscriber request is kept, 409 for others. # - broadcast: any number of subscriber requests may be long-polling. push_subscriber_concurrency broadcast; set $push_channel_id $arg_channel; #/?channel=239aff3 or some-such default_type text/plain; } } ---------------------------- Operation --------------------------------------- The following describes what is likely to be the most commonly desired setup: Assuming the example config given above, Clients will connect to http://example.com/activity?id=... and have the response delayed until a message is POSTed to http://example.com/publish?id=... Messages can be sent to clients that have not yet connected, i.e. they are queued. Upon sending a request to a push_publisher location, the message, contained in the publisher request body, will be sent to the channel identified by $push_channel_id and to all presently connected channel subscribers. If there are no subscribers waiting for a message at the time, the publisher will be sent to with a with a 202 Accepted response. Otherwise, a 201 Created response is sent. Additionally, the body of the publisher response will contain information about the channel (number of current subscribers, message queue length, etc). If you intend to have the publisher be a server-side application, it's a damn good idea to make sure the push_publisher location is not publicly accessible. Traversal through a channel's message buffer by a subscriber requires proper HTTP caching support from the subscriber client. Make sure it correctly sends Last-Modified and Etag headers. (All modern web browsers do this.) ----------------------- Protocol Spec -------------------------------------- This module is unconditionally (fully) compliant with the Basic HTTP Push Relay Protocol, Rev. 2.21, found in the file protocol.txt.debian/modules/nginx-auth-pam/0000755000000000000000000000000012305451341013471 5ustar debian/modules/nginx-auth-pam/config0000644000000000000000000000030612305451331014657 0ustar ngx_addon_name=ngx_http_auth_pam_module HTTP_MODULES="$HTTP_MODULES ngx_http_auth_pam_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_auth_pam_module.c" CORE_LIBS="$CORE_LIBS -lpam" debian/modules/nginx-auth-pam/README0000644000000000000000000000660112305451331014353 0ustar Nginx module to use PAM for simple http authentication ====================================================== :Date: $Date: 2010-11-15 12:35:32 +0100 (dl 15 de nov de 2010) $ :Revision: $Rev: 4488 $ Compilation ----------- When compiling from source build as usual adding the -add-module option:: ./configure --add-module=$PATH_TO_MODULE If you are using a Debian GNU/Linux distribution is easy to build a modified package that includes this module:: # Get the source apt-get source nginx # Copy the module NGINX_DEBIAN=$(ls -d nginx-*/debian) mkdir $NGINX_DEBIAN/ngx_http_auth_pam_module cp config $NGINX_DEBIAN/ngx_http_auth_pam_module/ cp ngx_http_auth_pam_module.c $NGINX_DEBIAN/ngx_http_auth_pam_module/ cd $NGINX_DEBIAN; cd ..; # Add the argument ``--add-module=./debian/ngx_http_auth_pam_module`` to the # ./configure call on the debian/rules file sed -i -e '/.\/configure .*\\$/,/[^\\]$/ { /^.*[^\\]$/ { s%^\(.*\)$%\t --add-module=./debian/ngx_http_auth_pam_module \\\n\1%; } }' debian/rules # Add the libpam-dev build dependency sed -i -e 's/^Build-Depends: /Build-Depends: libpam-dev, /;' debian/control # Update the package version using the dch command from devscripts dch -l'+authpam' 'Added ngx_http_auth_pam_module support' # Build the package dpkg-buildpackage # And install sudo dpkg -i ../nginx*deb Configuration ------------- The module only has two directives: - ``auth_pam``: This is the http authentication realm. If given the value ``off`` the module is disabled (needed when we want to override the value set on a lower-level directive). - ``auth_pam_service_name``: this is the PAM service name and by default it is set to ``nginx``. Examples -------- To protect everything under ``/secure`` you will add the following to the ``nginx.conf`` file:: location /secure { auth_pam "Secure Zone"; auth_pam_service_name "nginx"; } Note that the module runs as the web server user, so the PAM modules used must be able to authenticate the users without being root; that means that if you want to use the ``pam_unix.so`` module to autenticate users you need to let the web server user to read the ``/etc/shadow`` file if that does not scare you (on Debian like systems you can add the ``www-data`` user to the ``shadow`` group). As an example, to authenticate users against an LDAP server (using the ``pam_ldap.so`` module) you will use an ``/etc/pam.d/nginx`` like the following:: auth required /lib/security/pam_ldap.so account required /lib/security/pam_ldap.so If you also want to limit the users from LDAP that can authenticate you can use the ``pam_listfile.so`` module; to limit who can access resources under ``/restricted`` add the following to the ``nginx.conf`` file:: location /restricted { auth_pam "Restricted Zone"; auth_pam_service_name "nginx_restricted"; } Use the following ``/etc/pam.d/nginx_restricted`` file:: auth required /lib/security/pam_listfile.so onerr=fail item=user \ sense=allow file=/etc/nginx/restricted_users auth required /lib/security/pam_ldap.so account required /lib/security/pam_ldap.so And add the users allowed to authenticate to the ``/etc/nginx/restricted_users`` (remember that the web server user has to be able to read this file). .. ...... .. SVN Id: $Id: README 4488 2010-11-15 11:35:32Z sto $ debian/modules/nginx-auth-pam/LICENSE0000644000000000000000000000247112305451331014501 0ustar /* * Copyright (C) 2008-2010 Sergio Talens Oliag * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ debian/modules/nginx-auth-pam/ChangeLog0000644000000000000000000000053612305451331015246 0ustar 2010-11-15 sto@iti.upv.es * Version 1.2. * Fixed possible memory leak when authentication fails, pam_end has to be called to free memory (thanks to Neil Chintomby). 2009-01-26 sto@iti.upv.es * Version 1.1. * Fixed ngx_log_debugX calls, no we use the correct X value on each call. 2008-09-17 sto@iti.upv.es * Initial version (1.0). debian/modules/nginx-auth-pam/ngx_http_auth_pam_module.c0000644000000000000000000002535712305451331020726 0ustar /* * Copyright (C) 2008-2010 Sergio Talens-Oliag * * Based on nginx's 'ngx_http_auth_basic_module.c' by Igor Sysoev and apache's * 'mod_auth_pam.c' by Ingo Luetkebolhe. * * SVN Id: $Id: ngx_http_auth_pam_module.c 4487 2010-11-15 09:57:03Z sto $ */ #include #include #include #include #define NGX_PAM_SERVICE_NAME "nginx" /* Module context data */ typedef struct { ngx_str_t passwd; } ngx_http_auth_pam_ctx_t; /* PAM userinfo */ typedef struct { ngx_str_t username; ngx_str_t password; } ngx_pam_userinfo; /* Module configuration struct */ typedef struct { ngx_str_t realm; /* http basic auth realm */ ngx_str_t service_name; /* pam service name */ } ngx_http_auth_pam_loc_conf_t; /* Module handler */ static ngx_int_t ngx_http_auth_pam_handler(ngx_http_request_t *r); /* Function that authenticates the user -- is the only function that uses PAM */ static ngx_int_t ngx_http_auth_pam_authenticate(ngx_http_request_t *r, ngx_http_auth_pam_ctx_t *ctx, ngx_str_t *passwd, void *conf); static ngx_int_t ngx_http_auth_pam_set_realm(ngx_http_request_t *r, ngx_str_t *realm); static void *ngx_http_auth_pam_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_auth_pam_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_auth_pam_init(ngx_conf_t *cf); static char *ngx_http_auth_pam(ngx_conf_t *cf, void *post, void *data); static ngx_conf_post_handler_pt ngx_http_auth_pam_p = ngx_http_auth_pam; static int ngx_auth_pam_talker(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr); static ngx_command_t ngx_http_auth_pam_commands[] = { { ngx_string("auth_pam"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_pam_loc_conf_t, realm), &ngx_http_auth_pam_p }, { ngx_string("auth_pam_service_name"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_pam_loc_conf_t, service_name), NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_auth_pam_module_ctx = { NULL, /* preconfiguration */ ngx_http_auth_pam_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_auth_pam_create_loc_conf, /* create location configuration */ ngx_http_auth_pam_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_auth_pam_module = { NGX_MODULE_V1, &ngx_http_auth_pam_module_ctx, /* module context */ ngx_http_auth_pam_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; /* * ngx_auth_pam_talker: supply authentication information to PAM when asked * * Assumptions: * A password is asked for by requesting input without echoing * A username is asked for by requesting input _with_ echoing */ static int ngx_auth_pam_talker(int num_msg, const struct pam_message ** msg, struct pam_response ** resp, void *appdata_ptr) { int i; ngx_pam_userinfo *uinfo; struct pam_response *response; uinfo = (ngx_pam_userinfo *) appdata_ptr; response = NULL; /* parameter sanity checking */ if (!resp || !msg || !uinfo) return PAM_CONV_ERR; /* allocate memory to store response */ response = malloc(num_msg * sizeof(struct pam_response)); if (!response) return PAM_CONV_ERR; /* copy values */ for (i = 0; i < num_msg; i++) { /* initialize to safe values */ response[i].resp_retcode = 0; response[i].resp = 0; /* select response based on requested output style */ switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* on memory allocation failure, auth fails */ response[i].resp = strdup((const char *)uinfo->username.data); break; case PAM_PROMPT_ECHO_OFF: response[i].resp = strdup((const char *)uinfo->password.data); break; default: if (response) { free(response); } return PAM_CONV_ERR; } } /* everything okay, set PAM response values */ *resp = response; return PAM_SUCCESS; } static ngx_int_t ngx_http_auth_pam_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_auth_pam_ctx_t *ctx; ngx_http_auth_pam_loc_conf_t *alcf; alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_pam_module); if (alcf->realm.len == 0) { return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_auth_pam_module); if (ctx) { return ngx_http_auth_pam_authenticate(r, ctx, &ctx->passwd, alcf); } /* Decode http auth user and passwd, leaving values on the request */ rc = ngx_http_auth_basic_user(r); if (rc == NGX_DECLINED) { return ngx_http_auth_pam_set_realm(r, &alcf->realm); } if (rc == NGX_ERROR) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* Check user & password using PAM */ return ngx_http_auth_pam_authenticate(r, ctx, &ctx->passwd, alcf); } static ngx_int_t ngx_http_auth_pam_authenticate(ngx_http_request_t *r, ngx_http_auth_pam_ctx_t *ctx, ngx_str_t *passwd, void *conf) { ngx_int_t rc; ngx_http_auth_pam_loc_conf_t *alcf; ngx_pam_userinfo uinfo; struct pam_conv conv_info; /* PAM struct */ pam_handle_t *pamh; u_char *service_name; alcf = conf; size_t len; u_char *uname_buf, *p; /** * Get username and password, note that r->headers_in.user contains the * string 'user:pass', so we need to copy the username **/ for (len = 0; len < r->headers_in.user.len; len++) { if (r->headers_in.user.data[len] == ':') { break; } } uname_buf = ngx_palloc(r->pool, len+1); if (uname_buf == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } p = ngx_cpymem(uname_buf, r->headers_in.user.data , len); *p ='\0'; uinfo.username.data = uname_buf; uinfo.username.len = len; uinfo.password.data = r->headers_in.passwd.data; uinfo.password.len = r->headers_in.passwd.len; conv_info.conv = &ngx_auth_pam_talker; conv_info.appdata_ptr = (void *) &uinfo; pamh = NULL; /* Initialize PAM */ if (alcf->service_name.data == NULL) { service_name = (u_char *) NGX_PAM_SERVICE_NAME; } else { service_name = alcf->service_name.data; } if ((rc = pam_start((const char *) service_name, (const char *) uinfo.username.data, &conv_info, &pamh)) != PAM_SUCCESS) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "PAM: Could not start pam service: %s", pam_strerror(pamh, rc)); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* try to authenticate user, log error on failure */ if ((rc = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "PAM: user '%s' - not authenticated: %s", uinfo.username.data, pam_strerror(pamh, rc)); pam_end(pamh, PAM_SUCCESS); return ngx_http_auth_pam_set_realm(r, &alcf->realm); } /* endif authenticate */ /* check that the account is healthy */ if ((rc = pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "PAM: user '%s' - invalid account: %s", uinfo.username.data, pam_strerror(pamh, rc)); pam_end(pamh, PAM_SUCCESS); return ngx_http_auth_pam_set_realm(r, &alcf->realm); } pam_end(pamh, PAM_SUCCESS); return NGX_OK; } static ngx_int_t ngx_http_auth_pam_set_realm(ngx_http_request_t *r, ngx_str_t *realm) { r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); if (r->headers_out.www_authenticate == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->headers_out.www_authenticate->hash = 1; r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; r->headers_out.www_authenticate->value = *realm; return NGX_HTTP_UNAUTHORIZED; } static void * ngx_http_auth_pam_create_loc_conf(ngx_conf_t *cf) { ngx_http_auth_pam_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_pam_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } return conf; } static char * ngx_http_auth_pam_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_auth_pam_loc_conf_t *prev = parent; ngx_http_auth_pam_loc_conf_t *conf = child; if (conf->realm.data == NULL) { conf->realm = prev->realm; } if (conf->service_name.data == NULL) { conf->service_name = prev->service_name; } return NGX_CONF_OK; } static ngx_int_t ngx_http_auth_pam_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_auth_pam_handler; return NGX_OK; } static char * ngx_http_auth_pam(ngx_conf_t *cf, void *post, void *data) { ngx_str_t *realm = data; size_t len; u_char *basic, *p; if (ngx_strcmp(realm->data, "off") == 0) { realm->len = 0; realm->data = (u_char *) ""; return NGX_CONF_OK; } len = sizeof("Basic realm=\"") - 1 + realm->len + 1; basic = ngx_palloc(cf->pool, len); if (basic == NULL) { return NGX_CONF_ERROR; } p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1); p = ngx_cpymem(p, realm->data, realm->len); *p = '"'; realm->len = len; realm->data = basic; return NGX_CONF_OK; } /* SVN Id: $Id: ngx_http_auth_pam_module.c 4487 2010-11-15 09:57:03Z sto $ */ debian/modules/ngx_http_substitutions_filter_module/0000755000000000000000000000000012305451341020420 5ustar debian/modules/ngx_http_substitutions_filter_module/doc/0000755000000000000000000000000012305451341021165 5ustar debian/modules/ngx_http_substitutions_filter_module/doc/README.google_code_home_page.wiki0000644000000000000000000000671212305451333027267 0ustar = nginx_substitutions_filter = _Note: this module is not distributed with the Nginx source. Installation instructions can be found [#Installation below]._ == Description == `nginx_substitutions_filter` is a filter module which can do both regular expression and fixed string substitutions on response bodies. This module is quite different from the Nginx's native Substitution Module. It scans the output chains buffer and matches string line by line, just like Apache's [http://httpd.apache.org/docs/trunk/mod/mod_substitute.html mod_substitute]. == Example == {{{ location / { subs_filter_types text/html text/css text/xml; subs_filter st(\d*).example.com $1.example.com ir; subs_filter a.example.com s.example.com; } }}} == Directives == * [#subs_filter_types subs_filter_types] * [#subs_filter subs_filter] === subs_filter_types === `syntax:` _subs_filter_types mime-type `[`mime-types`]` _ `default:` _subs_filter_types text/html_ `context:` _http, server, location_ _subs_filter_types_ is used to specify which content types should be checked for _subs_filter_. The default is only _text/html_. This module just works with plain text. If the response is compressed, it can't uncompress the response and will ignore this response. This module can be compatible with gzip filter module. But it will not work with proxy compressed response. You can disable the compressed response like this: proxy_set_header Accept-Encoding ""; === subs_filter === `syntax:` _subs_filter source_str destination_str `[`gior`]` _ `default:` _none_ `context:` _http, server, location_ _subs_filter_ allows replacing source string(regular expression or fixed) in the nginx response with destination string. Substitution text may contain variables. More than one substitution rules per location is supported. The meaning of the third flags are: * _g_(default): Replace all the match strings. * _i_: Perform a case-insensitive match. * _o_: Just replace the first one. * _r_: The pattern is treated as a regular expression, default is fixed string. == Installation == To install, get the source with subversion: {{{ git clone git://github.com/yaoweibin/ngx_http_substitutions_filter_module.git }}} and then compile nginx with the following option: {{{ ./configure --add-module=/path/to/module }}} == Known issue == * == CHANGES == Changes with nginx_substitutions_filter 0.6.0 2012-06-30 * refactor this module Changes with nginx_substitutions_filter 0.5.2 2010-08-11 * do many optimizing for this module * fix a bug of buffer overlap * fix a segment fault bug when output chain return NGX_AGAIN. * fix a bug about last buffer with no linefeed. This may cause segment fault. Thanks for Josef Fröhle Changes with nginx_substitutions_filter 0.5 2010-04-15 * refactor the source structure, create branches of dev * fix a bug of small chunk of buffers causing lose content * fix the bug of last_buf and the nginx's compatibility above 0.8.25 * fix a bug with unwanted capture config error in fix string substitution * add feature of regex captures Changes with nginx_substitutions_filter 0.4 2009-12-23 * fix many bugs Changes with nginx_substitutions_filter 0.3 2009-02-04 * Initial public release == Reporting a bug == Questions/patches may be directed to Weibin Yao, yaoweibin@gmail.com. debian/modules/ngx_http_substitutions_filter_module/doc/README.wiki0000644000000000000000000000702712305451333023016 0ustar = nginx_substitutions_filter = ''Note: this module is not distributed with the Nginx source. Installation instructions can be found [[#Installation|below]].'' == Description == '''nginx_substitutions_filter''' is a filter module which can do both regular expression and fixed string substitutions on response bodies. This module is quite different from the Nginx's native Substitution Module. It scans the output chains buffer and matches string line by line, just like Apache's [http://httpd.apache.org/docs/trunk/mod/mod_substitute.html mod_substitute]. == Example == location / { subs_filter_types text/html text/css text/xml; subs_filter st(\d*).example.com $1.example.com ir; subs_filter a.example.com s.example.com; } == Directives == * [[#subs_filter_types|subs_filter_types]] * [[#subs_filter|subs_filter]] === subs_filter_types === '''syntax:''' ''subs_filter_types mime-type [mime-types] '' '''default:''' ''subs_filter_types text/html'' '''context:''' ''http, server, location'' ''subs_filter_types'' is used to specify which content types should be checked for ''subs_filter''. The default is only ''text/html''. This module just works with plain text. If the response is compressed, it can't uncompress the response and will ignore this response. This module can be compatible with gzip filter module. But it will not work with proxy compressed response. You can disable the compressed response like this: proxy_set_header Accept-Encoding ""; === subs_filter === '''syntax:''' ''subs_filter source_str destination_str [gior] '' '''default:''' ''none'' '''context:''' ''http, server, location'' ''subs_filter'' allows replacing source string(regular expression or fixed) in the nginx response with destination string. Substitution text may contain variables. More than one substitution rules per location is supported. The meaning of the third flags are: * ''g''(default): Replace all the match strings. * ''i'': Perform a case-insensitive match. * ''o'': Just replace the first one. * ''r'': The pattern is treated as a regular expression, default is fixed string. == Installation == To install, get the source with subversion: git clone git://github.com/yaoweibin/ngx_http_substitutions_filter_module.git and then compile nginx with the following option: ./configure --add-module=/path/to/module == Known issue == * == CHANGES == Changes with nginx_substitutions_filter 0.6.0 2012-06-30 * refactor this module Changes with nginx_substitutions_filter 0.5.2 2010-08-11 * do many optimizing for this module * fix a bug of buffer overlap * fix a segment fault bug when output chain return NGX_AGAIN. * fix a bug about last buffer with no linefeed. This may cause segment fault. Thanks for Josef Fröhle Changes with nginx_substitutions_filter 0.5 2010-04-15 * refactor the source structure, create branches of dev * fix a bug of small chunk of buffers causing lose content * fix the bug of last_buf and the nginx's compatibility above 0.8.25 * fix a bug with unwanted capture config error in fix string substitution * add feature of regex captures Changes with nginx_substitutions_filter 0.4 2009-12-23 * fix many bugs Changes with nginx_substitutions_filter 0.3 2009-02-04 * Initial public release == Reporting a bug == Questions/patches may be directed to Weibin Yao, yaoweibin@gmail.com. debian/modules/ngx_http_substitutions_filter_module/doc/README.html0000644000000000000000000001342012305451333023011 0ustar nginx_substitutions_filter


nginx_substitutions_filter

Note: this module is not distributed with the Nginx source. Installation instructions can be found below. >

Description

nginx_substitutions_filter is a filter module which can do both regular expression and fixed string substitutions on response bodies. This module is quite different from the Nginx's native Substitution Module. It scans the output chains buffer and matches string line by line, just like Apache's mod_substitute (http://httpd.apache.org/docs/trunk/mod/mod_substitute.html).

Example

location / {

    subs_filter_types text/html text/css text/xml;
    subs_filter st(\d*).example.com $1.example.com ir;
    subs_filter a.example.com s.example.com;

}

Directives

subs_filter_types

syntax: subs_filter_types mime-type [mime-types]

default: subs_filter_types text/html

context: http, server, location

subs_filter_types is used to specify which content types should be checked for subs_filter. The default is only text/html.

This module just works with plain text. If the response is compressed, it can't uncompress the response and will ignore this response. This module can be compatible with gzip filter module. But it will not work with proxy compressed response. You can disable the compressed response like this:

proxy_set_header Accept-Encoding ``'';

subs_filter

syntax: subs_filter source_str destination_str [gior]

default: none

context: http, server, location

subs_filter allows replacing source string(regular expression or fixed) in the nginx response with destination string. Substitution text may contain variables. More than one substitution rules per location is supported. The meaning of the third flags are:

  • g(default): Replace all the match strings.

  • i: Perform a case-insensitive match.

  • o: Just replace the first one.

  • r: The pattern is treated as a regular expression, default is fixed string.

Installation

To install, get the source with subversion:

git clone git://github.com/yaoweibin/ngx_http_substitutions_filter_module.git

and then compile nginx with the following option:

./configure --add-module=/path/to/module

Known issue

*

CHANGES

Changes with nginx_substitutions_filter 0.6.0 2012-06-30

  • refactor this module

Changes with nginx_substitutions_filter 0.5.2 2010-08-11

  • do many optimizing for this module

  • fix a bug of buffer overlap

  • fix a segment fault bug when output chain return NGX_AGAIN.

  • fix a bug about last buffer with no linefeed. This may cause segment fault. Thanks for Josef Fröhle

Changes with nginx_substitutions_filter 0.5 2010-04-15

  • refactor the source structure, create branches of dev

  • fix a bug of small chunk of buffers causing lose content

  • fix the bug of last_buf and the nginx's compatibility above 0.8.25

  • fix a bug with unwanted capture config error in fix string substitution

  • add feature of regex captures

Changes with nginx_substitutions_filter 0.4 2009-12-23

  • fix many bugs

Changes with nginx_substitutions_filter 0.3 2009-02-04

  • Initial public release

Reporting a bug

Questions/patches may be directed to Weibin Yao, yaoweibin@gmail.com.

debian/modules/ngx_http_substitutions_filter_module/config0000644000000000000000000000031012305451333021603 0ustar ngx_addon_name=ngx_http_subs_filter_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_subs_filter_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_subs_filter_module.c" debian/modules/ngx_http_substitutions_filter_module/CHANGES0000644000000000000000000000241712305451333021420 0ustar Changes with nginx_substitutions_filter 0.6.2 2012-08-26 *) fixed a bug of buffer overlap *) fixed a bug with last zero buffer Changes with nginx_substitutions_filter 0.6.0 2012-06-30 *) refactor this module Changes with nginx_substitutions_filter 0.5.2 2010-08-11 *) do many optimizing for this module *) fix a bug of buffer overlap *) fix a segment fault bug when output chain return NGX_AGAIN. *) fix a bug about last buffer with no linefeed. This may cause segment fault. Thanks for Josef Fröhle Changes with nginx_substitutions_filter 0.5 2010-04-15 *) refactor the source structure, create branches of dev *) fix a bug of small chunk of buffers causing lose content *) fix the bug of last_buf and the nginx's compatibility above 0.8.25 *) fix a bug with unwanted capture config error in fix string substitution *) add feature of regex captures Changes with nginx_substitutions_filter 0.4 2009-12-23 *) fix many bugs Changes with nginx_substitutions_filter 0.3 2009-02-04 *) Initial public release debian/modules/ngx_http_substitutions_filter_module/test/0000755000000000000000000000000012305451341021377 5ustar debian/modules/ngx_http_substitutions_filter_module/test/lib/0000755000000000000000000000000012305451341022145 5ustar debian/modules/ngx_http_substitutions_filter_module/test/lib/Test/0000755000000000000000000000000012305451341023064 5ustar debian/modules/ngx_http_substitutions_filter_module/test/lib/Test/Nginx.pm0000644000000000000000000002242612305451334024515 0ustar package Test::Nginx; use strict; use warnings; our $VERSION = '0.17'; __END__ =encoding utf-8 =head1 NAME Test::Nginx - Testing modules for Nginx C module development =head1 DESCRIPTION This distribution provides two testing modules for Nginx C module development: =over =item * L =item * L =back All of them are based on L. Usually, L is preferred because it works on a much lower level and not that fault tolerant like L. Also, a lot of connection hang issues (like wrong C<< r->main->count >> value in nginx 0.8.x) can only be captured by L because Perl's L client will close the connection itself which will conceal such issues from the testers. Test::Nginx automatically starts an nginx instance (from the C env) rooted at t/servroot/ and the default config template makes this nginx instance listen on the port C<1984> by default. One can specify a different port number by setting his port number to the C environment, as in export TEST_NGINX_PORT=1989 =head2 etcproxy integration The default settings in etcproxy (https://github.com/chaoslawful/etcproxy) makes this small TCP proxy split the TCP packets into bytes and introduce 1 ms latency among them. There's usually various TCP chains that we can put etcproxy into, for example =head3 Test::Nginx <=> nginx $ ./etcproxy 1234 1984 Here we tell etcproxy to listen on port 1234 and to delegate all the TCP traffic to the port 1984, the default port that Test::Nginx makes nginx listen to. And then we tell Test::Nginx to test against the port 1234, where etcproxy listens on, rather than the port 1984 that nginx directly listens on: $ TEST_NGINX_CLIENT_PORT=1234 prove -r t/ Then the TCP chain now looks like this: Test::Nginx <=> etcproxy (1234) <=> nginx (1984) So etcproxy can effectively emulate extreme network conditions and exercise "unusual" code paths in your nginx server by your tests. In practice, *tons* of weird bugs can be captured by this setting. Even ourselves didn't expect that this simple approach is so effective. =head3 nginx <=> memcached We first start the memcached server daemon on port 11211: memcached -p 11211 -vv and then we another etcproxy instance to listen on port 11984 like this $ ./etcproxy 11984 11211 Then we tell our t/foo.t test script to connect to 11984 rather than 11211: # foo.t use Test::Nginx::Socket; repeat_each(1); plan tests => 2 * repeat_each() * blocks(); $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211; # make this env take a default value run_tests(); __DATA__ === TEST 1: sanity --- config location /foo { set $memc_cmd set; set $memc_key foo; set $memc_value bar; memc_pass 127.0.0.1:$TEST_NGINX_MEMCACHED_PORT; } --- request GET /foo --- response_body_like: STORED The Test::Nginx library will automatically expand the special macro C<$TEST_NGINX_MEMCACHED_PORT> to the environment with the same name. You can define your own C<$TEST_NGINX_BLAH_BLAH_PORT> macros as long as its prefix is C and all in upper case letters. And now we can run your test script against the etcproxy port 11984: TEST_NGINX_MEMCACHED_PORT=11984 prove t/foo.t Then the TCP chains look like this: Test::Nginx <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211) If C is not set, then it will take the default value 11211, which is what we want when there's no etcproxy configured: Test::Nginx <=> nginx (1984) <=> memcached (11211) This approach also works for proxied mysql and postgres traffic. Please see the live test suite of ngx_drizzle and ngx_postgres for more details. Usually we set both C and C (and etc) at the same time, effectively yielding the following chain: Test::Nginx <=> etcproxy (1234) <=> nginx (1984) <=> etcproxy (11984) <=> memcached (11211) as long as you run two separate etcproxy instances in two separate terminals. It's easy to verify if the traffic actually goes through your etcproxy server. Just check if the terminal running etcproxy emits outputs. By default, etcproxy always dump out the incoming and outgoing data to stdout/stderr. =head2 valgrind integration Test::Nginx has integrated support for valgrind (L) even though by default it does not bother running it with the tests because valgrind will significantly slow down the test sutie. First ensure that your valgrind executable visible in your PATH env. And then run your test suite with the C env set to true: TEST_NGINX_USE_VALGRIND=1 prove -r t If you see false alarms, you do have a chance to skip them by defining a ./valgrind.suppress file at the root of your module source tree, as in L This is the suppression file for ngx_drizzle. Test::Nginx will automatically use it to start nginx with valgrind memcheck if this file does exist at the expected location. If you do see a lot of "Connection refused" errors while running the tests this way, then you probably have a slow machine (or a very busy one) that the default waiting time is not sufficient for valgrind to start. You can define the sleep time to a larger value by setting the C env: TEST_NGINX_SLEEP=1 prove -r t The time unit used here is "second". The default sleep setting just fits my ThinkPad (C). Applying the no-pool patch to your nginx core is recommended while running nginx with valgrind: L The nginx memory pool can prevent valgrind from spotting lots of invalid memory reads/writes as well as certain double-free errors. We did find a lot more memory issues in many of our modules when we first introduced the no-pool patch in practice ;) There's also more advanced features in Test::Nginx that have never documented. I'd like to write more about them in the near future ;) =head1 Nginx C modules that use Test::Nginx to drive their test suites =over =item ngx_echo L =item ngx_headers_more L =item ngx_chunkin L =item ngx_memc L =item ngx_drizzle L =item ngx_rds_json L =item ngx_xss L =item ngx_srcache L =item ngx_lua L =item ngx_set_misc L =item ngx_array_var L =item ngx_form_input L =item ngx_iconv L =item ngx_set_cconv L =item ngx_postgres L =item ngx_coolkit L =back =head1 SOURCE REPOSITORY This module has a Git repository on Github, which has access for all. http://github.com/agentzh/test-nginx If you want a commit bit, feel free to drop me a line. =head1 AUTHORS agentzh (章亦春) C<< >> Antoine BONAVITA C<< >> =head1 COPYRIGHT & LICENSE Copyright (c) 2009-2011, Taobao Inc., Alibaba Group (L). Copyright (c) 2009-2011, agentzh C<< >>. Copyright (c) 2011, Antoine Bonavita C<< >>. This module is licensed under the terms of the BSD license. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: =over =item * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. =item * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. =item * Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. =back THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =head1 SEE ALSO L, L, L. debian/modules/ngx_http_substitutions_filter_module/test/lib/Test/Nginx/0000755000000000000000000000000012305451341024147 5ustar debian/modules/ngx_http_substitutions_filter_module/test/lib/Test/Nginx/Socket.pm0000644000000000000000000015416212305451334025750 0ustar package Test::Nginx::Socket; use lib 'lib'; use lib 'inc'; use Test::Base -Base; our $VERSION = '0.17'; use Encode; use Data::Dumper; use Time::HiRes qw(sleep time); use Test::LongString; use List::MoreUtils qw( any ); use IO::Select (); our $ServerAddr = 'localhost'; our $Timeout = $ENV{TEST_NGINX_TIMEOUT} || 2; use Test::Nginx::Util qw( setup_server_root write_config_file get_canon_version get_nginx_version trim show_all_chars parse_headers run_tests $ServerPortForClient $ServerPort $PidFile $ServRoot $ConfFile $RunTestHelper $RepeatEach worker_connections master_process_enabled config_preamble repeat_each workers master_on log_level no_shuffle no_root_location server_root html_dir server_port no_nginx_manager ); #use Smart::Comments::JSON '###'; use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); use POSIX qw(EAGAIN); use IO::Socket; #our ($PrevRequest, $PrevConfig); our $NoLongString = undef; our @EXPORT = qw( plan run_tests run_test repeat_each config_preamble worker_connections master_process_enabled no_long_string workers master_on log_level no_shuffle no_root_location server_addr server_root html_dir server_port timeout no_nginx_manager ); sub send_request ($$$$@); sub run_test_helper ($$); sub error_event_handler ($); sub read_event_handler ($); sub write_event_handler ($); sub no_long_string () { $NoLongString = 1; } sub server_addr (@) { if (@_) { #warn "setting server addr to $_[0]\n"; $ServerAddr = shift; } else { return $ServerAddr; } } sub timeout (@) { if (@_) { $Timeout = shift; } else { $Timeout; } } $RunTestHelper = \&run_test_helper; # This will parse a "request"" string. The expected format is: # - One line for the HTTP verb (POST, GET, etc.) plus optional relative URL # (default is /) plus optional HTTP version (default is HTTP/1.1). # - More lines considered as the body of the request. # Most people don't care about headers and this is enough. # # This function will return a reference to a hash with the parsed elements # plus information on the parsing itself like "how many white spaces were # skipped before the VERB" (skipped_before_method), "was the version provided" # (http_ver_size = 0). sub parse_request ($$) { my ( $name, $rrequest ) = @_; open my $in, '<', $rrequest; my $first = <$in>; if ( !$first ) { Test::More::BAIL_OUT("$name - Request line should be non-empty"); die; } #$first =~ s/^\s+|\s+$//gs; my ($before_meth, $meth, $after_meth); my ($rel_url, $rel_url_size, $after_rel_url); my ($http_ver, $http_ver_size, $after_http_ver); my $end_line_size; if ($first =~ /^(\s*)(\S+)( *)((\S+)( *))?((\S+)( *))?(\s*)/) { $before_meth = defined $1 ? length($1) : undef; $meth = $2; $after_meth = defined $3 ? length($3) : undef; $rel_url = $5; $rel_url_size = defined $5 ? length($5) : undef; $after_rel_url = defined $6 ? length($6) : undef; $http_ver = $8; if (!defined $8) { $http_ver_size = undef; } else { $http_ver_size = defined $8 ? length($8) : undef; } if (!defined $9) { $after_http_ver = undef; } else { $after_http_ver = defined $9 ? length($9) : undef; } $end_line_size = defined $10 ? length($10) : undef; } else { Test::More::BAIL_OUT("$name - Request line is not valid. Should be 'meth [url [version]]'"); die; } if ( !defined $rel_url ) { $rel_url = '/'; $rel_url_size = 0; $after_rel_url = 0; } if ( !defined $http_ver ) { $http_ver = 'HTTP/1.1'; $http_ver_size = 0; $after_http_ver = 0; } #my $url = "http://localhost:$ServerPortForClient" . $rel_url; my $content = do { local $/; <$in> }; my $content_size; if ( !defined $content ) { $content = ""; $content_size = 0; } else { $content_size = length($content); } #warn Dumper($content); close $in; return { method => $meth, url => $rel_url, content => $content, http_ver => $http_ver, skipped_before_method => $before_meth, method_size => length($meth), skipped_after_method => $after_meth, url_size => $rel_url_size, skipped_after_url => $after_rel_url, http_ver_size => $http_ver_size, skipped_after_http_ver => $after_http_ver + $end_line_size, content_size => $content_size, }; } # From a parsed request, builds the "moves" to apply to the original request # to transform it (e.g. add missing version). Elements of the returned array # are of 2 types: # - d : number of characters to remove. # - s_* : number of characters (s_s) to replace by value (s_v). sub get_moves($) { my ($parsed_req) = @_; return ({d => $parsed_req->{skipped_before_method}}, {s_s => $parsed_req->{method_size}, s_v => $parsed_req->{method}}, {d => $parsed_req->{skipped_after_method}}, {s_s => $parsed_req->{url_size}, s_v => $parsed_req->{url}}, {d => $parsed_req->{skipped_after_url}}, {s_s => $parsed_req->{http_ver_size}, s_v => $parsed_req->{http_ver}}, {d => $parsed_req->{skipped_after_http_ver}}, {s_s => 0, s_v => $parsed_req->{headers}}, {s_s => $parsed_req->{content_size}, s_v => $parsed_req->{content}} ); } # Apply moves (see above) to an array of packets that correspond to a request. # The use of this function is explained in the build_request_from_packets # function. sub apply_moves($$) { my ($r_packet, $r_move) = @_; my $current_packet = shift @$r_packet; my $current_move = shift @$r_move; my $in_packet_cursor = 0; my @result = (); while (defined $current_packet) { if (!defined $current_move) { push @result, $current_packet; $current_packet = shift @$r_packet; $in_packet_cursor = 0; } elsif (defined $current_move->{d}) { # Remove stuff from packet if ($current_move->{d} > length($current_packet) - $in_packet_cursor) { # Eat up what is left of packet. $current_move->{d} -= length($current_packet) - $in_packet_cursor; if ($in_packet_cursor > 0) { # Something in packet from previous iteration. push @result, $current_packet; } $current_packet = shift @$r_packet; $in_packet_cursor = 0; } else { # Remove from current point in current packet substr($current_packet, $in_packet_cursor, $current_move->{d}) = ''; $current_move = shift @$r_move; } } else { # Substitute stuff if ($current_move->{s_s} > length($current_packet) - $in_packet_cursor) { # {s_s=>3, s_v=>GET} on ['GE', 'T /foo'] $current_move->{s_s} -= length($current_packet) - $in_packet_cursor; substr($current_packet, $in_packet_cursor) = substr($current_move->{s_v}, 0, length($current_packet) - $in_packet_cursor); push @result, $current_packet; $current_move->{s_v} = substr($current_move->{s_v}, length($current_packet) - $in_packet_cursor); $current_packet = shift @$r_packet; $in_packet_cursor = 0; } else { substr($current_packet, $in_packet_cursor, $current_move->{s_s}) = $current_move->{s_v}; $in_packet_cursor += length($current_move->{s_v}); $current_move = shift @$r_move; } } } return \@result; } # Given a request as an array of packets, will parse it, append the appropriate # headers and return another array of packets. # The function implemented here can be high-level summarized as: # 1 - Concatenate all packets to obtain a string representation of request. # 2 - Parse the string representation # 3 - Get the "moves" from the parsing # 4 - Apply the "moves" to the packets. sub build_request_from_packets($$$$$) { my ( $name, $more_headers, $is_chunked, $conn_header, $request_packets ) = @_; # Concatenate packets as a string my $parsable_request = ''; my @packet_length; for my $one_packet (@$request_packets) { $parsable_request .= $one_packet; push @packet_length, length($one_packet); } # Parse the string representation. my $parsed_req = parse_request( $name, \$parsable_request ); # Append headers my $len_header = ''; if ( !$is_chunked && defined $parsed_req->{content} && $parsed_req->{content} ne '' && $more_headers !~ /\bContent-Length:/ ) { $parsed_req->{content} =~ s/^\s+|\s+$//gs; $len_header .= "Content-Length: " . length( $parsed_req->{content} ) . "\r\n"; } $parsed_req->{method} .= ' '; $parsed_req->{url} .= ' '; $parsed_req->{http_ver} .= "\r\n"; $parsed_req->{headers} = "Host: localhost\r\nConnection: $conn_header\r\n$more_headers$len_header\r\n"; # Get the moves from parsing my @elements_moves = get_moves($parsed_req); # Apply them to the packets. return apply_moves($request_packets, \@elements_moves); } # Returns an array of array of hashes from the block. Each element of # the first-level array is a request. # Each request is an array of the "packets" to be sent. Each packet is a # string to send, with an (optionnal) delay before sending it. # This function parses (and therefore defines the syntax) of "request*" # sections. See documentation for supported syntax. sub get_req_from_block ($) { my ($block) = @_; my $name = $block->name; my @req_list = (); if ( defined $block->raw_request ) { # Should be deprecated. if ( ref $block->raw_request && ref $block->raw_request eq 'ARRAY' ) { # User already provided an array. So, he/she specified where the # data should be split. This allows for backward compatibility but # should use request with arrays as it provides the same functionnality. my @rr_list = (); for my $elt ( @{ $block->raw_request } ) { push @rr_list, {value => $elt}; } push @req_list, \@rr_list; } else { push @req_list, [{value => $block->raw_request}]; } } else { my $request; if ( defined $block->request_eval ) { diag "$name - request_eval DEPRECATED. Use request eval instead."; $request = eval $block->request_eval; if ($@) { warn $@; } } else { $request = $block->request; } my $is_chunked = 0; my $more_headers = ''; if ( $block->more_headers ) { my @headers = split /\n+/, $block->more_headers; for my $header (@headers) { next if $header =~ /^\s*\#/; my ( $key, $val ) = split /:\s*/, $header, 2; if ( lc($key) eq 'transfer-encoding' and $val eq 'chunked' ) { $is_chunked = 1; } #warn "[$key, $val]\n"; $more_headers .= "$key: $val\r\n"; } } if ( $block->pipelined_requests ) { my $reqs = $block->pipelined_requests; if ( !ref $reqs || ref $reqs ne 'ARRAY' ) { Test::More::BAIL_OUT( "$name - invalid entries in --- pipelined_requests"); } my $i = 0; my $prq = ""; for my $request (@$reqs) { my $conn_type; if ( $i++ == @$reqs - 1 ) { $conn_type = 'close'; } else { $conn_type = 'keep-alive'; } my $r_br = build_request_from_packets($name, $more_headers, $is_chunked, $conn_type, [$request] ); $prq .= $$r_br[0]; } push @req_list, [{value =>$prq}]; } else { # request section. if (!ref $request) { # One request and it is a good old string. my $r_br = build_request_from_packets($name, $more_headers, $is_chunked, 'Close', [$request] ); push @req_list, [{value => $$r_br[0]}]; } elsif (ref $request eq 'ARRAY') { # A bunch of requests... for my $one_req (@$request) { if (!ref $one_req) { # This request is a good old string. my $r_br = build_request_from_packets($name, $more_headers, $is_chunked, 'Close', [$one_req] ); push @req_list, [{value => $$r_br[0]}]; } elsif (ref $one_req eq 'ARRAY') { # Request expressed as a serie of packets my @packet_array = (); for my $one_packet (@$one_req) { if (!ref $one_packet) { # Packet is a string. push @packet_array, $one_packet; } elsif (ref $one_packet eq 'HASH'){ # Packet is a hash with a value... push @packet_array, $one_packet->{value}; } else { Test::More::BAIL_OUT "$name - Invalid syntax. $one_packet should be a string or hash with value."; } } my $transformed_packet_array = build_request_from_packets($name, $more_headers, $is_chunked, 'Close', \@packet_array); my @transformed_req = (); my $idx = 0; for my $one_transformed_packet (@$transformed_packet_array) { if (!ref $$one_req[$idx]) { push @transformed_req, {value => $one_transformed_packet}; } else { # Is a HASH (checked above as $one_packet) $$one_req[$idx]->{value} = $one_transformed_packet; push @transformed_req, $$one_req[$idx]; } $idx++; } push @req_list, \@transformed_req; } else { Test::More::BAIL_OUT "$name - Invalid syntax. $one_req should be a string or an array of packets."; } } } else { Test::More::BAIL_OUT( "$name - invalid ---request : MUST be string or array of requests"); } } } return \@req_list; } sub run_test_helper ($$) { my ( $block, $dry_run ) = @_; my $name = $block->name; my $r_req_list = get_req_from_block($block); if ( $#$r_req_list < 0 ) { Test::More::BAIL_OUT("$name - request empty"); } #warn "request: $req\n"; my $timeout = $block->timeout; if ( !defined $timeout ) { $timeout = $Timeout; } my $req_idx = 0; for my $one_req (@$r_req_list) { my $raw_resp; if ($dry_run) { $raw_resp = "200 OK HTTP/1.0\r\nContent-Length: 0\r\n\r\n"; } else { $raw_resp = send_request( $one_req, $block->raw_request_middle_delay, $timeout, $block->name ); } #warn "raw resonse: [$raw_resp]\n"; my ($n, $need_array); if ($block->pipelined_requests) { $n = @{ $block->pipelined_requests }; $need_array = 1; } else { $need_array = $#$r_req_list > 0; } again: #warn "!!! resp: [$raw_resp]"; if (!defined $raw_resp) { $raw_resp = ''; } my ( $res, $raw_headers, $left ) = parse_response( $name, $raw_resp ); if (!$n) { if ($left) { my $name = $block->name; $left =~ s/([\0-\037\200-\377])/sprintf('\x{%02x}',ord $1)/eg; warn "WARNING: $name - unexpected extra bytes after last chunk in ", "response: \"$left\"\n"; } } else { $raw_resp = $left; $n--; } check_error_code($block, $res, $dry_run, $req_idx, $need_array); check_raw_response_headers($block, $raw_headers, $dry_run, $req_idx, $need_array); check_response_headers($block, $res, $raw_headers, $dry_run, $req_idx, $need_array); check_response_body($block, $res, $dry_run, $req_idx, $need_array); $req_idx++; if ($n) { goto again; } } } # Helper function to retrieve a "check" (e.g. error_code) section. This also # checks that tests with arrays of requests are arrays themselves. sub get_indexed_value($$$$) { my ($name, $value, $req_idx, $need_array) = @_; if ($need_array) { if (ref $value && ref $value eq 'ARRAY') { return $$value[$req_idx]; } else { Test::More::BAIL_OUT("$name - You asked for many requests, the expected results should be arrays as well."); } } else { # One element but still provided as an array. if (ref $value && ref $value eq 'ARRAY') { if ($req_idx != 0) { Test::More::BAIL_OUT("$name - SHOULD NOT HAPPEN: idx != 0 and don't need array."); } else { return $$value[0]; } } else { return $value; } } } sub check_error_code($$$$$) { my ($block, $res, $dry_run, $req_idx, $need_array) = @_; my $name = $block->name; SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; if ( defined $block->error_code ) { is( $res->code || '', get_indexed_value($name, $block->error_code, $req_idx, $need_array), "$name - status code ok" ); } else { is( $res->code || '', 200, "$name - status code ok" ); } } } sub check_raw_response_headers($$$$$) { my ($block, $raw_headers, $dry_run, $req_idx, $need_array) = @_; my $name = $block->name; if ( defined $block->raw_response_headers_like ) { SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; my $expected = get_indexed_value($name, $block->raw_response_headers_like, $req_idx, $need_array); like $raw_headers, qr/$expected/s, "$name - raw resp headers like"; } } } sub check_response_headers($$$$$) { my ($block, $res, $raw_headers, $dry_run, $req_idx, $need_array) = @_; my $name = $block->name; if ( defined $block->response_headers ) { my $headers = parse_headers( get_indexed_value($name, $block->response_headers, $req_idx, $need_array)); while ( my ( $key, $val ) = each %$headers ) { if ( !defined $val ) { #warn "HIT"; SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; unlike $raw_headers, qr/^\s*\Q$key\E\s*:/ms, "$name - header $key not present in the raw headers"; } next; } my $actual_val = $res->header($key); if ( !defined $actual_val ) { $actual_val = ''; } SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; is $actual_val, $val, "$name - header $key ok"; } } } elsif ( defined $block->response_headers_like ) { my $headers = parse_headers( get_indexed_value($name, $block->response_headers_like, $req_idx, $need_array) ); while ( my ( $key, $val ) = each %$headers ) { my $expected_val = $res->header($key); if ( !defined $expected_val ) { $expected_val = ''; } SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; like $expected_val, qr/^$val$/, "$name - header $key like ok"; } } } } sub check_response_body() { my ($block, $res, $dry_run, $req_idx, $need_array) = @_; my $name = $block->name; if ( defined $block->response_body || defined $block->response_body_eval ) { my $content = $res->content; if ( defined $content ) { $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; $content =~ s/^Connection: TE, close\r\n//gms; } my $expected; if ( $block->response_body_eval ) { diag "$name - response_body_eval is DEPRECATED. Use response_body eval instead."; $expected = eval get_indexed_value($name, $block->response_body_eval, $req_idx, $need_array); if ($@) { warn $@; } } else { $expected = get_indexed_value($name, $block->response_body, $req_idx, $need_array); } if ( $block->charset ) { Encode::from_to( $expected, 'UTF-8', $block->charset ); } unless (ref $expected) { $expected =~ s/\$ServerPort\b/$ServerPort/g; $expected =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; } #warn show_all_chars($content); #warn "no long string: $NoLongString"; SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; if (ref $expected) { like $content, $expected, "$name - response_body - like"; } else { if ($NoLongString) { is( $content, $expected, "$name - response_body - response is expected" ); } else { is_string( $content, $expected, "$name - response_body - response is expected" ); } } } } elsif ( defined $block->response_body_like ) { my $content = $res->content; if ( defined $content ) { $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; } $content =~ s/^Connection: TE, close\r\n//gms; my $expected_pat = get_indexed_value($name, $block->response_body_like, $req_idx, $need_array); $expected_pat =~ s/\$ServerPort\b/$ServerPort/g; $expected_pat =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; my $summary = trim($content); SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; like( $content, qr/$expected_pat/s, "$name - response_body_like - response is expected ($summary)" ); } } elsif ( defined $block->response_body_unlike ) { my $content = $res->content; if ( defined $content ) { $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; } $content =~ s/^Connection: TE, close\r\n//gms; my $expected_pat = get_indexed_value($name, $block->response_body_unlike, $req_idx, $need_array); $expected_pat =~ s/\$ServerPort\b/$ServerPort/g; $expected_pat =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; my $summary = trim($content); SKIP: { skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; unlike( $content, qr/$expected_pat/s, "$name - response_body_like - response is expected ($summary)" ); } } sub parse_response($$) { my ( $name, $raw_resp ) = @_; my $left; my $raw_headers = ''; if ( $raw_resp =~ /(.*?\r\n)\r\n/s ) { #warn "\$1: $1"; $raw_headers = $1; } #warn "raw headers: $raw_headers\n"; my $res = HTTP::Response->parse($raw_resp); my $enc = $res->header('Transfer-Encoding'); my $len = $res->header('Content-Length'); if ( defined $enc && $enc eq 'chunked' ) { #warn "Found chunked!"; my $raw = $res->content; if ( !defined $raw ) { $raw = ''; } my $decoded = ''; while (1) { if ( $raw =~ /\G 0 [\ \t]* \r\n \r\n /gcsx ) { if ( $raw =~ /\G (.+) /gcsx ) { $left = $1; } last; } if ( $raw =~ m{ \G [\ \t]* ( [A-Fa-f0-9]+ ) [\ \t]* \r\n }gcsx ) { my $rest = hex($1); #warn "chunk size: $rest\n"; my $bit_sz = 32765; while ( $rest > 0 ) { my $bit = $rest < $bit_sz ? $rest : $bit_sz; #warn "bit: $bit\n"; if ( $raw =~ /\G(.{$bit})/gcs ) { $decoded .= $1; #warn "decoded: [$1]\n"; } else { fail( "$name - invalid chunked data received (not enought octets for the data section)" ); return; } $rest -= $bit; } if ( $raw !~ /\G\r\n/gcs ) { fail( "$name - invalid chunked data received (expected CRLF)." ); return; } } elsif ( $raw =~ /\G.+/gcs ) { fail "$name - invalid chunked body received: $&"; return; } else { fail "$name - no last chunk found - $raw"; return; } } #warn "decoded: $decoded\n"; $res->content($decoded); } elsif (defined $len && $len ne '' && $len >= 0) { my $raw = $res->content; if (length $raw < $len) { warn "WARNING: $name - response body truncated: ", "$len expected, but got ", length $raw, "\n"; } elsif (length $raw > $len) { my $content = substr $raw, 0, $len; $left = substr $raw, $len; $res->content($content); #warn "parsed body: [", $res->content, "]\n"; } } return ( $res, $raw_headers, $left ); } sub send_request ($$$$@) { my ( $req, $middle_delay, $timeout, $name, $tries ) = @_; my $sock = IO::Socket::INET->new( PeerAddr => $ServerAddr, PeerPort => $ServerPortForClient, Proto => 'tcp' ); if (! defined $sock) { $tries ||= 0; if ($tries < 3) { warn "Can't connect to $ServerAddr:$ServerPortForClient: $!\n"; sleep 1; return send_request($req, $middle_delay, $timeout, $name, $tries + 1); } else { die "Can't connect to $ServerAddr:$ServerPortForClient: $!\n"; } } my @req_bits = ref $req ? @$req : ($req); my $flags = fcntl $sock, F_GETFL, 0 or die "Failed to get flags: $!\n"; fcntl $sock, F_SETFL, $flags | O_NONBLOCK or die "Failed to set flags: $!\n"; my $ctx = { resp => '', write_offset => 0, buf_size => 1024, req_bits => \@req_bits, write_buf => (shift @req_bits)->{"value"}, middle_delay => $middle_delay, sock => $sock, name => $name, }; my $readable_hdls = IO::Select->new($sock); my $writable_hdls = IO::Select->new($sock); my $err_hdls = IO::Select->new($sock); while (1) { if ( $readable_hdls->count == 0 && $writable_hdls->count == 0 && $err_hdls->count == 0 ) { last; } my ( $new_readable, $new_writable, $new_err ) = IO::Select->select( $readable_hdls, $writable_hdls, $err_hdls, $timeout ); if ( !defined $new_err && !defined $new_readable && !defined $new_writable ) { # timed out timeout_event_handler($ctx); last; } for my $hdl (@$new_err) { next if !defined $hdl; error_event_handler($ctx); if ( $err_hdls->exists($hdl) ) { $err_hdls->remove($hdl); } if ( $readable_hdls->exists($hdl) ) { $readable_hdls->remove($hdl); } if ( $writable_hdls->exists($hdl) ) { $writable_hdls->remove($hdl); } for my $h (@$readable_hdls) { next if !defined $h; if ( $h eq $hdl ) { undef $h; last; } } for my $h (@$writable_hdls) { next if !defined $h; if ( $h eq $hdl ) { undef $h; last; } } close $hdl; } for my $hdl (@$new_readable) { next if !defined $hdl; my $res = read_event_handler($ctx); if ( !$res ) { # error occured if ( $err_hdls->exists($hdl) ) { $err_hdls->remove($hdl); } if ( $readable_hdls->exists($hdl) ) { $readable_hdls->remove($hdl); } if ( $writable_hdls->exists($hdl) ) { $writable_hdls->remove($hdl); } for my $h (@$writable_hdls) { next if !defined $h; if ( $h eq $hdl ) { undef $h; last; } } close $hdl; } } for my $hdl (@$new_writable) { next if !defined $hdl; my $res = write_event_handler($ctx); if ( !$res ) { # error occured if ( $err_hdls->exists($hdl) ) { $err_hdls->remove($hdl); } if ( $readable_hdls->exists($hdl) ) { $readable_hdls->remove($hdl); } if ( $writable_hdls->exists($hdl) ) { $writable_hdls->remove($hdl); } close $hdl; } elsif ( $res == 2 ) { if ( $writable_hdls->exists($hdl) ) { $writable_hdls->remove($hdl); } } } } return $ctx->{resp}; } sub timeout_event_handler ($) { my $ctx = shift; warn "ERROR: socket client: timed out - $ctx->{name}\n"; } sub error_event_handler ($) { warn "exception occurs on the socket: $!\n"; } sub write_event_handler ($) { my ($ctx) = @_; while (1) { return undef if !defined $ctx->{write_buf}; my $rest = length( $ctx->{write_buf} ) - $ctx->{write_offset}; #warn "offset: $write_offset, rest: $rest, length ", length($write_buf), "\n"; #die; if ( $rest > 0 ) { my $bytes; eval { $bytes = syswrite( $ctx->{sock}, $ctx->{write_buf}, $rest, $ctx->{write_offset} ); }; if ($@) { my $errmsg = "write failed: $@"; warn "$errmsg\n"; $ctx->{resp} = $errmsg; return undef; } if ( !defined $bytes ) { if ( $! == EAGAIN ) { #warn "write again..."; #sleep 0.002; return 1; } my $errmsg = "write failed: $!"; warn "$errmsg\n"; if ( !$ctx->{resp} ) { $ctx->{resp} = "$errmsg"; } return undef; } #warn "wrote $bytes bytes.\n"; $ctx->{write_offset} += $bytes; } else { my $next_send = shift @{ $ctx->{req_bits} } or return 2; $ctx->{write_buf} = $next_send->{'value'}; $ctx->{write_offset} = 0; my $wait_time; if (!defined $next_send->{'delay_before'}) { if (defined $ctx->{middle_delay}) { $wait_time = $ctx->{middle_delay}; } } else { $wait_time = $next_send->{'delay_before'}; } if ($wait_time) { #warn "sleeping.."; sleep $wait_time; } } } # impossible to reach here... return undef; } sub read_event_handler ($) { my ($ctx) = @_; while (1) { my $read_buf; my $bytes = sysread( $ctx->{sock}, $read_buf, $ctx->{buf_size} ); if ( !defined $bytes ) { if ( $! == EAGAIN ) { #warn "read again..."; #sleep 0.002; return 1; } $ctx->{resp} = "500 read failed: $!"; return undef; } if ( $bytes == 0 ) { return undef; # connection closed } $ctx->{resp} .= $read_buf; #warn "read $bytes ($read_buf) bytes.\n"; } # impossible to reach here... return undef; } 1; __END__ =encoding utf-8 =head1 NAME Test::Nginx::Socket - Socket-backed test scaffold for the Nginx C modules =head1 SYNOPSIS use Test::Nginx::Socket; plan tests => $Test::Nginx::Socket::RepeatEach * 2 * blocks(); run_tests(); __DATA__ === TEST 1: sanity --- config location /echo { echo_before_body hello; echo world; } --- request GET /echo --- response_body hello world --- error_code: 200 === TEST 2: set Server --- config location /foo { echo hi; more_set_headers 'Server: Foo'; } --- request GET /foo --- response_headers Server: Foo --- response_body hi === TEST 3: clear Server --- config location /foo { echo hi; more_clear_headers 'Server: '; } --- request GET /foo --- response_headers_like Server: nginx.* --- response_body hi === TEST 3: chunk size too small --- config chunkin on; location /main { echo_request_body; } --- more_headers Transfer-Encoding: chunked --- request eval "POST /main 4\r hello\r 0\r \r " --- error_code: 400 --- response_body_like: 400 Bad Request =head1 DESCRIPTION This module provides a test scaffold based on non-blocking L for automated testing in Nginx C module development. This class inherits from L, thus bringing all its declarative power to the Nginx C module testing practices. You need to terminate or kill any Nginx processes before running the test suite if you have changed the Nginx server binary. Normally it's as simple as killall nginx PATH=/path/to/your/nginx-with-memc-module:$PATH prove -r t This module will create a temporary server root under t/servroot/ of the current working directory and starts and uses the nginx executable in the PATH environment. You will often want to look into F when things go wrong ;) =head1 Sections supported The following sections are supported: =head2 config Content of this section will be included in the "server" part of the generated config file. This is the place where you want to put the "location" directive enabling the module you want to test. Example: location /echo { echo_before_body hello; echo world; } Sometimes you simply don't want to bother copying ten times the same configuration for the ten tests you want to run against your module. One way to do this is to write a config section only for the first test in your C<.t> file. All subsequent tests will re-use the same config. Please note that this depends on the order of test, so you should run C with variable C (see below for more on this variable). Please note that config section goes through environment variable expansion provided the variables to expand start with TEST_NGINX. So, the following is a perfectly legal (provided C is set correctly): location /main { echo_subrequest POST /sub -f $TEST_NGINX_HTML_DIR/blah.txt; } =head2 http_config Content of this section will be included in the "http" part of the generated config file. This is the place where you want to put the "upstream" directive you might want to test. Example: upstream database { postgres_server 127.0.0.1:$TEST_NGINX_POSTGRESQL_PORT dbname=ngx_test user=ngx_test password=wrong_pass; } As you guessed from the example above, this section goes through environment variable expansion (variables have to start with TEST_NGINX). =head2 main_config Content of this section will be included in the "main" part of the generated config file. This is very rarely used, except if you are testing nginx core itself. This section goes through environment variable expansion (variables have to start with TEST_NGINX). =head2 request This is probably the most important section. It defines the request(s) you are going to send to the nginx server. It offers a pretty powerful grammar which we are going to walk through one example at a time. In its most basic form, this section looks like that: --- request GET This will just do a GET request on the root (i.e. /) of the server using HTTP/1.1. Of course, you might want to test something else than the root of your web server and even use a different version of HTTP. This is possible: --- request GET /foo HTTP/1.0 Please note that specifying HTTP/1.0 will not prevent Test::Nginx from sending the C header. Actually Test::Nginx always sends 2 headers: C (with value localhost) and C (with value Close for simple requests and keep-alive for all but the last pipelined_requests). You can also add a content to your request: --- request POST /foo Hello world Test::Nginx will automatically calculate the content length and add the corresponding header for you. This being said, as soon as you want to POST real data, you will be interested in using the more_headers section and using the power of Test::Base filters to urlencode the content you are sending. Which gives us a slightly more realistic example: --- more_headers Content-type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST /rrd/foo value=".uri_escape("N:12345") Sometimes a test is more than one request. Typically you want to POST some data and make sure the data has been taken into account with a GET. You can do it using arrays: --- request eval ["POST /users name=foo", "GET /users/foo"] This way, REST-like interfaces are pretty easy to test. When you develop nifty nginx modules you will eventually want to test things with buffers and "weird" network conditions. This is where you split your request into network packets: --- request eval [["POST /users\nna", "me=foo"]] Here, Test::Nginx will first send the request line, the headers it automatically added for you and the first two letters of the body ("na" in our example) in ONE network packet. Then, it will send the next packet (here it's "me=foo"). When we talk about packets here, this is nto exactly correct as there is no way to guarantee the behavior of the TCP/IP stack. What Test::Nginx can guarantee is that this will result in two calls to C. A good way to make I sure the two calls result in two packets is to introduce a delay (let's say 2 seconds)before sending the second packet: --- request eval [["POST /users\nna", {value => "me=foo", delay_before => 2}]] Of course, everything can be combined till your brain starts boiling ;) : --- request eval use URI::Escape; my $val="value=".uri_escape("N:12346"); [["POST /rrd/foo ".substr($val, 0, 6), {value => substr($val, 6, 5), delay_before=>5}, substr($val, 11)], "GET /rrd/foo"] =head2 request_eval Use of this section is deprecated and tests using it should replace it with a C section with an C filter. More explicitly: --- request_eval "POST /echo_body hello\x00\x01\x02 world\x03\x04\xff" should be replaced by: --- request eval "POST /echo_body hello\x00\x01\x02 world\x03\x04\xff" =head2 pipelined_requests Specify pipelined requests that use a single keep-alive connection to the server. Here is an example from ngx_lua's test suite: === TEST 7: discard body --- config location = /foo { content_by_lua ' ngx.req.discard_body() ngx.say("body: ", ngx.var.request_body) '; } location = /bar { content_by_lua ' ngx.req.read_body() ngx.say("body: ", ngx.var.request_body) '; } --- pipelined_requests eval ["POST /foo hello, world", "POST /bar hiya, world"] --- response_body eval ["body: nil\n", "body: hiya, world\n"] =head2 more_headers Adds the content of this section as headers to the request being sent. Example: --- more_headers X-Foo: blah This will add C to the request (on top of the automatically generated headers like C, C and potentially C). =head2 response_body The expected value for the body of the submitted request. --- response_body hello If the test is made of multiple requests, then the response_body B be an array and each request B return the corresponding expected body: --- request eval ["GET /hello", "GET /world"] --- response_body eval ["hello", "world"] =head2 response_body_eval Use of this section is deprecated and tests using it should replace it with a C section with an C filter. Therefore: --- response_body_eval "hello\x00\x01\x02 world\x03\x04\xff" should be replaced by: --- response_body eval "hello\x00\x01\x02 world\x03\x04\xff" =head2 response_body_like The body returned by the request MUST match the pattern provided by this section. Example: --- response_body_like ^elapsed 0\.00[0-5] sec\.$ If the test is made of multiple requests, then response_body_like B be an array and each request B match the corresponding pattern. =head2 response_headers The headers specified in this section are in the response sent by nginx. --- response_headers Content-Type: application/x-resty-dbd-stream Of course, you can specify many headers in this section: --- response_headers X-Resty-DBD-Module: Content-Type: application/x-resty-dbd-stream The test will be successful only if all headers are found in the response with the appropriate values. If the test is made of multiple requests, then response_headers B be an array and each element of the array is checked against the response to the corresponding request. =head2 response_headers_like The value of the headers returned by nginx match the patterns. --- response_headers_like X-Resty-DBD-Module: ngx_drizzle \d+\.\d+\.\d+ Content-Type: application/x-resty-dbd-stream This will check that the response's C is application/x-resty-dbd-stream and that the C matches C. The test will be successful only if all headers are found in the response and if the values match the patterns. If the test is made of multiple requests, then response_headers_like B be an array and each element of the array is checked against the response to the corresponding request. =head2 raw_response_headers_like Checks the headers part of the response against this pattern. This is particularly useful when you want to write tests of redirect functions that are not bound to the value of the port your nginx server (under test) is listening to: --- raw_response_headers_like: Location: http://localhost(?::\d+)?/foo\r\n As usual, if the test is made of multiple requests, then raw_response_headers_like B be an array. =head2 error_code The expected value of the HTTP response code. If not set, this is assumed to be 200. But you can expect other things such as a redirect: --- error_code: 302 If the test is made of multiple requests, then error_code B be an array with the expected value for the response status of each request in the test. =head2 raw_request The exact request to send to nginx. This is useful when you want to test soem behaviors that are not available with "request" such as an erroneous C header or splitting packets right in the middle of headers: --- raw_request eval ["POST /rrd/taratata HTTP/1.1\r Host: localhost\r Connection: Close\r Content-Type: application/", "x-www-form-urlencoded\r Content-Length:15\r\n\r\nvalue=N%3A12345"] This can also be useful to tests "invalid" request lines: --- raw_request GET /foo HTTP/2.0 THE_FUTURE_IS_NOW =head2 user_files With this section you can create a file that will be copied in the html directory of the nginx server under test. For example: --- user_files >>> blah.txt Hello, world will create a file named C in the html directory of the nginx server tested. The file will contain the text "Hello, world". =head2 skip_nginx =head2 skip_nginx2 Both string scalar and string arrays are supported as values. =head2 raw_request_middle_delay Delay in sec between sending successive packets in the "raw_request" array value. Also used when a request is split in packets. =head1 Environment variables All environment variables starting with C are expanded in the sections used to build the configuration of the server that tests automatically starts. The following environment variables are supported by this module: =head2 TEST_NGINX_NO_NGINX_MANAGER Defaults to 0. If set to 1, Test::Nginx module will not manage (configure/start/stop) the C process. Can be useful to run tests against an already configured (and running) nginx server. =head2 TEST_NGINX_NO_SHUFFLE Dafaults to 0. If set to 1, will make sure the tests are run in the order they appear in the test file (and not in random order). =head2 TEST_NGINX_USE_VALGRIND If set to 1, will start nginx with valgrind. nginx is actually started with C, the suppressions option being used only if there is actually a valgrind.suppress file. =head2 TEST_NGINX_BINARY The command to start nginx. Defaults to C. Can be used as an alternative to setting C to run a specific nginx instance. =head2 TEST_NGINX_LOG_LEVEL Value of the last argument of the C configuration directive. Defaults to C. =head2 TEST_NGINX_MASTER_PROCESS Value of the C configuration directive. Defaults to C. =head2 TEST_NGINX_SERVER_PORT Value of the port the server started by Test::Nginx will listen to. If not set, C is used. If C is not set, then C<1984> is used. See below for typical use. =head2 TEST_NGINX_CLIENT_PORT Value of the port Test::Nginx will diirect requests to. If not set, C is used. If C is not set, then C<1984> is used. A typical use of this feature is to test extreme network conditions by adding a "proxy" between Test::Nginx and nginx itself. This is described in the C section of this module README. =head2 TEST_NGINX_PORT A shortcut for setting both C and C. =head2 TEST_NGINX_SLEEP How much time (in seconds) should Test::Nginx sleep between two calls to C when sending request data. Defaults to 0. =head2 TEST_NGINX_FORCE_RESTART_ON_TEST Defaults to 1. If set to 0, Test::Nginx will not restart the nginx server when the config does not change between two tests. =head2 TEST_NGINX_SERVROOT The root of the nginx "hierarchy" (where you find the conf, *_tmp and logs directories). This value will be used with the C<-p> option of C. Defaults to appending C to the current directory. =head2 TEST_NGINX_IGNORE_MISSING_DIRECTIVES If set to 1 will SKIP all tests which C sections resulted in a C when trying to start C. Useful when you want to run tests on a build of nginx that does not include all modules it should. By default, these tests will FAIL. =head2 TEST_NGINX_EVENT_TYPE This environment can be used to specify a event API type to be used by Nginx. Possible values are C, C, C"; parse_str($_SERVER["HTTP_NAXSI_SIG"], $nsig); $include_body = 0; $msg = ""; $msg .= "The request emited from ".$nsig["ip"]." IP address has been blocked.\n"; $msg .= "While access page :\n"; $msg .= $_SERVER["HTTP_ORIG_URL"]."\n"; $msg .= "With arguments :\n"; $msg .= $_SERVER["HTTP_ORIG_ARGS"]."\n"; $msg .= "This request has been blocked for the following reasons :\n"; for ($i = 0; ; $i++) { if (isset($nsig["id".$i])) { if ($nsig["zone".$i] == "BODY") $include_body = 1; $pattern = get_forbidden_pattern($nsig["id".$i]); if (isset($nsig["var_name".$i]) && strlen($nsig["var_name".$i]) > 0) { $rulematch = "The forbidden caracter '".$pattern."' has been detected on '".$nsig["var_name".$i]."' variable"; $rulematch .= " in zone '".$nsig["zone".$i]."'\n"; $msg .= $rulematch; } else { $rulematch = "The forbidden caracter '".$pattern."' has been detected within '".$nsig["zone".$i]."' zone\n"; $msg .= $rulematch; } } else break; } if ($include_body == 1) { ob_start(); print_r($_POST); $output = ob_get_clean(); $msg .= "------- BODY DUMP ------\n".$output."\n-------------\n"; } $msg .= "-------- ORIG NAXSI SIG --------\n".$_SERVER['HTTP_NAXSI_SIG']."\n--------\n"; $_SESSION["msg"] = $msg; } else { $resp = recaptcha_check_answer ($private_key, $_SERVER["REMOTE_ADDR"], $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"]); if (!$resp->is_valid) { die ("The reCAPTCHA wasn't entered correctly. Go back and try it again." . "(reCAPTCHA said: " . $resp->error . ")"); } else { echo "Hey, here is your message :
".$_SESSION["msg"]."
"; destroy_session(); } } function get_forbidden_pattern($sig_id) { if (!file_exists(CORE_RULES_FILE)) die ("Cannot open ".CORE_RULES_FILE); $fd = fopen(CORE_RULES_FILE, "r"); while(($line = fgets($fd))) { if (($idx = strpos($line, "id:"))) { $rid = intval(substr($line, $idx+3)); if ($rid == $sig_id) { return (extract_pattern_from_line($line)); break; } } } fclose($fd); } function extract_pattern_from_line($line) { if (($rid = strpos($line, "rx:"))) $len = 3; else if (($rid = strpos($line, "str:"))) $len = 4; else return ("unable to extract pattern"); $tok_id = strpos(substr($line, $rid+$len), '"'); return (substr($line, $rid+$len, $tok_id)); } ?> debian/modules/naxsi/contrib/fp-reporter/README0000644000000000000000000000136512305451332016550 0ustar Fp-reporter 0.1 README 0) Description Fp-reporter is a raw php script which is supposed to be called on DeniedUrl call when a user request is blocked. Using this page allow this end user to submit a false positive request to the email address configured within the fp-reporter. To avoid spaming the contact, a CATPCHA using Google ReCAPTCHA API is requested to the user. 1) Installation - Copy forbidden.php somewhere in your webroot - Get recaptchalib.php lib from ReCAPTCHA and put it in the same directory (http://code.google.com/p/recaptcha/downloads/list?q=label:phplib-Latest) - Configure variables according to your setup: $public_key, $private_key, MAIL_DST email address 2) Bug report Please submit bug using Naxsi GoogleCode issue tracker. debian/modules/naxsi/contrib/rules_generator/0000755000000000000000000000000012305451341016616 5ustar debian/modules/naxsi/contrib/rules_generator/rules_transformer.py0000755000000000000000000001757312305451332022764 0ustar #!/usr/bin/python import urlparse import urllib import pprint from operator import itemgetter, attrgetter import sys class rulz: def __init__(self, rules, debug): self.keyword = "NAXSI_FMT" self.fatdict = [] self.rules = rules self.log = debug #self.mlog("#RT - Start") pass def mlog(self, fmt): f = open (self.rules, 'a+') if not f: print "enabled to open dst rules file .." return f.write(fmt+"\n") def create_rulestruct(self, tmpdict): currdict={} server="" uri="" ridx = '0' for i in range(len(tmpdict)): if (tmpdict[i][0][-1] >= '0' and tmpdict[i][0][-1] <= '9' and tmpdict[i][0][-1] != ridx): currdict["uri"] = uri currdict["server"] = server if ("var_name" not in currdict): currdict["var_name"] = "" self.fatdict.append(currdict) currdict={} ridx = tmpdict[i][0][-1] if (tmpdict[i][0].startswith("server")): server = tmpdict[i][1] if (tmpdict[i][0].startswith("uri")): uri = tmpdict[i][1] if (tmpdict[i][0].startswith("id")): currdict["id"] = tmpdict[i][1] if (tmpdict[i][0].startswith("zone")): currdict["zone"] = tmpdict[i][1] if (tmpdict[i][0].startswith("var_name")): currdict["var_name"] = tmpdict[i][1] # and do the last one :) currdict["uri"] = uri currdict["server"] = server if ("var_name" not in currdict): currdict["var_name"] = "" self.fatdict.append(currdict) def eat_rules(self, logfile): try: f = open(logfile, 'r') except IOError: return lines = f.readlines() f.close() # if it's "final" log file, clear it. if (logfile is self.rules): f = open(logfile, 'w') f.close() for i in range(len(lines)): ln = lines[i] ln = ln.strip("\n") if ln.find(self.keyword) == -1: continue #if (logfile is not self.rules): self.mlog("#"+ln) # just relying on parse_qsl to be clever tmpdict = urlparse.parse_qsl(ln) self.create_rulestruct(tmpdict) self.mlog( "# total rule number pre-opti : "+ str(len(self.fatdict))+" in file "+logfile) #print "END-OUTPUT" def gen_rulz(self): #print "END OUTPUT" for x in range(len(self.fatdict)): tmprule = "BasicRule wl:"+self.fatdict[x]["id"]+ " \"mz:" if (len(self.fatdict[x]["uri"]) > 0): tmprule += "$URL:"+self.fatdict[x]["uri"]+"|" if (len(self.fatdict[x]["var_name"]) > 0): tmprule += "$"+self.fatdict[x]["zone"]+"_VAR:"+urllib.quote(self.fatdict[x]["var_name"]) else: tmprule += self.fatdict[x]["zone"] tmprule += "\";" # finally, write the rule self.mlog(tmprule) def opti_rulz(self, search_id=-1, search_zone=""): self.fatdict.sort(key=itemgetter('zone')) self.fatdict.sort(key=itemgetter('id')) cid_start = -1 cid_end = -1 last = 0 if (search_id == -1): search_id = int(self.fatdict[0]["id"]) if (search_zone == ""): search_zone = self.fatdict[0]["zone"] #print "[post init] searching id:"+str(search_id)+", zone:"+search_zone # seek the zone were the ID starts for i in range(len(self.fatdict)): tmpid = int(self.fatdict[i]["id"]) tmpzone = self.fatdict[i]["zone"] if (search_id == tmpid): if (search_zone == ""): search_zone = tmpzone break elif (search_zone == tmpzone): break cid_start = i # step 1 : Identify range of rules targetting ID and ZONE given as args. for x in range(cid_start, len(self.fatdict)): tmpid = int(self.fatdict[x]["id"]) tmpzone = self.fatdict[x]["zone"] # good id, but wrong zone, group switch if (search_id == tmpid and search_zone != tmpzone): print "RANGE for ID="+str(search_id)+",ZONE="+search_zone+", range="+str(cid_start)+"-"+str(x) self.agreggate_rulesrange(cid_start, x) #opti self.opti_rulz(tmpid, tmpzone) return if (search_id != tmpid): print "RANGE for ID="+str(search_id)+",ZONE="+search_zone+", range="+str(cid_start)+"-"+str(x) self.agreggate_rulesrange(cid_start, x) #opti self.opti_rulz(tmpid, "") return if (x == len(self.fatdict)-1): print "RANGE for ID="+str(search_id)+",ZONE=NONE, range="+str(cid_start)+"-"+str(x) self.agreggate_rulesrange(cid_start, x+1) def agreggate_rulesrange(self, start, end): same_url = 1 same_zone = 1 same_name = 1 saw_rule_without_url = -1 id = self.fatdict[start]["id"] uri = self.fatdict[start]["uri"] zone = self.fatdict[start]["zone"] var_name = self.fatdict[start]['var_name'] cid = self.fatdict[start]["id"] for z in range(start, end): if (self.fatdict[z]["uri"] is ""): saw_rule_without_url = i if (self.fatdict[z]["uri"] != self.fatdict[start]["uri"]): same_url = 0 if (self.fatdict[z]["zone"] != self.fatdict[start]["zone"]): same_zone = 0 if (self.fatdict[z]["var_name"] != self.fatdict[start]["var_name"]): same_name = 0 # if we have duplicate rules, drop'em if (same_zone and same_name and same_url and saw_rule_without_url == -1): print "# for rule "+str(id)+", we have "+str(end - start)+" elements in zone "+zone print "#duplicate for id "+str(id)+", delete ("+str(end - start)+" elems)" del self.fatdict[start+1:end] # if we have "inclusive" rules, a.k.a one agreggating others if (saw_rule_without_url > -1): print "# for rule "+str(id)+", we have "+str(end - start)+" elements in zone "+zone print "#GLOB for id "+str(id)+", "+str(end - start)+" rules agreggated !" del self.fatdict[start:end] self.fatdict.append({'id' : id, 'server' : server, 'uri': '', 'zone' : zone, 'var_name' : var_name}) # if we can agreggate on zone+arg_name if (same_zone and same_name and not same_url): print "# for rule "+str(id)+", we have "+str(end - start)+" elements in zone "+zone print "#opti for id "+str(id)+", "+str(end - start)+" rules agreggated !" del self.fatdict[start:end] self.fatdict.append({'id' : id, 'uri': '', 'zone' : zone, 'var_name' : var_name}) # pprint.pprint(self.fatdict) dst_rules="/tmp/RT_naxsi.tmp" debug_log="/tmp/nginx_error.log" print "NAXSI rules generator (from nginx's logs)" print "args: [debug_log="+debug_log+"] [rules file="+dst_rules+"]" #print "args:"+str(len(sys.argv)) if (len(sys.argv) > 1): debug_log = sys.argv[1] if (len(sys.argv) > 2): dst_rules = sys.argv[2] r = rulz(dst_rules, debug_log) r.eat_rules(dst_rules) r.eat_rules(debug_log) r.fatdict.sort(key=itemgetter('id')) #pprint.pprint(r.fatdict) # sort'em r.opti_rulz() pprint.pprint(r.fatdict) r.gen_rulz() debian/modules/naxsi/contrib/rules_generator/README.txt0000644000000000000000000000013712305451332020315 0ustar This are OLD naxsi's rule generators. Do not use them, except if you know what you are doing. debian/modules/naxsi/contrib/rules_generator/http_config.py0000644000000000000000000003663212305451332021506 0ustar #!/usr/bin/python # try to provide minimal multi-version support try: from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler except ImportError: from http.server import BaseHTTPRequestHandler, HTTPServer try: import urlparse except ImportError: import urllib.parse as urlparse import sqlite3 import pprint import hashlib import sys import os import argparse import re import cgi class Handler(BaseHTTPRequestHandler): def do_POST(self): self.do_GET() def do_GET(self): message = "" # if it's a background-forwarded request ... if ("naxsi_sig" in self.headers.keys()): if params.v > 2: print ("Exception catched.") print ("ExUrl: "+self.headers["naxsi_sig"]) nx.eat_rule(self.headers["naxsi_sig"]) nx.agreggate_rules() return # user wanna reload its config if (self.path.startswith("/write_and_reload")): if params.v > 2: print ("writting rules, reloading nginx.") if self.path.find("?servmd5=") != -1: print ("writting and reloading specific server."+self.path[self.path.find("?servmd5=")+9:]) nx.dump_rules(self.path[self.path.find("?servmd5=")+9:]) else: print ("writting and reloading all servers.") nx.dump_rules() if params.n is False: os.system(params.cmd) self.send_response(302) self.send_header('Location', '/') self.end_headers() else: print ("Not reloading anything as user is non-root") self.send_response(200) self.end_headers() if sys.version_info > (3, 0): self.wfile.write(bytes("Not root, not reloading anything.", 'utf-8')) else: self.wfile.write("Not root, not reloading anything.") return if params.log is not None: # else, read possible log file, show ui/report self.feed_from_logs(params.log) message = self.ui_report() self.send_response(200) self.end_headers() if sys.version_info > (3, 0): self.wfile.write(bytes(message, 'utf-8')) else: self.wfile.write(message) def feed_from_logs(self, logfile): if nx.log_fd is None: print ("Opening log file for #1 time.") nx.log_fd = open(params.log) else: print ("log file already opened") # we're at the begining of the file ... if nx.log_fd.tell() == 0: print ("at the beginning of file.") while True: tmpbuf = nx.log_fd.readline() if tmpbuf == '': print ("EOF") return if tmpbuf.find("NAXSI_FMT: ") != -1 and tmpbuf.find(", client: ") != -1: sys.stdout.write("[eating rule]") nx.eat_rule(tmpbuf[tmpbuf.find("NAXSI_FMT: ") + 11:tmpbuf.find(", client: ")]) nx.agreggate_rules() def ui_report(self): nbr = nx.get_written_rules_count() nbs = nx.get_exception_count() message = """ 0): message += "nx_ko> " else: message += "nx_ok> " message += "You currently have "+str(nbr) message += " rules generated by naxsi
" message += "You have a total of "+str(nbs)+" exceptions hit.
" if (nbr > 2): message += "You should reload nginx's config.
" message += "Write rules and reload for ALL sites.
" message += nx.display_written_rules() message += "" return (message) class NaxsiDB: def read_text(self): try: fd = open(params.rules, "r") except IOError: print ("Unable to open rules file : "+params.rules) return for rules in fd: rid = re.search('id:([0-9]+)', rules) if rid is None: continue ptr = re.search('str:([^"]+)', rules) if ptr is None: continue self.static[str(rid.group(1))] = cgi.escape(ptr.group(1)) fd.close() def dump_rules(self, server=None): if server is None: fd = open(params.dst, "a+") else: fd = open(params.dst+"."+hashlib.md5(server.encode('utf-8')).hexdigest(), "a+") cur = self.con.cursor() if server is None: cur.execute("SELECT id, uri, zone, var_name, " "server from tmp_rules where written = 0") else: cur.execute("SELECT id, uri, zone, var_name, " "server from tmp_rules where written = 0 and server = ?", [server]) rr = cur.fetchall() print ("Writting "+str(len(rr))+" rules.") for i in range(len(rr)): tmprule = "BasicRule wl:"+str(rr[i][0])+" \"mz:" if len(rr[i][1]) > 0: tmprule += "$URL:"+rr[i][1]+"|" if len(rr[i][3]) > 0: tmprule += "$"+rr[i][2]+"_VAR:"+rr[i][3]+"\" " else: tmprule += rr[i][2]+"\" " tmprule += "; #"+rr[i][4]+"\n" cur.execute("UPDATE tmp_rules SET written=1 WHERE id=? and " "uri=? and zone=? and var_name=? and server=?", [rr[i][0], rr[i][1], rr[i][2], rr[i][3], rr[i][4]]) self.con.commit() fd.write(tmprule) if params.v > 2: print ("Generated Rule : "+tmprule) fd.close() def gen_write(self, mid, uri, zone, var_name, server): cur = self.con.cursor() cur.execute("SELECT count(*) from tmp_rules where id=? and uri=? " "and zone=? and var_name=? and server=?", [mid, uri, zone, var_name, server]) ra = cur.fetchone() if (ra[0] >= 1): if params.v > 2: print ("already present in tmp_rules ...") return cur.execute("INSERT INTO tmp_rules (id, uri, zone, var_name, " "server, written) VALUES (?, ?, ?, ?, ?, 0)", [mid, uri, zone, var_name, server]) self.con.commit() def agreggate_rules(self, mid=0, zone="", var_name=""): cur = self.con.cursor() cur.execute("SELECT id,uri,zone,var_name,server FROM received_sigs" " GROUP BY zone,var_name,id ORDER BY zone,var_name,id") rr = cur.fetchall() for i in range(len(rr)): if len(rr[i][2]) > 0 and len(rr[i][3]) > 0: self.gen_write(rr[i][0], "", rr[i][2], rr[i][3], rr[i][4]) continue if len(rr[i][3]) <= 0: self.gen_write(rr[i][0], rr[i][1], rr[i][2], rr[i][3], rr[i][4]) continue def cursor(self): return self.con.cursor() def get_written_rules_count(self, server=None): cur = self.con.cursor() if server is None: cur.execute("SELECT COUNT(id) FROM tmp_rules where written = 0") else: cur.execute("SELECT COUNT(id) FROM tmp_rules where written = 0 and server = ?", [server]) ra = cur.fetchone() return (ra[0]) def display_written_rules(self): msg = "" cur = self.con.cursor() cur.execute("SELECT distinct(server) " " FROM tmp_rules") rr = cur.fetchall() pprint.pprint(rr) for i in range(len(rr)): print ("adding elems !") if self.get_written_rules_count(rr[i][0]) > 0: tmpstyle="lnk_ko" else: tmpstyle="lnk_ok" msg += """ [write&reload {1}|{2} pending rules| filename:{3}]
""".format(rr[i][0], rr[i][0], str(self.get_written_rules_count(rr[i][0])), params.dst+"."+hashlib.md5(rr[i][0].encode('utf-8')).hexdigest(), tmpstyle) msg += "
" cur.execute("SELECT id,uri,zone,var_name,server" " FROM tmp_rules where written = 0") rr = cur.fetchall() if len(rr): msg += "Authorizing :
" for i in range(len(rr)): pattern = "" if (str(rr[i][0]) in self.static.keys()): pattern = nx.static[str(rr[i][0])] if len(rr[i][2]) > 0 and len(rr[i][3]) > 0: msg += """[{0}] -- pattern '{1}' ({2}) authorized on URL '{3}' for argument '{4}' of zone {5}
""".format(str(rr[i][4]), pattern, str(rr[i][0]), rr[i][1], rr[i][3], rr[i][2]) continue if len(rr[i][3]) <= 0: msg += """[{0}] -- pattern '{1}' ({2}) authorized on url '{3}' for zone {4}
""".format(str(rr[i][4]), pattern, str(rr[i][0]), rr[i][1], rr[i][2]) continue return msg def get_exception_count(self): cur = self.con.cursor() cur.execute("SELECT COUNT(id) FROM received_sigs") ra = cur.fetchone() return (ra[0]) def eat_rule(self, source): currdict = {} server = "" uri = "" ridx = '0' tmpdict = urlparse.parse_qsl(source) for i in range(len(tmpdict)): if (tmpdict[i][0][-1] >= '0' and tmpdict[i][0][-1] <= '9' and tmpdict[i][0][-1] != ridx): currdict["uri"] = uri currdict["server"] = server if ("var_name" not in currdict): currdict["var_name"] = "" currdict["md5"] = hashlib.md5((currdict["uri"]+ currdict["server"]+ currdict["id"]+ currdict["zone"]+ currdict["var_name"]).encode('utf-8')).hexdigest() # print ('#1 here:'+currdict["md5"]) self.fatdict.append(currdict) currdict={} ridx = tmpdict[i][0][-1] if (tmpdict[i][0].startswith("server")): server = tmpdict[i][1] if (tmpdict[i][0].startswith("uri")): uri = tmpdict[i][1] if (tmpdict[i][0].startswith("id")): currdict["id"] = tmpdict[i][1] if (tmpdict[i][0].startswith("zone")): currdict["zone"] = tmpdict[i][1] if (tmpdict[i][0].startswith("var_name")): currdict["var_name"] = tmpdict[i][1] currdict["uri"] = uri currdict["server"] = server if ("var_name" not in currdict): currdict["var_name"] = "" currdict["md5"] = hashlib.md5((currdict["uri"]+currdict["server"]+ currdict["id"]+currdict["zone"]+ currdict["var_name"]).encode('utf-8')).hexdigest() self.fatdict.append(currdict) self.push_to_db(self.fatdict) def push_to_db(self, dd): cur = self.con.cursor() for i in range(len(dd)): cur.execute("""SELECT count(id) FROM received_sigs WHERE md5=?""", [dd[i]["md5"]]) ra = cur.fetchone() if (ra[0] >= 1): print ("Already present in db.") continue if params.v > 2: print ("Pushing to db :") pprint.pprint(dd[i]) cur.execute("INSERT INTO received_sigs (md5, server, id, uri, zone, var_name) VALUES ("+ "?, ?, ?, ?, ?, ?)", [dd[i]["md5"], dd[i]["server"], dd[i]["id"], dd[i]["uri"], dd[i]["zone"], dd[i]["var_name"]]) self.con.commit() def dbcreate(self): if params.v > 2: print ("Creating (new) database.") cur = self.con.cursor() cur.execute("CREATE TABLE received_sigs (md5 text, server text, id int, uri text, zone text, var_name text)") cur.execute("CREATE TABLE tmp_rules (id int, uri text, zone text, var_name text, written int, server text)") self.con.commit() print ("Finished DB creation.") os.system("touch %s" % params.dst) if params.v > 2: print ("Touched TMP rules file.") def dbinit(self): if (self.con is None): self.con = sqlite3.connect(params.db) cur = self.con.cursor() cur.execute("SELECT name FROM sqlite_master WHERE name='received_sigs'") ra = cur.fetchone() if (ra is None): self.dbcreate() if params.v > 2: print ("done.") def __init__(self): self.con = None self.fatdict = [] self.static = {} self.dbinit() self.log_fd = None return class Params(object): pass params = Params() if __name__ == '__main__': parser = argparse.ArgumentParser(description="Naxsi's learning-mode HTTP server.\n"+ "Should be run as root (yes scarry), as it will need to perform /etc/init.d/nginx reload.\n"+ "Runs fine as non-root, but you'll have to manually restart nginx", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--dst', type=str, default='/tmp/naxsi_rules.tmp', help='''Full path to the temp rule file. This file should be included in your naxsi's location configuration file.''') parser.add_argument('--db', type=str, default='naxsi_tmp.db', help='''SQLite database file to use.''') parser.add_argument('--rules', type=str, default='/etc/nginx/naxsi_core.rules', help='''Path to your core rules file.''') parser.add_argument('--cmd', type=str, default='/etc/init.d/nginx reload', help='''Command that will be called to reload nginx's config file''') parser.add_argument('--port', type=int, default=4242, help='''The port the HTTP server will listen to''') parser.add_argument('--log', type=str, default=None, help='''Pickup false positives from log file as well.(ie. /var/log/nginx_error.log)''') parser.add_argument('-n', action='store_true', default=False, help='''Run the daemon as non-root, don't try to reload nginx.''') parser.add_argument('-v', type=int, default=1, help='''Verbosity level 0-3''') args = parser.parse_args(namespace=params) server = HTTPServer(('localhost', params.port), Handler) nx = NaxsiDB() nx.read_text() print ('Starting server, use to stop') try: server.serve_forever() except KeyboardInterrupt: server.shutdown() sys.exit(1) debian/modules/naxsi/contrib/rules_generator/naxsi-ui_v0.1/0000755000000000000000000000000012305451341021117 5ustar debian/modules/naxsi/contrib/rules_generator/naxsi-ui_v0.1/nx_parser.py0000644000000000000000000002112512305451332023473 0ustar import urlparse import pprint import MySQLdb import hashlib #exception + zone : select * from exception LEFT JOIN (peer as srcpeer, peer as dstpeer, connections, match_zone) on (connections.src_peer_id = srcpeer.peer_id and connections.dst_peer_id = dstpeer.peer_id and connections.exception_id = exception.exception_id and match_zone.exception_id = exception.exception_id); #select de gros bourrin : select exc.url, exc.count, exc.md5, cap.http_request, srcpeer.peer_ip, dstpeer.peer_host, mz.zone, mz.arg_name, mz.rule_id from exception as exc, capture as cap, peer as srcpeer, peer as dstpeer, match_zone as mz, connections as conn WHERE (mz.exception_id = exc.exception_id and cap.exception_id = exc.exception_id and srcpeer.peer_id = conn.src_peer_id and conn.dst_peer_id = dstpeer.peer_id); # select all exception with associated peers. ##select * from exception LEFT JOIN (peer as srcpeer, peer as dstpeer, connections) on (connections.src_peer_id = srcpeer.peer_id and connections.dst_peer_id = dstpeer.peer_id and exception.connection_id = connections.connection_id) LIMIT 10; # select all exceptions with associated zone_match and peers. # select * from exception LEFT JOIN (peer as srcpeer, peer as dstpeer, connections, match_zone) on (connections.src_peer_id = srcpeer.peer_id and connections.dst_peer_id = dstpeer.peer_id and exception.connection_id = connections.connection_id and match_zone.exception_id = exception.exception_id) where srcpeer.peer_ip != '88.191.133.106' and srcpeer.peer_ip != '82.234.123.117' and srcpeer.peer_ip != '82.247.12.197'; class signature_parser: def __init__(self, host, user, password, dbname): # print "[+] Connecting to database" self.db = MySQLdb.connect(host, user, password, dbname) if self.db is None: print "ERROR!" return self.cursor = self.db.cursor() if self.cursor is None: print "ERROR!" return # Checking wether the base already exists try: self.cursor.execute("SELECT COUNT(*) FROM exception") except: self.dbcreate() def dbcreate(self): print ("[+] drop'ing and creating new tables") self.cursor.execute("DROP TABLES IF EXISTS rules") self.cursor.execute("CREATE TABLE rules (rule_id integer " "auto_increment primary key " ", action TEXT, msg TEXT, rx TEXT, " "rx_type INT, url TEXT, " "zone TEXT, arg_name TEXT);") self.cursor.execute("DROP TABLES IF EXISTS connections") self.cursor.execute("CREATE TABLE connections (connection_id INTEGER " "auto_increment primary key, " "src_peer_id INT, dst_peer_id INT, exception_id INT, capture_id INT);") self.cursor.execute("DROP TABLES IF EXISTS peer") self.cursor.execute("CREATE TABLE peer (peer_id INTEGER " "auto_increment primary key, " "peer_ip TEXT, peer_host TEXT, peer_tags TEXT);") self.cursor.execute("DROP TABLES IF EXISTS exception") self.cursor.execute("CREATE TABLE exception (exception_id integer " "auto_increment primary key " ",url TEXT, md5 TEXT, count INT default 1);") self.cursor.execute("DROP TABLES IF EXISTS match_zone") self.cursor.execute("CREATE TABLE match_zone (match_id INTEGER " "auto_increment primary key, exception_id INTEGER, " "zone TEXT, arg_name TEXT, rule_id INTEGER);") self.cursor.execute("DROP TABLES IF EXISTS capture") self.cursor.execute("CREATE TABLE capture (capture_id INTEGER " "auto_increment primary key, http_request TEXT, exception_id INTEGER);") # self.cursor.execute("DROP TABLES IF EXISTS router") # self.cursor.execute("CREATE TABLE router(route_id INTEGER " # "auto_increment primary key," # "exception_id INTEGER, rule_id INTEGER, " # "conn_id INTEGER, capture_id INTEGER);") def extract_sig(self, raw_rule, is_from_http=False, is_from_log=False): start = raw_rule.find(": ") if (start != -1): if (is_from_log == True): end = raw_rule[start:].find(", client: ") if (end): return (raw_rule[raw_rule.find(": ") + 2: raw_rule.find(", client: ")]) elif (is_from_http == True): return (raw_rule[raw_rule.find(": ") + 2:]) return ("") def last_id(self): self.cursor.execute("SELECT last_insert_id()") data = self.cursor.fetchone() return data[0] def insert(self, fmt, *args): self.cursor.execute(fmt, [args]) def add_capture(self, exception_id, raw_request): #capture information self.cursor.execute("SELECT COUNT(*) FROM capture where exception_id = %s", (str(exception_id))) x = self.cursor.fetchone() if (x is None or x[0] < 10): # print "less than 10 "+str(x[0]) self.cursor.execute("INSERT INTO capture (http_request, exception_id)" "VALUES (%s, %s)", (str(raw_request), str(exception_id))) capture_id = self.last_id() else: capture_id = 0 return capture_id def sig_to_db(self, raw_request, d, force_insert=False): if (force_insert == False): sig_hash = d["server"][0]+"#"+d["uri"][0]+"#" for i in range(0, 50): if "zone"+str(i) in d: sig_hash = sig_hash + d["zone"+str(i)][0] + "#" else: break if "var_name"+str(i) in d: sig_hash = sig_hash + d["var_name"+str(i)][0] + "#" sig_hash = sig_hash + d["id"+str(i)][0] + "#" sig_md5 = hashlib.md5(sig_hash).hexdigest() self.cursor.execute("SELECT exception_id FROM exception where md5 = %s LIMIT 1", (sig_md5)) exception_id = self.cursor.fetchone() if (exception_id is not None): self.add_capture(exception_id[0], raw_request) self.cursor.execute("UPDATE exception SET count=count+1 where md5 = %s", (sig_md5)) return #peer information sig_hash = d["server"][0]+"#"+d["uri"][0]+"#" self.cursor.execute("INSERT INTO peer (peer_ip) " "VALUES (%s)", (d["ip"][0])) ip_id = self.last_id() self.cursor.execute("INSERT INTO peer (peer_host) " "VALUES (%s)", (d["server"][0])) host_id = self.last_id() #exception self.cursor.execute("INSERT INTO exception (url) VALUES " "(%s)", (d["uri"][0])) exception_id = self.last_id() #capture information capture_id = self.add_capture(exception_id, raw_request) # print "cap id : "+str(capture_id) #connection information self.cursor.execute("INSERT INTO connections (src_peer_id, dst_peer_id, exception_id, capture_id)" "VALUES (%s, %s, %s, %s)", (str(ip_id), str(host_id), str(exception_id), str(capture_id))) connection_id = self.last_id() #match_zones for i in range(0, 50): zn = "" vn = "" if "zone"+str(i) in d: zn = d["zone"+str(i)][0] sig_hash = sig_hash + d["zone"+str(i)][0] + "#" else: break if "var_name"+str(i) in d: vn = d["var_name"+str(i)][0] sig_hash = sig_hash + d["var_name"+str(i)][0] + "#" sig_hash = sig_hash + d["id"+str(i)][0] + "#" self.cursor.execute("INSERT INTO match_zone (exception_id, zone, arg_name, rule_id) " "VALUES (%s, %s, %s, %s)", (str(exception_id), zn, vn, d["id"+str(i)][0])) self.cursor.execute("UPDATE exception SET md5=%s WHERE exception_id=%s", (hashlib.md5(sig_hash).hexdigest(), str(exception_id))) return (connection_id) def raw_parser(self, raw_request, raw_rule, is_from_http=True, is_from_log=False): sig = self.extract_sig(raw_rule, is_from_http, is_from_log) tmpdict = urlparse.parse_qs(sig) connection_id = self.sig_to_db(raw_request, tmpdict, force_insert=False) self.db.close() debian/modules/naxsi/contrib/rules_generator/naxsi-ui_v0.1/nx_intercept.py0000644000000000000000000000375512305451332024205 0ustar #!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet.protocol import Protocol, Factory from twisted.internet import reactor, threads from nx_parser import signature_parser import syslog #import threading ### Protocol Implementation # This is just about the simplest possible protocol class NaxsiHTTPInterceptor(Protocol): def dataReceived(self, data): """ HTTP request from learning mode might be received here :) """ # print str(threading.currentThread().name)+"\n" sig = signature_parser("localhost", "root", "trivialpassword", "naxsi_sig") sig_idx = data.find("\r\nnaxsi_sig: ") if (sig_idx == -1): print "ERROR: request doesn't contain naxis_sig header" print data+"\n---\n" self.finish() return sig_idx = sig_idx + 2 sig_end = data[sig_idx:].find("\r\n") if (sig_end == -1): print "ERROR: request doesn't contain naxsi_sig header" print data+"\n---\n" self.finish() return self.finish() threads.deferToThread(sig.raw_parser, *(data, data[sig_idx:sig_idx+sig_end])) #sig.raw_parser(data[sig_idx:sig_idx+sig_end], is_from_http=True) return def finish(self): self.transport.write("HTTP/1.0 200 OK\r\n" "Server: nx-learn\r\n" "Content-Type: text/html\r\n" "Content-Length: 2\r\n" "Connection: close\r\n\r\n" "ok") # print ">" self.transport.loseConnection() # self.transport.close() def main(): f = Factory() f.protocol = NaxsiHTTPInterceptor reactor.listenTCP(8000, f) # reactor.suggestThreadPoolSize(30) # d = threads.deferToThread(doLongCalculation, 4) # d.addCallback(printResult) reactor.run() if __name__ == '__main__': main() debian/modules/naxsi/naxsi_src/0000755000000000000000000000000012305451341013747 5ustar debian/modules/naxsi/naxsi_src/config0000644000000000000000000000045412305451333015143 0ustar ngx_addon_name=ngx_http_naxsi_module HTTP_MODULES="$HTTP_MODULES ngx_http_naxsi_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/naxsi_runtime.c $ngx_addon_dir/naxsi_config.c $ngx_addon_dir/naxsi_utils.c $ngx_addon_dir/naxsi_skeleton.c " NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/naxsi.h" debian/modules/naxsi/naxsi_src/Makefile0000644000000000000000000000135712305451333015416 0ustar VERS := $(shell grep NAXSI_VERSION naxsi.h | cut -d '"' -f 2) unit_test: export PERL5LIB=/usr/local/share/perl/5.12.4/ ; \ export PATH=$(PATH):/usr/sbin/ ; \ cd .. ; \ prove -r t/*.t package: mkdir ../../naxsi-core-$(VERS) mkdir ../../naxsi-ui-$(VERS) cp -R ../naxsi_src ../../naxsi-core-$(VERS) cp -R ../naxsi_config ../../naxsi-core-$(VERS) cp -R ../contrib/naxsi-ui ../../naxsi-ui-$(VERS) cp -R ../t ../../naxsi-core-$(VERS) cp ../COPYING ../../naxsi-core-$(VERS) cd ../../ ; \ tar --exclude-vcs -cvzf naxsi-core-$(VERS).tgz naxsi-core-$(VERS)/ ; \ tar --exclude-vcs -cvzf naxsi-ui-$(VERS).tgz naxsi-ui-$(VERS)/ ; \ md5sum naxsi-core-$(VERS).tgz > naxsi-core-$(VERS).md5 ; \ md5sum naxsi-ui-$(VERS).tgz > naxsi-ui-$(VERS).md5 ; debian/modules/naxsi/naxsi_src/naxsi_utils.c0000644000000000000000000006015212305451333016462 0ustar /* * NAXSI, a web application firewall for NGINX * Copyright (C) 2011, Thibault 'bui' Koechlin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "naxsi.h" char * strnchr(const char *s, int c, int len) { int cpt; for (cpt = 0; cpt < len && s[cpt]; cpt++) if (s[cpt] == c) return ((char *) s+cpt); return (NULL); } char * strncasechr(const char *s, int c, int len) { int cpt; for (cpt = 0; cpt < len && s[cpt]; cpt++) if (tolower(s[cpt]) == c) return ((char *) s+cpt); return (NULL); } /* ** strstr: faster, stronger, harder ** (because strstr from libc is very slow) */ char * strfaststr(unsigned char *haystack, unsigned int hl, unsigned char *needle, unsigned int nl) { char *cpt, *found, *end; if (hl < nl || !haystack || !needle || !nl || !hl) return (NULL); cpt = (char *) haystack; end = (char *) haystack + hl; while (cpt < end) { found = strncasechr((const char *) cpt, (int) needle[0], hl); if (!found) return (NULL); if (nl == 1) return (found); if (!strncasecmp((const char *)found+1, (const char *) needle+1, nl-1)) return ((char *) found); else { if (found+nl >= end) break; if (found+nl < end) cpt = found+1; } } return (NULL); } /* unescape routine, returns number of nullbytes present */ int naxsi_unescape(ngx_str_t *str) { u_char *dst, *src; u_int nullbytes = 0, i; dst = str->data; src = str->data; naxsi_unescape_uri(&src, &dst, str->len, 0); str->len = src - str->data; //tmp hack fix, avoid %00 & co (null byte) encoding :p for (i = 0; i < str->len; i++) if (str->data[i] == 0x0) { nullbytes++; str->data[i] = '0'; } return (nullbytes); } /* ** Patched ngx_unescape_uri : ** The original one does not care if the character following % is in valid range. ** For example, with the original one : ** '%uff' -> 'uff' */ void naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type) { u_char *d, *s, ch, c, decoded; enum { sw_usual = 0, sw_quoted, sw_quoted_second } state; d = *dst; s = *src; state = 0; decoded = 0; while (size--) { ch = *s++; switch (state) { case sw_usual: if (ch == '?' && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) { *d++ = ch; goto done; } if (ch == '%') { state = sw_quoted; break; } *d++ = ch; break; case sw_quoted: if (ch >= '0' && ch <= '9') { decoded = (u_char) (ch - '0'); state = sw_quoted_second; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { decoded = (u_char) (c - 'a' + 10); state = sw_quoted_second; break; } /* the invalid quoted character */ state = sw_usual; *d++ = '%'; *d++ = ch; break; case sw_quoted_second: state = sw_usual; if (ch >= '0' && ch <= '9') { ch = (u_char) ((decoded << 4) + ch - '0'); if (type & NGX_UNESCAPE_REDIRECT) { if (ch > '%' && ch < 0x7f) { *d++ = ch; break; } *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); break; } *d++ = ch; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (u_char) ((decoded << 4) + c - 'a' + 10); if (type & NGX_UNESCAPE_URI) { if (ch == '?') { *d++ = ch; goto done; } *d++ = ch; break; } if (type & NGX_UNESCAPE_REDIRECT) { if (ch == '?') { *d++ = ch; goto done; } if (ch > '%' && ch < 0x7f) { *d++ = ch; break; } *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); break; } *d++ = ch; break; } /* the invalid quoted character */ *d++ = ch; break; } } done: *dst = d; *src = s; } //#define whitelist_heavy_debug #ifdef whitelist_heavy_debug #define whitelist_light_debug #define whitelist_debug #endif #ifdef whitelist_debug #define whitelist_light_debug #endif /* push rule into disabled rules. */ ngx_int_t ngx_http_wlr_push_disabled(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, ngx_http_rule_t *curr) { ngx_http_rule_t **dr; #ifdef whitelist_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "[naxsi] rule %d disabled", curr->wl_id[0]); #endif if (!dlc->disabled_rules) dlc->disabled_rules = ngx_array_create(cf->pool, 4, sizeof(ngx_http_rule_t *)); if (!dlc->disabled_rules) return (NGX_ERROR); dr = ngx_array_push(dlc->disabled_rules); if (!dr) return (NGX_ERROR); *dr = (ngx_http_rule_t *) curr; return (NGX_OK); } /* merge the two rules into father_wl, meaning ids. Not locations, as we are getting rid of it */ ngx_int_t ngx_http_wlr_merge(ngx_conf_t *cf, ngx_http_whitelist_rule_t *father_wl, ngx_http_rule_t *curr) { uint i; ngx_int_t *tmp_ptr; if (!father_wl->ids) { father_wl->ids = ngx_array_create(cf->pool, 3, sizeof(int)); if (!father_wl->ids) return (NGX_ERROR); } for (i = 0; curr->wl_id[i] >= 0 ; i++) { tmp_ptr = ngx_array_push(father_wl->ids); if (!tmp_ptr) return (NGX_ERROR); *tmp_ptr = curr->wl_id[i]; } return (NGX_OK); } /*check rule, returns associed zone, as well as location index. location index refers to $URL:bla or $ARGS_VAR:bla */ #define custloc_array(x) ((ngx_http_custom_rule_location_t *) x) //#define whitelist_heavy_debug ngx_int_t ngx_http_wlr_identify(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, ngx_http_rule_t *curr, int *zone, int *uri_idx, int *name_idx) { uint i; /* identify global match zones (|ARGS|BODY|HEADERS|URL|FILE_EXT) */ if (curr->br->body || curr->br->body_var) *zone = BODY; else if (curr->br->headers || curr->br->headers_var) *zone = HEADERS; else if (curr->br->args || curr->br->args_var) *zone = ARGS; else if (curr->br->url) /*don't assume that named $URL means zone is URL.*/ *zone = URL; else if (curr->br->file_ext) *zone = FILE_EXT; /* if we're facing a WL in the style $URL:/bla|ARGS (or any other zone), push it to */ for (i = 0; i < curr->br->custom_locations->nelts; i++) { /* locate target URL if exists ($URL:/bla|ARGS) or ($URL:/bla|$ARGS_VAR:foo) */ if (custloc_array(curr->br->custom_locations->elts)[i].specific_url) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has URI %V", &(custloc_array(curr->br->custom_locations->elts)[i].target)); #endif *uri_idx = i; } /* identify named match zones ($ARGS_VAR:bla|$HEADERS_VAR:bla|$BODY_VAR:bla) */ if (custloc_array(curr->br->custom_locations->elts)[i].body_var) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has body_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target)); #endif *name_idx = i; *zone = BODY; } if (custloc_array(curr->br->custom_locations->elts)[i].headers_var) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has header_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target)); #endif *name_idx = i; *zone = HEADERS; } if (custloc_array(curr->br->custom_locations->elts)[i].args_var) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has arg_var %V", &(custloc_array(curr->br->custom_locations->elts)[i].target)); #endif *name_idx = i; *zone = ARGS; } } if (*zone == -1) return (NGX_ERROR); return (NGX_OK); } ngx_http_whitelist_rule_t * ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, ngx_http_rule_t *curr, int zone, int uri_idx, int name_idx, char **fullname) { uint i; /* Create unique string for rule, and try to find it in existing rules.*/ /*name AND uri*/ if (uri_idx != -1 && name_idx != -1) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has uri + name"); #endif /* allocate one extra byte in case curr->br->target_name is set. */ *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len + custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 3); /* if WL targets variable name instead of content, prefix hash with '#' */ if (curr->br->target_name) strncat(*fullname, (const char *) "#", 1); strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len); strncat(*fullname, (const char *) "#", 1); strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len); } /* only uri */ else if (uri_idx != -1 && name_idx == -1) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has uri"); #endif //XXX set flag only_uri *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len + 1); strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[uri_idx].target.data, custloc_array(curr->br->custom_locations->elts)[uri_idx].target.len); } /* only name */ else if (name_idx != -1) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has name"); #endif *fullname = ngx_pcalloc(cf->pool, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len + 2); if (curr->br->target_name) strncat(*fullname, (const char *) "#", 1); strncat(*fullname, (const char *) custloc_array(curr->br->custom_locations->elts)[name_idx].target.data, custloc_array(curr->br->custom_locations->elts)[name_idx].target.len); } /* problem houston */ else return (NULL); for (i = 0; i < dlc->tmp_wlr->nelts; i++) if (!strcmp((const char *)*fullname, (const char *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data) && ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone == (uint) zone) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "found existing 'same' WL : %V", ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name); #endif return (&((ngx_http_whitelist_rule_t *)dlc->tmp_wlr->elts)[i]); } return (NULL); } #define httprule_array(x) ((ngx_http_rule_t *) x) ngx_int_t ngx_http_wlr_finalize_hashtables(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc) { int get_sz = 0, headers_sz = 0, body_sz = 0, uri_sz = 0; ngx_array_t *get_ar = NULL, *headers_ar = NULL, *body_ar = NULL, *uri_ar = NULL; ngx_hash_key_t *arr_node; ngx_hash_init_t hash_init; uint i; #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "finalizing hashtables"); #endif for (i = 0; i < dlc->tmp_wlr->nelts; i++) { switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) { case FILE_EXT: case BODY: body_sz++; break; case HEADERS: headers_sz++; break; case URL: uri_sz++; break; case ARGS: get_sz++; break; case UNKNOWN: default: return (NGX_ERROR); } } #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "nb items : body:%d headers:%d uri:%d get:%d", body_sz, headers_sz, uri_sz, get_sz); #endif if (get_sz) get_ar = ngx_array_create(cf->pool, get_sz, sizeof(ngx_hash_key_t)); if (headers_sz) headers_ar = ngx_array_create(cf->pool, headers_sz, sizeof(ngx_hash_key_t)); if (body_sz) body_ar = ngx_array_create(cf->pool, body_sz, sizeof(ngx_hash_key_t)); if (uri_sz) uri_ar = ngx_array_create(cf->pool, uri_sz, sizeof(ngx_hash_key_t)); for (i = 0; i < dlc->tmp_wlr->nelts; i++) { switch (((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone) { case FILE_EXT: case BODY: arr_node = (ngx_hash_key_t*) ngx_array_push(body_ar); break; case HEADERS: arr_node = (ngx_hash_key_t*) ngx_array_push(headers_ar); break; case URL: arr_node = (ngx_hash_key_t*) ngx_array_push(uri_ar); break; case ARGS: arr_node = (ngx_hash_key_t*) ngx_array_push(get_ar); break; default: return (NGX_ERROR); } ngx_memset(arr_node, 0, sizeof(ngx_hash_key_t)); arr_node->key = *(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name); arr_node->key_hash = ngx_hash_key_lc(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->data, ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name->len); arr_node->value = (void *) &(((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i]); #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing new WL, zone:%d, target:%V, %d IDs", ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].zone , ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].name, ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts); unsigned int z; for (z = 0; z < ((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->nelts; z++) ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "id:%d", ((int *)((ngx_http_whitelist_rule_t *) dlc->tmp_wlr->elts)[i].ids->elts)[z]); #endif } hash_init.key = &ngx_hash_key_lc; hash_init.pool = cf->pool; hash_init.temp_pool = NULL; hash_init.max_size = 1024; hash_init.bucket_size = 512; if (body_ar) { dlc->wlr_body_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t)); hash_init.hash = dlc->wlr_body_hash; hash_init.name = "wlr_body_hash"; if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) body_ar->elts, body_ar->nelts) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init failed"); return (NGX_ERROR); } #ifdef whitelist_debug else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$BODY hashtable init successed !"); #endif } if (uri_ar) { dlc->wlr_url_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t)); hash_init.hash = dlc->wlr_url_hash; hash_init.name = "wlr_url_hash"; if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) uri_ar->elts, uri_ar->nelts) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init failed"); return (NGX_ERROR); } #ifdef whitelist_debug else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$URL hashtable init successed !"); #endif } if (get_ar) { dlc->wlr_args_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t)); hash_init.hash = dlc->wlr_args_hash; hash_init.name = "wlr_args_hash"; if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) get_ar->elts, get_ar->nelts) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init failed"); return (NGX_ERROR); } #ifdef whitelist_debug else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$ARGS hashtable init successed %d !", dlc->wlr_args_hash->size); #endif } if (headers_ar) { dlc->wlr_headers_hash = (ngx_hash_t*) ngx_pcalloc(cf->pool, sizeof(ngx_hash_t)); hash_init.hash = dlc->wlr_headers_hash; hash_init.name = "wlr_headers_hash"; if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) headers_ar->elts, headers_ar->nelts) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init failed"); return (NGX_ERROR); } #ifdef whitelist_debug else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "$HEADERS hashtable init successed %d !", dlc->wlr_headers_hash->size); #endif } return (NGX_OK); } /* ** This function will take the whitelist basicrules generated during the configuration ** parsing phase, and aggregate them to build hashtables according to the matchzones. ** ** As whitelist can be in the form : ** "mz:$URL:bla|$ARGS_VAR:foo" ** "mz:$URL:bla|ARGS" ** "mz:$HEADERS_VAR:Cookie" ** ... ** ** So, we will aggregate all the rules that are pointing to the same URL together, ** as well as rules targetting the same argument name / zone. */ //#define whitelist_heavy_debug ngx_int_t ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc, ngx_conf_t *cf) { int zone, uri_idx, name_idx, ret; ngx_http_rule_t *curr_r/*, *father_r*/; ngx_http_whitelist_rule_t *father_wlr; char *fullname; uint i; if (!dlc->whitelist_rules || dlc->whitelist_rules->nelts < 1) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "No whitelist registred, but it's your call."); #endif return (NGX_OK); } #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Building whitelist hashtables, %d items in list", dlc->whitelist_rules->nelts); #endif dlc->tmp_wlr = ngx_array_create(cf->pool, dlc->whitelist_rules->nelts, sizeof(ngx_http_whitelist_rule_t)); /* iterate through each stored whitelist rule. */ for (i = 0; i < dlc->whitelist_rules->nelts; i++) { uri_idx = name_idx = zone = -1; /*a whitelist is in fact just another basic_rule_t */ curr_r = &(httprule_array(dlc->whitelist_rules->elts)[i]); #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Processing wl %d/%p", i, curr_r); #endif /*no custom location at all means that the rule is disabled */ if (!curr_r->br->custom_locations) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "WL %d is a disable rule.", i); #endif if (ngx_http_wlr_push_disabled(cf, dlc, curr_r) == NGX_ERROR) return (NGX_ERROR); continue; } ret = ngx_http_wlr_identify(cf, dlc, curr_r, &zone, &uri_idx, &name_idx); if (ret != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Following whitelist doesn't target any zone or is incorrect :"); if (name_idx != -1) ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target name : %V", &(custloc_array(curr_r->br->custom_locations->elts)[name_idx].target)); else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist has no target name."); if (uri_idx != -1) ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelist target uri : %V", &(custloc_array(curr_r->br->custom_locations->elts)[uri_idx].target)); else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "whitelists has no target uri."); return (NGX_ERROR); } /* ngx_http_whitelist_rule_t * ngx_http_wlr_find(ngx_conf_t *cf, ngx_http_dummy_loc_conf_t *dlc, ngx_http_rule_t *curr, int zone, int uri_idx, int name_idx, char **fullname) { */ father_wlr = ngx_http_wlr_find(cf, dlc, curr_r, zone, uri_idx, name_idx, (char **) &fullname); if (!father_wlr) { #ifdef whitelist_heavy_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "creating fresh WL [%s].", fullname); #endif /* creates a new whitelist rule in the right place. setup name and zone, create a new (empty) whitelist_location, as well as a new (empty) id aray. */ father_wlr = ngx_array_push(dlc->tmp_wlr); if (!father_wlr) return (NGX_ERROR); memset(father_wlr, 0, sizeof(ngx_http_whitelist_rule_t)); father_wlr->name = ngx_pcalloc(cf->pool, sizeof(ngx_str_t)); if (!father_wlr->name) return (NGX_ERROR); father_wlr->name->len = strlen((const char *) fullname); father_wlr->name->data = (unsigned char *) fullname; father_wlr->zone = zone; /* If there is URI and no name idx, specify it, so that WL system won't get fooled by an argname like an URL */ if (uri_idx != -1 && name_idx == -1) father_wlr->uri_only = 1; /* If target_name is present in son, report it. */ if (curr_r->br && curr_r->br->target_name) father_wlr->target_name = curr_r->br->target_name; } /*merges the two whitelist rules together, including custom_locations. */ if (ngx_http_wlr_merge(cf, father_wlr, curr_r) != NGX_OK) return (NGX_ERROR); } /* and finally, build the hashtables for various zones. */ if (ngx_http_wlr_finalize_hashtables(cf, dlc) != NGX_OK) return (NGX_ERROR); /* TODO : Free old whitelist_rules (dlc->whitelist_rules)*/ return (NGX_OK); } /* function used for intensive log if dynamic flag is set. Output format : ip=&server=&uri=&id=&zone=&content= */ static char *dummy_match_zones[] = { "HEADERS", "URL", "ARGS", "BODY", "FILE_EXT", "UNKNOWN", NULL }; void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req, ngx_http_rule_t *rule, enum DUMMY_MATCH_ZONE zone) { ngx_str_t tmp_uri, tmp_val, tmp_name; ngx_str_t empty=ngx_string(""); //encode uri tmp_uri.len = req->uri.len + (2 * ngx_escape_uri(NULL, req->uri.data, req->uri.len, NGX_ESCAPE_ARGS)); tmp_uri.data = ngx_pcalloc(req->pool, tmp_uri.len+1); if (tmp_uri.data == NULL) return ; ngx_escape_uri(tmp_uri.data, req->uri.data, req->uri.len, NGX_ESCAPE_ARGS); //encode val if (val->len <= 0) tmp_val = empty; else { tmp_val.len = val->len + (2 * ngx_escape_uri(NULL, val->data, val->len, NGX_ESCAPE_ARGS)); tmp_val.data = ngx_pcalloc(req->pool, tmp_val.len+1); if (tmp_val.data == NULL) return ; ngx_escape_uri(tmp_val.data, val->data, val->len, NGX_ESCAPE_ARGS); } //encode name if (name->len <= 0) tmp_name = empty; else { tmp_name.len = name->len + (2 * ngx_escape_uri(NULL, name->data, name->len, NGX_ESCAPE_ARGS)); tmp_name.data = ngx_pcalloc(req->pool, tmp_name.len+1); if (tmp_name.data == NULL) return ; ngx_escape_uri(tmp_name.data, name->data, name->len, NGX_ESCAPE_ARGS); } ngx_log_debug(NGX_LOG_ERR, req->connection->log, 0, "NAXSI_EXLOG: ip=%V&server=%V&uri=%V&id=%d&zone=%s&var_name=%V&content=%V", &(req->connection->addr_text), &(req->headers_in.server), &(tmp_uri), rule->rule_id, dummy_match_zones[zone], &(tmp_name), &(tmp_val)); if (tmp_val.len > 0) ngx_pfree(req->pool, tmp_val.data); if (tmp_name.len > 0) ngx_pfree(req->pool, tmp_name.data); if (tmp_uri.len > 0) ngx_pfree(req->pool, tmp_uri.data); } debian/modules/naxsi/naxsi_src/naxsi_runtime.c0000644000000000000000000016767412305451333017026 0ustar /* * NAXSI, a web application firewall for NGINX * Copyright (C) 2011, Thibault 'bui' Koechlin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "naxsi.h" /* used to store locations during the configuration time. then, accessed by the hashtable building feature during "init" time. */ ngx_http_dummy_loc_conf_t *dummy_lc; /* ** Static defined rules struct for internal rules. ** We use those to be able to call is_rule_whitelisted_n() on those ** rules at any time ;) */ //nx_int__post_without_data //nx_int__no_headers //nx_int__weird_url //nx_int__weird_body //nx_int__weird_args //nx_int__post_without_content_type ngx_http_rule_t nx_int__weird_request = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 1, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 0, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__uncommon_hex_encoding = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 10, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 1, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__uncommon_content_type = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 11, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 1, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__uncommon_url = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 12, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 1, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__uncommon_post_format = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 13, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 1, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__uncommon_post_boundary = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 13, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 1, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; ngx_http_rule_t nx_int__big_request = {/*type*/ 0, /*whitelist flag*/ 0, /*wl_id ptr*/ NULL, /*rule_id*/ 2, /*log_msg*/ NULL, /*score*/ 0, /*sscores*/ NULL, /*sc_block*/ 0, /*sc_allow*/ 0, /*block*/ 1, /*allow*/ 0, /*log*/ 0, /*lnk_to & from*/ 0, 0, /*br ptrs*/ NULL}; #define dummy_error_fatal(ctx, r, ...) do { \ if (ctx) ctx->block = 1; \ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \ "XX-******** NGINX NAXSI INTERNAL ERROR ********"); \ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, __VA_ARGS__); \ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \ "XX-func:%s file:%s line:%d", \ __func__, __FILE__, __LINE__); \ if (r && r->uri.data) ngx_log_debug(NGX_LOG_DEBUG_HTTP, \ r->connection->log, 0, \ "XX-uri:%s", r->uri.data); \ } while (0) void ngx_http_dummy_update_current_ctx_status(ngx_http_request_ctx_t *ctx, ngx_http_dummy_loc_conf_t *cf, ngx_http_request_t *r); int ngx_http_process_basic_rule_buffer(ngx_str_t *str, ngx_http_rule_t *rl, ngx_int_t *match); void ngx_http_dummy_payload_handler(ngx_http_request_t *r); int ngx_http_basestr_ruleset_n(ngx_pool_t *pool, ngx_str_t *name, ngx_str_t *value, ngx_array_t *rules, ngx_http_request_t *req, ngx_http_request_ctx_t *ctx, enum DUMMY_MATCH_ZONE zone); void ngx_http_dummy_body_parse(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r, ngx_http_dummy_loc_conf_t *cf, ngx_http_dummy_main_conf_t *main_cf); void naxsi_log_offending(ngx_str_t *name, ngx_str_t *val, ngx_http_request_t *req, ngx_http_rule_t *rule, enum DUMMY_MATCH_ZONE zone); /* ** in : string to inspect, associated rule ** does : apply the rule on the string, return 1 if matched, ** 0 else and -1 on error */ int ngx_http_process_basic_rule_buffer(ngx_str_t *str, ngx_http_rule_t *rl, ngx_int_t *nb_match) { ngx_int_t match, tmp_idx, len, i; unsigned char *ret; int captures[6]; if (!rl->br || !nb_match) return (-1); *nb_match = 0; if (rl->br->rx) { tmp_idx = 0; len = str->len; while #if defined nginx_version && (nginx_version >= 1002002 && nginx_version != 1003000) (tmp_idx < len && (match = pcre_exec(rl->br->rx->regex->code, 0, (const char *) str->data, str->len, tmp_idx, 0, captures, 6)) >= 0) #elif defined nginx_version && (nginx_version > 1001011) (tmp_idx < len && (match = pcre_exec(rl->br->rx->regex->pcre, 0, (const char *) str->data, str->len, tmp_idx, 0, captures, 6)) >= 0) #elif defined nginx_version && (nginx_version <= 1001011) (tmp_idx < len && (match = pcre_exec(rl->br->rx->regex, 0, (const char *) str->data, str->len, tmp_idx, 0, captures, 6)) >= 0) #elif defined nginx_version #error "Inconsistent nginx version." (0) #else #error "nginx_version not defined." (0) #endif { for(i = 0; i < match; ++i) *nb_match += 1; tmp_idx = captures[1]; } if (*nb_match > 0) { if (rl->br->negative) return (0); else return (1); } else if (*nb_match == 0) { if (rl->br->negative) return (1); else return (0); } return (-1); } else if (rl->br->str) { match = 0; tmp_idx = 0; while (1) { ret = (unsigned char *) strfaststr((unsigned char *)str->data+tmp_idx, (unsigned int)str->len - tmp_idx, (unsigned char *)rl->br->str->data, (unsigned int)rl->br->str->len); if (ret) { match = 1; *nb_match = *nb_match+1; } else break; if (nb_match && ret < (str->data + str->len)) { tmp_idx = (ret - str->data) + 1; if (tmp_idx > (int) (str->len - 1)) break; } else break; } if (match) { if (rl->br->negative) return (0); else return (1); } else { if (rl->br->negative) return (1); else return (0); } } return (0); } /* ** Check if a (matched) rule is whitelisted. ** This func will look for the current URI in the wlr_url_hash [hashtable] ** It will also look for varname in the wlr_body|args|headers_hash [hashtable] ** and It will also look for disabled rules. ** 1 - If the rule is disabled, it's whitelisted ** 2 - If a matching URL is found, check if the further information confirms that the rule should be whitelisted ** ($URL:/bar|$ARGS_VAR:foo : it's not because URL matches that we should whitelist rule) ** 3 - If a matching varname is found, check zone and rules IDs. ** [TODO] : Add mz matches with style BODY|HEADERS|... ** returns (1) if rule is whitelisted, else (0) */ //#define whitelist_debug //#define whitelist_heavy_debug int ngx_http_dummy_is_whitelist_adapted(ngx_http_whitelist_rule_t *b, ngx_str_t *name, enum DUMMY_MATCH_ZONE zone, ngx_http_rule_t *r, ngx_http_request_t *req, enum MATCH_TYPE type, ngx_int_t target_name) { unsigned int i; /* if something was found, check the rule ID */ if (!b) return (0); /* FILE_EXT zone is just a hack, as it indeed targets BODY */ if (zone == FILE_EXT) zone = BODY; #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Possible whitelist ... check..."); #endif /* if whitelist targets arg name, but the rules hit content*/ if (b->target_name && !target_name) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "whitelist targets name, but rule matched content."); #endif return (0); } /* if if the whitelist target contents, but the rule hit arg name*/ if (!b->target_name && target_name) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "whitelist targets content, but rule matched name."); #endif return (0); } if (type == NAME_ONLY) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Name match in zone %s", zone == ARGS ? "ARGS" : zone == BODY ? "BODY" : zone == HEADERS ? "HEADERS" : "UNKNOWN!!!!!"); #endif //False Positive, there was a whitelist that matches the argument name, // But is was actually matching an existing URI name. if (zone != b->zone || b->uri_only) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "bad whitelist, name match, but WL was only on URL."); #endif return (0); } for (i = 0; i < b->ids->nelts; i++) { if ( ((int *)b->ids->elts)[i] == r->rule_id || ((int *)b->ids->elts)[i] == 0) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "WhiteListing0 rule %d on var [%V] at uri [%V] (dst id:%d)", r->rule_id, name, &(req->uri), ((int *)b->ids->elts)[i]); #endif return (1); } } return (0); } if (type == URI_ONLY || type == MIXED) { /* zone must match */ if (zone != b->zone || /* if the whitelist matched on an URI, check that the 'name' field in the whitelist is really an URI and not an argument name. */ (type == URI_ONLY && !b->uri_only)) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "bad whitelist, URL match, but WL was not on URL."); #endif return (0); } for (i = 0; i < b->ids->nelts; i++) { #ifdef whitelist_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "wl : %d, matched rule : %d", ((int *)b->ids->elts)[i], r->rule_id); #endif if ( ((int *)b->ids->elts)[i] == r->rule_id || ((int *)b->ids->elts)[i] == 0) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "WhiteListing1 rule %d/ wl[%d] = %d (wl had %d wl ids) on var [%V] at uri [%V] (zone:%s)", r->rule_id, i, ((int *)b->ids->elts)[i], b->ids->nelts, name, &(req->uri), zone == HEADERS ? "HEADERS" : zone == URL ? "URL" : zone == BODY ? "BODY" : zone == ARGS ? "ARGS" : "UNKNOWN!!!!"); #endif return (1); } } return (0); } return (0); } //#define whitelist_debug ngx_http_whitelist_rule_t * nx_find_wl_in_hash(ngx_str_t *mstr, ngx_http_dummy_loc_conf_t *cf, enum DUMMY_MATCH_ZONE zone) { ngx_int_t k; ngx_http_whitelist_rule_t *b = NULL; k = ngx_hash_key_lc(mstr->data, mstr->len); if ((zone == BODY || zone == FILE_EXT) && cf->wlr_body_hash && cf->wlr_body_hash->size > 0) b = (ngx_http_whitelist_rule_t*) ngx_hash_find(cf->wlr_body_hash, k, (u_char*) mstr->data, mstr->len); else if (zone == HEADERS && cf->wlr_headers_hash && cf->wlr_headers_hash->size > 0) b = (ngx_http_whitelist_rule_t*) ngx_hash_find(cf->wlr_headers_hash, k, (u_char*) mstr->data, mstr->len); else if (zone == URL && cf->wlr_url_hash && cf->wlr_url_hash->size > 0) b = (ngx_http_whitelist_rule_t*) ngx_hash_find(cf->wlr_url_hash, k, (u_char*) mstr->data, mstr->len); else if (zone == ARGS && cf->wlr_args_hash && cf->wlr_args_hash->size > 0) b = (ngx_http_whitelist_rule_t*) ngx_hash_find(cf->wlr_args_hash, k, (u_char*) mstr->data, mstr->len); return (b); } int ngx_http_dummy_is_rule_whitelisted_n(ngx_http_request_t *req, ngx_http_dummy_loc_conf_t *cf, ngx_http_rule_t *r, ngx_str_t *name, enum DUMMY_MATCH_ZONE zone, ngx_int_t target_name) { ngx_int_t k; ngx_http_whitelist_rule_t *b = NULL; unsigned int i, z; ngx_http_rule_t **dr; ngx_str_t tmp_hashname; ngx_str_t nullname = ngx_null_string; /* if name is NULL, replace it by an empty string */ if (!name) name = &nullname; #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "is rule [%d] whitelisted in zone %s for item %V", r->rule_id, zone == ARGS ? "ARGS" : zone == HEADERS ? "HEADERS" : zone == BODY ? "BODY" : zone == URL ? "URL" : zone == FILE_EXT ? "FILE_EXT" : "UNKNOWN", name); #endif tmp_hashname.data = NULL; /* Check if the rule is part of disabled rules for this location */ if (cf->disabled_rules) { dr = cf->disabled_rules->elts; for (i = 0; i < cf->disabled_rules->nelts; i++) { for (z = 0; dr[i]->wl_id[z] >= 0; z++) { /* if it's the same ID or that the WL id is 0 (which means ALL RULES), it's whitelisted ! */ /* TODO : test case for WL on rule_id 0 */ if (dr[i]->wl_id[z] == r->rule_id || dr[i]->wl_id[z] == 0) { /* matched in args zone and whitelisted in full args zone */ if (zone == ARGS && dr[i]->br && dr[i]->br->args) { if (dr[i]->br->target_name && target_name) return (1); if (!dr[i]->br->target_name && !target_name) return (1); } /* matched in headers zone and whitelisted in full headers zone */ else if (zone == HEADERS && dr[i]->br && dr[i]->br->headers) { if (dr[i]->br->target_name && target_name) return (1); if (!dr[i]->br->target_name && !target_name) return (1); } else if (zone == BODY && dr[i]->br && dr[i]->br->body) { if (dr[i]->br->target_name && target_name) return (1); if (!dr[i]->br->target_name && !target_name) return (1); } else if (zone == FILE_EXT && dr[i]->br && dr[i]->br->file_ext) { if (dr[i]->br->target_name && target_name) return (1); if (!dr[i]->br->target_name && !target_name) return (1); } else if (zone == URL && dr[i]->br && dr[i]->br->url) return (1); /* this one, with no match zone at all, means the rule is purely disabled */ else if (dr[i]->br && !(dr[i]->br->args || dr[i]->br->headers || dr[i]->br->body || dr[i]->br->url)) return (1); } } } } #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "hashing varname [%V]", name); #endif /* ** First, check for whitelists targetting ARG name, ** and check as well ARGS_VAR:x|NAME whitelists. */ if (name->len > 0) { /* lower case the var name before checking it against hash tables */ for (i = 0; i < name->len; i++) name->data[i] = tolower(name->data[i]); b = nx_find_wl_in_hash(name, cf, zone); if (!b) { /*prefix hash with '#', to find whitelists that would be done only on ARGS_VAR:X|NAME */ tmp_hashname.len = name->len+1; tmp_hashname.data = ngx_pcalloc(req->pool, tmp_hashname.len+1); tmp_hashname.data[0] = '#'; memcpy(tmp_hashname.data+1, name->data, name->len); b = nx_find_wl_in_hash(&tmp_hashname, cf, zone); } } if (b) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "potential match on name [%V]", name); #endif if (ngx_http_dummy_is_whitelist_adapted(b, name, zone, r, req, NAME_ONLY, target_name)) return (1); } /*XXXX- URI only whitelists */ #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "hashing (uri) [%V]", &(req->uri)); #endif /* check the URL no matter what zone we're in */ if (cf->wlr_url_hash && cf->wlr_url_hash->size > 0) { k = ngx_hash_key_lc(req->uri.data, req->uri.len); /* check if the rule was not whitelisted */ #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Check if rule [%d] is whitelist on arg [%V] from uri [%V]", r->rule_id, name, &(req->uri)); #endif b = (ngx_http_whitelist_rule_t*) ngx_hash_find(cf->wlr_url_hash, k, (u_char*) req->uri.data, req->uri.len); if (b) if (ngx_http_dummy_is_whitelist_adapted(b, name, zone, r, req, URI_ONLY, target_name)) return (1); } b = nx_find_wl_in_hash(&(req->uri), cf, zone); if (b) if (ngx_http_dummy_is_whitelist_adapted(b, name, zone, r, req, URI_ONLY, target_name)) return (1); /*XXXXX- maybe it was $URL+$VAR ? */ if (!b) { tmp_hashname.len = req->uri.len + 1 + name->len; /* one extra byte for target_name '#' */ tmp_hashname.data = ngx_pcalloc(req->pool, tmp_hashname.len+2); if (!tmp_hashname.data) return (NGX_ERROR); if (target_name) { tmp_hashname.len++; ngx_memset(tmp_hashname.data, 0, tmp_hashname.len+1); strncat((char*)tmp_hashname.data, "#", 1); } else ngx_memset(tmp_hashname.data, 0, tmp_hashname.len+1); strncat((char*) tmp_hashname.data, (char*)req->uri.data, req->uri.len); strncat((char*)tmp_hashname.data, "#", 1); strncat((char*)tmp_hashname.data, (char*)name->data, name->len); #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "hashing [%V]", &tmp_hashname); #endif b = nx_find_wl_in_hash(&(tmp_hashname), cf, zone); } if (b) if (ngx_http_dummy_is_whitelist_adapted(b, name, zone, r, req, MIXED, target_name)) { if (tmp_hashname.data) ngx_pfree(req->pool, tmp_hashname.data); return (1); } if (tmp_hashname.data) ngx_pfree(req->pool, tmp_hashname.data); return (0); } //#define output_forbidden ngx_int_t ngx_http_output_forbidden_page(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r) { ngx_int_t rc, w; u_int i; char *fmt; const char *fmt_base = "ip=%.*s&server=%.*s&uri=%.*s&learning=%d&total_processed=%zu&total_blocked=%zu"; const char *fmt_rm = "&zone%d=%s&id%d=%d&var_name%d=%.*s"; ngx_str_t denied_args, tmp_uri; ngx_http_dummy_loc_conf_t *cf; ngx_http_matched_rule_t *mr; /* create output message */ cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module); #ifdef output_forbidden ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "#Forbidding page"); #endif tmp_uri.len = r->uri.len + (2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_ARGS)); tmp_uri.data = ngx_pcalloc(r->pool, tmp_uri.len+1); ngx_escape_uri(tmp_uri.data, r->uri.data, r->uri.len, NGX_ESCAPE_ARGS); rc = snprintf(0, 0, fmt_base, r->connection->addr_text.len, r->connection->addr_text.data, r->headers_in.server.len, r->headers_in.server.data, tmp_uri.len, tmp_uri.data, ctx->learning ? 1 : 0, cf->request_processed, cf->request_blocked); if (ctx->matched) { mr = ctx->matched->elts; for (i = 0; i < ctx->matched->nelts; i++) rc += snprintf(0, 0, fmt_rm, i, "-----BODY|ARGS|HEADERS|URL----", i, mr[i].rule->rule_id, i, mr[i].name->len, mr[i].name->data); } else { if (ctx->weird_request || ctx->big_request) rc += snprintf(0, 0, fmt_rm, 99, "-----BODY|ARGS|HEADERS|URL----", 99, 99, 99, 99, "REQUEST_LONG_LONG"); } fmt = ngx_pcalloc(r->pool, rc+2); if (!fmt) return (NGX_ERROR); w = snprintf(fmt, rc, fmt_base, r->connection->addr_text.len, r->connection->addr_text.data, r->headers_in.server.len, r->headers_in.server.data, tmp_uri.len, tmp_uri.data, ctx->learning ? 1 : 0, cf->request_processed, cf->request_blocked); char tmp_zone[30]; /*<- should be a dynamic allocation, no bof here, just mem waste , but i'm lazy :) */ if (ctx->matched) { mr = ctx->matched->elts; for (i = 0; i < ctx->matched->nelts; i++) { memset(tmp_zone, 0, 30); #ifdef output_forbidden ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "----"); ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "zones:H:%d/U:%d/A:%d/B:%d", mr[i].headers_var , mr[i].url, mr[i].args_var , mr[i].body_var ); #endif //zone = UNKNOWN; if (mr[i].body_var) strcat(tmp_zone, "BODY"); if (mr[i].args_var) strcat(tmp_zone, "ARGS"); if (mr[i].headers_var) strcat(tmp_zone, "HEADERS"); if (mr[i].url) strcat(tmp_zone, "URL"); if (mr[i].file_ext) strcat(tmp_zone, "FILE_EXT"); if (mr[i].target_name) strcat(tmp_zone, "|NAME"); w += snprintf(fmt+w, rc, fmt_rm, i, tmp_zone, i, mr[i].rule->rule_id, i, mr[i].name->len, mr[i].name->data); #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "FMT (sub) (%d) LEN:%s", i, fmt); #endif } } else { if (ctx->weird_request) w += snprintf(fmt+w, rc, fmt_rm, 0, "REQUEST", 0, 1, 0, 5, "WEIRD"); if (ctx->big_request) w += snprintf(fmt+w, rc, fmt_rm, 0, "REQUEST", 0, 2, 0, 8, "BIG_REQUEST"); } denied_args.data = (unsigned char *)fmt; denied_args.len = w; if (ctx->log && !ctx->block) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "NAXSI_FMT: %s", fmt); return (NGX_DECLINED); } /* add headers with original url and arguments */ ngx_table_elt_t *h; if(r->headers_in.headers.last) { h = ngx_list_push(&(r->headers_in.headers)); h->key.len = strlen("orig_url"); h->key.data = ngx_pcalloc(r->pool, strlen("orig_url")+1); memcpy(h->key.data, "orig_url", strlen("orig_url")); h->lowcase_key = ngx_pcalloc(r->pool, strlen("orig_url") + 1); memcpy(h->lowcase_key, "orig_url", strlen("orig_url")); h->value.len = tmp_uri.len; h->value.data = ngx_pcalloc(r->pool, tmp_uri.len+1); memcpy(h->value.data, tmp_uri.data, tmp_uri.len); h = ngx_list_push(&(r->headers_in.headers)); h->key.len = strlen("orig_args"); h->key.data = ngx_pcalloc(r->pool, strlen("orig_args")+1); memcpy(h->key.data, "orig_args", strlen("orig_args")); h->lowcase_key = ngx_pcalloc(r->pool, strlen("orig_args") + 1); memcpy(h->lowcase_key, "orig_args", strlen("orig_args")); h->value.len = r->args.len; h->value.data = ngx_pcalloc(r->pool, r->args.len+1); memcpy(h->value.data, r->args.data, r->args.len); h = ngx_list_push(&(r->headers_in.headers)); h->key.len = strlen("naxsi_sig"); h->key.data = ngx_pcalloc(r->pool, strlen("naxsi_sig")+1); memcpy(h->key.data, "naxsi_sig", strlen("naxsi_sig")); h->lowcase_key = ngx_pcalloc(r->pool, strlen("naxsi_sig") + 1); memcpy(h->lowcase_key, "naxsi_sig", strlen("naxsi_sig")); h->value.len = denied_args.len; h->value.data = denied_args.data; } else if (ctx->learning) ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "[naxsi] no headers_in, not forwarded to learning mode."); if (ctx->learning) { if (ctx->post_action) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); clcf->post_action.data = cf->denied_url->data; clcf->post_action.len = cf->denied_url->len; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "NAXSI_FMT: %s", fmt); return (NGX_DECLINED); } else { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "NAXSI_FMT: %s", fmt); rc = ngx_http_internal_redirect(r, cf->denied_url, &denied_args); return (NGX_HTTP_OK); } return (NGX_ERROR); } /* ** new rulematch, less arguments ^ */ //#define whitelist_debug /* #define whitelist_light_debug */ /* #define whitelist_heavy_debug */ int ngx_http_apply_rulematch_v_n(ngx_http_rule_t *r, ngx_http_request_ctx_t *ctx, ngx_http_request_t *req, ngx_str_t *name, ngx_str_t *value, enum DUMMY_MATCH_ZONE zone, ngx_int_t nb_match, ngx_int_t target_name) { unsigned int found = 0, i, z; ngx_http_special_score_t *sc, *rsc; ngx_http_dummy_loc_conf_t *cf; ngx_http_matched_rule_t *mr; ngx_str_t empty=ngx_string(""); if (!name) name = ∅ if (!value) value = ∅ cf = ngx_http_get_module_loc_conf(req, ngx_http_naxsi_module); if (!cf || !ctx ) return (0); if (ngx_http_dummy_is_rule_whitelisted_n(req, cf, r, name, zone, target_name) == 1) { #ifdef whitelist_light_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "rule is whitelisted."); #endif return (0); } //XX42 #ifdef extensive_log_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Current extensive log value: %d", ctx->extensive_log); #endif if (ctx->extensive_log) { if (target_name) naxsi_log_offending(value, name, req, r, zone); else naxsi_log_offending(name, value, req, r, zone); } if (nb_match == 0) nb_match = 1; if (!ctx->matched) ctx->matched = ngx_array_create(req->pool, 2, sizeof(ngx_http_matched_rule_t)); /* log stuff, cause this case sux */ if (!ctx->matched) return (0); mr = ngx_array_push(ctx->matched); if (!mr) return (0); memset(mr, 0, sizeof(ngx_http_matched_rule_t)); if (target_name) mr->target_name = 1; switch(zone) { case HEADERS: mr->headers_var = 1; break; case URL: mr->url = 1; break; case ARGS: mr->args_var = 1; break; case BODY: mr->body_var = 1; break; case FILE_EXT: mr->file_ext = 1; break; default: break; }; mr->rule = r; // the current "name" ptr will be free by caller, so make a copy mr->name = ngx_pcalloc(req->pool, sizeof(ngx_str_t)); if (name->len > 0) { mr->name->data = ngx_pcalloc(req->pool, name->len+1); memcpy(mr->name->data, name->data, name->len); mr->name->len = name->len; } else { mr->name->data = NULL; mr->name->len = 0; } /* apply special score on rulematch */ if (r->sscores) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Rule applies %d custom scores", r->sscores->nelts); #endif if (!ctx->special_scores) //create the list ctx->special_scores = ngx_array_create(req->pool, 1, sizeof(ngx_http_special_score_t)); found = 0; rsc = r->sscores->elts; for (z = 0; z < r->sscores->nelts; z++) { //search into the list for matching special score found = 0; sc = ctx->special_scores->elts; for (i = 0; i < ctx->special_scores->nelts; i++) { if (rsc[z].sc_tag && sc[i].sc_tag && sc[i].sc_tag->len == rsc[z].sc_tag->len && !ngx_strcmp(sc[i].sc_tag->data, rsc[z].sc_tag->data)) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Special Score (%V) actual=%d,next=%d", rsc[z].sc_tag, sc[i].sc_score, sc[i].sc_score+(rsc[z].sc_score * nb_match)); #endif sc[i].sc_score += (rsc[z].sc_score * nb_match); found = 1; break; } } if (!found) { #ifdef whitelist_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "Special Score (%V) next=%d", rsc[z].sc_tag, (rsc[z].sc_score * nb_match)); #endif sc = ngx_array_push(ctx->special_scores); if (!sc) return (0); memset(sc, 0, sizeof(ngx_http_special_score_t)); sc->sc_tag = rsc[z].sc_tag; sc->sc_score = (rsc[z].sc_score * nb_match); } } } //else { /* else, apply normal score */ ctx->score += (r->score * nb_match); if (r->block) ctx->block = 1; if (r->allow) ctx->allow = 1; if (r->log) ctx->log = 1; //} ngx_http_dummy_update_current_ctx_status(ctx, cf, req); return (1); } /* ** does : this functions receives an string in the form [foo=bar&bla=foo..] ** it splits the string into varname/value couples, and then pass ** this couple along with valid rules to checking func. ** WARN/TODO : Even I tried to make my code bof proof, this should be seriously audited :) */ //#define spliturl_ruleset_debug int ngx_http_spliturl_ruleset(ngx_pool_t *pool, char *str, ngx_array_t *rules, ngx_array_t *main_rules, ngx_http_request_t *req, ngx_http_request_ctx_t *ctx, enum DUMMY_MATCH_ZONE zone) { ngx_str_t name, val; char *eq, *ev, *orig; int len, full_len; int nullbytes=0; #ifdef spliturl_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-check url-like [%s]", str); #endif orig = str; full_len = strlen(orig); while (str < (orig+full_len) && *str) { if (*str == '&') { str++; continue; } if (ctx->block && !ctx->learning) return (0); eq = strchr(str, '='); ev = strchr(str, '&'); if ((!eq && !ev) /*?foobar */ || (eq && ev && eq > ev)) /*?foobar&bla=test*/ { if (!ev) ev = str+strlen(str); /* len is now [name] */ len = ev - str; val.data = (unsigned char *) str; val.len = ev - str; name.data = (unsigned char *) NULL; name.len = 0; } /* ?&&val | ?var&& | ?val& | ?&val | ?val&var */ else if (!eq && ev) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_url, ctx, req, NULL, NULL, zone, 1, 0); if (ev > str) /* ?var& | ?var&val */ { val.data = (unsigned char *) str; val.len = ev - str; name.data = (unsigned char *) NULL; name.len = 0; len = ev - str; } else /* ?& | ?&&val */ { val.data = name.data = NULL; val.len = name.len = 0; len = 1; } } else /* should be normal like ?var=bar& ..*/ { if (!ev) /* ?bar=lol */ ev = str+strlen(str); /* len is now [name]=[content] */ len = ev - str; eq = strnchr(str, '=', len); if (!eq) { dummy_error_fatal(ctx, req, "malformed url, possible attack [%s]", str); return (1); } eq++; val.data = (unsigned char *) eq; val.len = ev - eq; name.data = (unsigned char *) str; name.len = eq - str - 1; } if (val.len || name.len) { nullbytes = naxsi_unescape(&name); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, req, &name, &val, zone, 1, 1); } nullbytes = naxsi_unescape(&val); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, req, &name, &val, zone, 1, 0); } #ifdef spliturl_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-extract [%V]=[%V]", &(name), &(val)); #endif if (rules) ngx_http_basestr_ruleset_n(pool, &name, &val, rules, req, ctx, zone); #ifdef spliturl_ruleset_debug else ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-no arg rules ?"); #endif if (main_rules) ngx_http_basestr_ruleset_n(pool, &name, &val, main_rules, req, ctx, zone); #ifdef spliturl_ruleset_debug else ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-no main rules ?"); #endif } str += len; } return (0); } /* ** check variable + name against a set of rules, checking against 'custom' location rules too. */ //#define basestr_ruleset_debug int ngx_http_basestr_ruleset_n(ngx_pool_t *pool, ngx_str_t *name, ngx_str_t *value, ngx_array_t *rules, ngx_http_request_t *req, ngx_http_request_ctx_t *ctx, enum DUMMY_MATCH_ZONE zone) { ngx_http_rule_t *r; unsigned int i, ret, z; ngx_int_t nb_match=0; ngx_http_custom_rule_location_t *location; #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-check check [%V]=[%V] in zone %s", name, value, zone == BODY ? "BODY" : zone == HEADERS ? "HEADERS" : zone == URL ? "URL" : zone == ARGS ? "ARGS" : zone == FILE_EXT ? "FILE_EXT" : "UNKNOWN"); #endif if (!rules) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-no rules, wtf ?!"); return (0); } r = rules->elts; #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-checking rules ..."); #endif for (i = 0; i < rules->nelts && (!ctx->block || ctx->learning) ; i++) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-rule %d (%V=%V)", r[i].rule_id, name, value); #endif /* does the rule have a custom location ? custom location means checking only on a specific argument */ if (name && name->len > 0 && r[i].br->custom_location) { location = r[i].br->custom_locations->elts; /* for each custom location */ for (z = 0; z < r[i].br->custom_locations->nelts; z++) { /* if the name are the same, check */ if (name->len == location[z].target.len && !strncasecmp((const char *)name->data, (const char *) location[z].target.data, location[z].target.len)) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-[SPECIFIC] check one rule [%d] iteration %d * %d", r[i].rule_id, i, z); #endif /* match rule against var content, */ ret = ngx_http_process_basic_rule_buffer(value, &(r[i]), &nb_match); if (ret == 1) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-apply rulematch [%V]=[%V] [rule=%d] (match %d times)", name, value, r[i].rule_id, nb_match); #endif ngx_http_apply_rulematch_v_n(&(r[i]), ctx, req, name, value, zone, nb_match, 0); } if (!r[i].br->negative) { /* match rule against var name, */ ret = ngx_http_process_basic_rule_buffer(name, &(r[i]), &nb_match); /* if our rule matched, apply effects (score etc.) */ if (ret == 1) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-apply rulematch[in name] [%V]=[%V] [rule=%d] (match %d times)", name, value, r[i].rule_id, nb_match); #endif ngx_http_apply_rulematch_v_n(&(r[i]), ctx, req, name, name, zone, nb_match, 1); } } } } } /* ** check against the rule if the current zone is matching ** the zone the rule is meant to be check against */ if ( (zone == HEADERS && r[i].br->headers) || (zone == URL && r[i].br->url) || (zone == ARGS && r[i].br->args) || (zone == BODY && r[i].br->body && !r[i].br->file_ext) || (zone == FILE_EXT && r[i].br->file_ext) ) { /* #ifdef basestr_ruleset_debug */ /* ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, */ /* "XX-check [%V]=[%V] [rule=%d] (%d times)", name, value, r[i].rule_id, nb_match); */ /* #endif */ #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-test rulematch!1 [%V]=[%V] [rule=%d] (%d times)", name, value, r[i].rule_id, nb_match); #endif /* check the rule against the value*/ ret = ngx_http_process_basic_rule_buffer(value, &(r[i]), &nb_match); /*if our rule matched, apply effects (score etc.)*/ if (ret == 1) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-apply rulematch!1 [%V]=[%V] [rule=%d] (%d times)", name, value, r[i].rule_id, nb_match); #endif ngx_http_apply_rulematch_v_n(&(r[i]), ctx, req, name, value, zone, nb_match, 0); } if (!r[i].br->negative) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-test rulematch!1 [%V]=[%V] [rule=%d] (%d times)", name, value, r[i].rule_id, nb_match); #endif /* check the rule against the name*/ ret = ngx_http_process_basic_rule_buffer(name, &(r[i]), &nb_match); /*if our rule matched, apply effects (score etc.)*/ if (ret == 1) { #ifdef basestr_ruleset_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, req->connection->log, 0, "XX-apply rulematch!1 [%V]=[%V] [rule=%d] (%d times)", name, value, r[i].rule_id, nb_match); #endif ngx_http_apply_rulematch_v_n(&(r[i]), ctx, req, name, value, zone, nb_match, 1); } } } } return (0); } /* ** does : parse body data, a.k.a POST/PUT datas. identifies content-type, ** and, if appropriate, boundary. then parse the stuff if multipart/for.. ** or rely on spliturl if application/x-w.. ** [XXX] : this function sucks ! I don't parse bigger-than-body-size posts that ** are partially stored in files, TODO ;) */ #define post_heavy_debug /* ** Parse content-disposition line. */ int nx_content_disposition_parse(unsigned char *str, unsigned char *line_end, unsigned char **fvarn_start, unsigned char **fvarn_end, unsigned char **ffilen_start, unsigned char **ffilen_end, ngx_http_request_t *r) { unsigned char *varn_start = NULL, *varn_end = NULL; unsigned char *filen_start = NULL, *filen_end = NULL; /* we have two cases : ** ---- file upload ** Content-Disposition: form-data; name="somename"; filename="NetworkManager.conf"\r\n ** Content-Type: application/octet-stream\r\n\r\n ** ** ---- normal post var ** Content-Disposition: form-data; name="lastname"\r\n\r\n ** */ while (str < line_end) { /* rfc allow spaces and tabs inbetween */ while (str < line_end && *str && (*str == ' ' || *str == '\t')) str++; if (str < line_end && *str && *str == ';') str++; while (str < line_end && *str && (*str == ' ' || *str == '\t')) str++; if (str >= line_end || !*str) break; if (!ngx_strncmp(str, "name=\"", 6)) { varn_end = varn_start = str + 6; do { varn_end = (unsigned char *) ngx_strchr(varn_end, '"'); if (varn_end && *(varn_end - 1) != '\\') break; varn_end++; } while (varn_end && varn_end < line_end); if (!varn_end || !*varn_end) return (NGX_ERROR); str = varn_end; if (str < line_end+1) str++; else return (NGX_ERROR); *fvarn_start = varn_start; *fvarn_end = varn_end; } else if (!ngx_strncmp(str, "filename=\"", 10)) { filen_end = filen_start = str + 10; do { filen_end = (unsigned char *) ngx_strchr(filen_end, '"'); if (filen_end && *(filen_end - 1) != '\\') break; filen_end++; } while (filen_end && filen_end < line_end); if (!filen_end) return (NGX_ERROR); str = filen_end; if (str < line_end+1) str++; else return (NGX_ERROR); *ffilen_end = filen_end; *ffilen_start = filen_start; } else if (str == line_end -1) break; else { /* gargabe is present ?*/ #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "extra data in content-disposition ? end:%p, str:%p, diff=%d", line_end, str, line_end-str); #endif return (NGX_ERROR); } } /* tssk tssk */ if (filen_end > line_end || varn_end > line_end) return (NGX_ERROR); return (NGX_OK); } int nx_content_type_parse(ngx_http_request_t *r, unsigned char **boundary, unsigned int *boundary_len) { unsigned char *h; unsigned char *end; h = r->headers_in.content_type->value.data + strlen("multipart/form-data;"); end = r->headers_in.content_type->value.data + r->headers_in.content_type->value.len; /* skip potential whitespace/tabs */ while (h < end && *h && (*h == ' ' || *h == '\t')) h++; if (strncmp((const char *) h, "boundary=", 9)) return (NGX_ERROR); h += 9; *boundary_len = end - h; *boundary = h; /* RFC 1867 says 70 char max */ if (*boundary_len > 70) return (NGX_ERROR); return (NGX_OK); } //#define dummy_body_parse_debug void ngx_http_dummy_multipart_parse(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r, u_char *src, u_int len) { ngx_str_t final_var, final_data; u_char *boundary, *varn_start, *varn_end; u_char *filen_start, *filen_end; u_char *end, *line_end; u_int boundary_len, varn_len, varc_len, idx, nullbytes; ngx_http_dummy_loc_conf_t *cf; ngx_http_dummy_main_conf_t *main_cf; cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module); main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module); /*extract boundary*/ if (nx_content_type_parse(r, (unsigned char **) &boundary, &boundary_len) != NGX_OK) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_boundary, ctx, r, NULL, NULL, BODY, 1, 0); return ; } /* fetch every line starting with boundary */ idx = 0; while (idx < len) { #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-POST data : (%s)", src+idx); ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Remaining Len:%d (boundary len:%d)", len - idx, boundary_len); #endif /* if we've reached the last boundary '--' + boundary + '--' + '\r\n'$END */ /* Authorize requests that don't have the leading \r\n */ if (idx+boundary_len+6 == len || idx+boundary_len+4 == len) { if (ngx_strncmp(src+idx, "--", 2) || ngx_strncmp(src+idx+2, boundary, boundary_len) || ngx_strncmp(src+idx+boundary_len+2, "--", 2)) { /* bad closing boundary ?*/ ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_boundary, ctx, r, NULL, NULL, BODY, 1, 0); return ; } else break; } /* --boundary\r\n : New var */ if ((len - idx < 4 + boundary_len) || src[idx] != '-' || src[idx+1] != '-' || /* and if it's really followed by a boundary */ ngx_strncmp(src+idx+2, boundary, boundary_len) || /* and if it's not the last boundary of the buffer */ idx+boundary_len + 2 + 2 >= len || /* and if it's followed by \r\n */ src[idx+boundary_len+2] != '\r' || src[idx+boundary_len+3] != '\n') { /* bad boundary */ ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_boundary, ctx, r, NULL, NULL, BODY, 1, 0); return ; } idx += boundary_len + 4; /* we have two cases : ** ---- file upload ** Content-Disposition: form-data; name="somename"; filename="NetworkManager.conf"\r\n ** Content-Type: application/octet-stream\r\n\r\n ** ** ---- normal post var ** Content-Disposition: form-data; name="lastname"\r\n\r\n ** */ if (ngx_strncasecmp(src+idx, (u_char *) "content-disposition: form-data;", 30)) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Unknown content-type: [%s]", src+idx); ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : unknown content-disposition"); return ; } idx += 30; line_end = (u_char *) ngx_strchr(src+idx, '\n'); if (!line_end) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : malformed boundary line"); return ; } /* Parse content-disposition, extract name / filename */ varn_start = varn_end = filen_start = filen_end = NULL; if (nx_content_disposition_parse(src+idx, line_end, &varn_start, &varn_end, &filen_start, &filen_end, r) != NGX_OK) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); return ; } /* var name is mandatory */ if (!varn_start || !varn_end || varn_end <= varn_start) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : no 'name' in POST var"); return ; } varn_len = varn_end - varn_start; /* If there is a filename, it is followed by a "content-type" line, skip it */ if (filen_start && filen_end) { line_end = (u_char *) ngx_strchr(line_end+1, '\n'); if (!line_end) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : malformed filename (no content-type ?)"); return ; } } /* ** now idx point to the end of the ** content-disposition: form-data; filename="" name="" */ idx += (u_char *)line_end - (src+idx) + 1; if (src[idx] != '\r' || src[idx+1] != '\n') { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : malformed content-disposition line"); return ; } idx += 2; /* seek the end of the data */ end = NULL; while (idx < len) { end = (u_char *) ngx_strstr(src+idx, "\r\n--"); /* file data can contain \x0 */ while (!end) { idx += strlen((const char *)src+idx); if (idx < len - 2) { idx++; end = (u_char *) ngx_strstr(src+idx, "\r\n--"); } else break; } if (!end) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_post_format, ctx, r, NULL, NULL, BODY, 1, 0); dummy_error_fatal(ctx, r, "POST data : malformed content-disposition line"); return ; } if (!ngx_strncmp(end+4, boundary, boundary_len)) break; else { idx += ((u_char *) end - (src+idx)) + 1; end = NULL; } } if (!end) { dummy_error_fatal(ctx, r, "POST data : malformed line"); return ; } if (filen_start) { final_var.data = (unsigned char *)varn_start; final_var.len = varn_len; final_data.data = (unsigned char *)filen_start; final_data.len = filen_end - filen_start; nullbytes = naxsi_unescape(&final_var); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, r, &final_var, &final_data, BODY, 1, 1); } nullbytes = naxsi_unescape(&final_data); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, r, &final_var, &final_data, BODY, 1, 0); } #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] checking filename [%V] = [%V]", &final_var, &final_data); #endif /* here we got val name + val content !*/ if (cf->body_rules) ngx_http_basestr_ruleset_n(r->pool, &final_var, &final_data, cf->body_rules, r, ctx, FILE_EXT); #ifdef post_heavy_debug else /* here we got val name + val content !*/ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] No local body rules"); #endif if (main_cf->body_rules) ngx_http_basestr_ruleset_n(r->pool, &final_var, &final_data, main_cf->body_rules, r, ctx, FILE_EXT); #ifdef post_heavy_debug else /* here we got val name + val content !*/ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] No main body rules"); #endif idx += (u_char *) end - (src+idx); } else if (varn_start) { varc_len = (u_char *) end - (src+idx); final_var.data = (unsigned char *)varn_start; final_var.len = varn_len; final_data.data = src+idx; final_data.len = varc_len; nullbytes = naxsi_unescape(&final_var); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, r, &final_var, &final_data, BODY, 1, 1); } nullbytes = naxsi_unescape(&final_data); if (nullbytes > 0) { ngx_http_apply_rulematch_v_n(&nx_int__uncommon_hex_encoding, ctx, r, &final_var, &final_data, BODY, 1, 0); } #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] [%V]=[%V]", &final_var, &final_data); #endif /* here we got val name + val content !*/ if (cf->body_rules) ngx_http_basestr_ruleset_n(r->pool, &final_var, &final_data, cf->body_rules, r, ctx, BODY); #ifdef post_heavy_debug else /* here we got val name + val content !*/ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "No local body rules ?!"); #endif if (main_cf->body_rules) ngx_http_basestr_ruleset_n(r->pool, &final_var, &final_data, main_cf->body_rules, r, ctx, BODY); #ifdef post_heavy_debug else /* here we got val name + val content !*/ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "No main body rules ?!"); #endif idx += (u_char *) end - (src+idx); } else { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "(multipart) : "); } if (!ngx_strncmp(end, "\r\n", 2)) idx += 2; } } //#define dummy_body_parse_debug void ngx_http_dummy_body_parse(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r, ngx_http_dummy_loc_conf_t *cf, ngx_http_dummy_main_conf_t *main_cf) { u_char *src; ngx_str_t tmp; ngx_chain_t *bb; u_char *full_body; u_int full_body_len; #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-BODY PARSE"); #endif if (!r->request_body->bufs || !r->headers_in.content_type) { #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-No content type .."); #endif ngx_http_apply_rulematch_v_n(&nx_int__uncommon_content_type, ctx, r, NULL, NULL, BODY, 1, 0); return ; } if (r->request_body->temp_file) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "naxsi: POST REQUEST to temp_file, partially parsed."); ngx_http_apply_rulematch_v_n(&nx_int__big_request, ctx, r, NULL, NULL, BODY, 1, 0); return ; } #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-VALID BODY"); #endif /* request body in single buffer */ if (r->request_body->bufs->next == NULL) { full_body_len = (u_int) (r->request_body->bufs->buf->last - r->request_body->bufs->buf->pos); full_body = ngx_pcalloc(r->pool, (u_int) (full_body_len+1)); memcpy(full_body, r->request_body->bufs->buf->pos, full_body_len); } /* request body in chain */ else { #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] REQUEST BODY IN CHAIN !"); #endif for (full_body_len = 0, bb = r->request_body->bufs; bb; bb = bb->next) full_body_len += (bb->buf->last - bb->buf->pos); full_body = ngx_pcalloc(r->pool, full_body_len+1); src = full_body; if (!full_body) return ; for(bb = r->request_body->bufs ; bb ; bb = bb->next) full_body = ngx_cpymem(full_body, bb->buf->pos, bb->buf->last - bb->buf->pos); full_body = src; #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] REQUEST BODY IN CHAIN [%s] (len=%d)", full_body, full_body_len); #endif } #ifdef dummy_body_parse_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "content-len header (%d) mismatch actual len (%d) ??", r->headers_in.content_length_n, full_body_len); #endif /* File probably got buffered. */ if (r->headers_in.content_length_n != full_body_len) { ngx_http_apply_rulematch_v_n(&nx_int__big_request, ctx, r, NULL, NULL, BODY, 1, 0); return ; } /* x-www-form-urlencoded POSTs */ /* 33 = echo -n "application/x-www-form-urlencoded" | wc -c */ if (!ngx_strncasecmp(r->headers_in.content_type->value.data, (u_char *)"application/x-www-form-urlencoded", 33)) { #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-application/x-www.."); #endif tmp.len = full_body_len; tmp.data = full_body; #ifdef post_heavy_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-POST DATA [%V]", &tmp); #endif if(ngx_http_spliturl_ruleset(r->pool, (char *)tmp.data, cf->body_rules, main_cf->body_rules, r, ctx, BODY)) { #ifdef post_heavy_debug dummy_error_fatal(ctx, r, "spliturl failed, someone is trying to trick us"); #endif ngx_http_apply_rulematch_v_n(&nx_int__uncommon_url, ctx, r, NULL, NULL, BODY, 1, 0); return ; } } /* 19 = echo -n "multipart/form-data" | wc -c */ else if (!ngx_strncasecmp(r->headers_in.content_type->value.data, (u_char *) "multipart/form-data", 19)) { ngx_http_dummy_multipart_parse(ctx, r, full_body, full_body_len); } else { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[POST] Unknown content-type"); ngx_http_apply_rulematch_v_n(&nx_int__uncommon_content_type, ctx, r, NULL, NULL, BODY, 1, 0); } } /* ** does : this is a 'main' function, all the stuff goes from here. ** to make it short, it does the following : ** - if we got header rules, apply header_rules on each. ** - apply generic_rules on url decoded URI. ** - if we got get_rules and get args, apply get_rules varname/value couple. ** - if we are in a POST/PUT request and we got body_rules, apply rules :) */ void ngx_http_dummy_uri_parse(ngx_http_dummy_main_conf_t *main_cf, ngx_http_dummy_loc_conf_t *cf, ngx_http_request_ctx_t *ctx, ngx_http_request_t *r) { ngx_str_t tmp, name; if (!r->uri.len) return ; if (ctx->block && !ctx->learning) return ; if (!main_cf->generic_rules && !cf->generic_rules) { dummy_error_fatal(ctx, r, "no generic rules ?!"); return ; } tmp.len = r->uri.len; tmp.data = ngx_pcalloc(r->pool, r->uri.len+1); if (!tmp.data) { dummy_error_fatal(ctx, r, "failed alloc of %d", r->uri.len+1); return ; } memcpy(tmp.data, r->uri.data, r->uri.len); name.data = NULL; name.len = 0; if (cf->generic_rules) ngx_http_basestr_ruleset_n(r->pool, &name, &tmp, cf->generic_rules, r, ctx, URL); if (main_cf->generic_rules) ngx_http_basestr_ruleset_n(r->pool, &name, &tmp, main_cf->generic_rules, r, ctx, URL); ngx_pfree(r->pool, tmp.data); } void ngx_http_dummy_args_parse(ngx_http_dummy_main_conf_t *main_cf, ngx_http_dummy_loc_conf_t *cf, ngx_http_request_ctx_t *ctx, ngx_http_request_t *r) { ngx_str_t tmp; if (ctx->block && !ctx->learning) return ; if (!r->args.len) return ; if (!cf->get_rules && !main_cf->get_rules) return ; tmp.len = r->args.len; tmp.data = ngx_pcalloc(r->pool, r->args.len+1); if (!tmp.data) { dummy_error_fatal(ctx, r, "failed alloc"); return ; } memcpy(tmp.data, r->args.data, r->args.len); if(ngx_http_spliturl_ruleset(r->pool, (char *)tmp.data, cf->get_rules, main_cf->get_rules, r, ctx, ARGS)) { dummy_error_fatal(ctx, r, "spliturl error : malformed url, possible attack"); return ; } ngx_pfree(r->pool, tmp.data); } void ngx_http_dummy_headers_parse(ngx_http_dummy_main_conf_t *main_cf, ngx_http_dummy_loc_conf_t *cf, ngx_http_request_ctx_t *ctx, ngx_http_request_t *r) { ngx_list_part_t *part; ngx_table_elt_t *h; unsigned int i; if (!cf->header_rules && !main_cf->header_rules) return ; // this check may be removed, as it shouldn't be needed anymore ! if (ctx->block && !ctx->learning) return ; part = &r->headers_in.headers.part; h = part->elts; // this check may be removed, as it shouldn't be needed anymore ! for (i = 0; !ctx->block ; i++) { if (i >= part->nelts) { if (part->next == NULL) break; part = part->next; h = part->elts; i = 0; } if (cf->header_rules) ngx_http_basestr_ruleset_n(r->pool, &(h[i].key), &(h[i].value), cf->header_rules, r, ctx, HEADERS); if (main_cf->header_rules) ngx_http_basestr_ruleset_n(r->pool, &(h[i].key), &(h[i].value), main_cf->header_rules, r, ctx, HEADERS); } return ; } void ngx_http_dummy_data_parse(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r) { ngx_http_dummy_loc_conf_t *cf; ngx_http_dummy_main_conf_t *main_cf; ngx_http_core_main_conf_t *cmcf; cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); main_cf = ngx_http_get_module_main_conf(r, ngx_http_naxsi_module); if (!cf || !ctx || !cmcf) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "naxsi: unable to parse data."); return ; } /* process rules only if request is not already blocked or if the learning mode is enabled */ ngx_http_dummy_headers_parse(main_cf, cf, ctx, r); /* check uri */ ngx_http_dummy_uri_parse(main_cf, cf, ctx, r); /* check args */ ngx_http_dummy_args_parse(main_cf, cf, ctx, r); /* check method */ if ((r->method == NGX_HTTP_POST || r->method == NGX_HTTP_PUT) && /* presence of body rules (POST/PUT rules) */ (cf->body_rules || main_cf->body_rules) && /* and the presence of data to parse */ r->request_body && (!ctx->block || ctx->learning)) ngx_http_dummy_body_parse(ctx, r, cf, main_cf); ngx_http_dummy_update_current_ctx_status(ctx, cf, r); } //#define custom_score_debug void ngx_http_dummy_update_current_ctx_status(ngx_http_request_ctx_t *ctx, ngx_http_dummy_loc_conf_t *cf, ngx_http_request_t *r) { unsigned int i, z, matched; ngx_http_check_rule_t *cr; ngx_http_special_score_t *sc; #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-custom check rules"); #endif if (ctx->weird_request) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-blocking, weird_request flag set"); #endif ctx->block = 1; } if (ctx->big_request) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-blocking unexpected big request"); #endif ctx->block = 1; } /*cr, sc, cf, ctx*/ if (cf->check_rules && ctx->special_scores) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-we have custom check rules and CTX got special score :)"); #endif cr = cf->check_rules->elts; sc = ctx->special_scores->elts; for (z = 0; z < ctx->special_scores->nelts; z++) for (i = 0; i < cf->check_rules->nelts; i++) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX- rule says :(%s:%d) vs current context:(%s:%d) (flag=%d)", cr[i].sc_tag.data, cr[i].sc_score, sc[z].sc_tag->data, sc[z].sc_score, cr[i].cmp); #endif if (!ngx_strcmp(sc[z].sc_tag->data, cr[i].sc_tag.data)) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX- rule says :(%s:%d) vs current context:(%s:%d) (flag=%d)", cr[i].sc_tag.data, cr[i].sc_score, sc[z].sc_tag->data, sc[z].sc_score, cr[i].cmp); #endif matched=0; // huglier than your mom :) switch (cr[i].cmp) { case SUP: matched = sc[z].sc_score > cr[i].sc_score ? 1 : 0; break; case SUP_OR_EQUAL: matched = sc[z].sc_score >= cr[i].sc_score ? 1 : 0; break; case INF: matched = sc[z].sc_score < cr[i].sc_score ? 1 : 0; break; case INF_OR_EQUAL: matched = sc[z].sc_score <= cr[i].sc_score ? 1 : 0; break; } if (matched) { #ifdef custom_score_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX- custom score rule triggered .."); #endif if (cr[i].block) ctx->block = 1; if (cr[i].allow) ctx->allow = 1; if (cr[i].log) ctx->log = 1; } } } } } /* ** This function is called when the body is read. ** Will set-up flags to tell that parsing can be done, ** and then run the core phases again ** (WARNING: check backward compatibility of count-- ** with older version of nginx 0.7.x) */ //#define payload_handler_debug void ngx_http_dummy_payload_handler(ngx_http_request_t *r) { ngx_http_request_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_naxsi_module); ctx->ready = 1; r->count--; #ifdef payload_handler_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy PAYLOAD HANDLER !"); #endif if (ctx->wait_for_body) { #ifdef payload_handler_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : AFTER NGX_AGAIN"); #endif ctx->wait_for_body = 0; ngx_http_core_run_phases(r); } } debian/modules/naxsi/naxsi_src/naxsi_config.c0000644000000000000000000003477512305451333016603 0ustar /* * NAXSI, a web application firewall for NGINX * Copyright (C) 2011, Thibault 'bui' Koechlin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "naxsi.h" /* ** TOP LEVEL configuration parsing code */ /* ** code to parse FLAGS and OPTIONS on each line. */ void *dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); void *dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule); /* ** Structures related to the configuration parser */ typedef struct { char *prefix; void *(*pars)(ngx_conf_t *, ngx_str_t *, ngx_http_rule_t *); } ngx_http_dummy_parser_t; static ngx_http_dummy_parser_t rule_parser[] = { {ID_T, dummy_id}, {SCORE_T, dummy_score}, {MSG_T, dummy_msg}, {RX_T, dummy_rx}, {STR_T, dummy_str}, {MATCH_ZONE_T, dummy_zone}, {NEGATIVE_T, dummy_negative}, {WHITELIST_T, dummy_whitelist}, {NULL, NULL} }; void * dummy_negative(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { rule->br->negative = 1; return (NGX_CONF_OK); } //#define score_debug void * dummy_score(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { int score, len; char *tmp_ptr, *tmp_end; ngx_http_special_score_t *sc; rule->score = 0; rule->block = 0; rule->allow = 0; tmp_ptr = (char *) (tmp->data + strlen(SCORE_T)); #ifdef score_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-(debug) dummy score (%V)", tmp); #endif /*allocate scores array*/ if (!rule->sscores) { rule->sscores = ngx_array_create(r->pool, 1, sizeof(ngx_http_special_score_t)); } while (*tmp_ptr) { if (tmp_ptr[0] == '$') { #ifdef score_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-(debug) special scoring rule (%s)", tmp_ptr); #endif tmp_end = strchr(tmp_ptr, ':'); if (!tmp_end) return (NGX_CONF_ERROR); len = tmp_end - tmp_ptr; if (len <= 0) return (NGX_CONF_ERROR); sc = ngx_array_push(rule->sscores); sc->sc_tag = ngx_pcalloc(r->pool, sizeof(ngx_str_t)); if (!sc->sc_tag) return (NGX_CONF_ERROR); sc->sc_tag->data = ngx_pcalloc(r->pool, len+1); if (!sc->sc_tag->data) return (NGX_CONF_ERROR); //memset(rule->sc_tag->data, 0, len+1); memcpy(sc->sc_tag->data, tmp_ptr, len); sc->sc_tag->len = len; sc->sc_score = atoi(tmp_end+1); #ifdef score_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-(debug) special scoring (%V) => (%d)", sc->sc_tag, sc->sc_score); #endif /* move to end of score. */ while ( /*don't overflow*/((unsigned int)((unsigned char *)tmp_ptr - tmp->data)) < tmp->len && /*and seek for next score */ *tmp_ptr != ',') ++tmp_ptr; } else if (tmp_ptr[0] == ',') ++tmp_ptr; else if (!strcasecmp(tmp_ptr, "BLOCK")) { rule->block = 1; tmp_ptr += 5; } else if (!strcasecmp(tmp_ptr, "ALLOW")) { rule->allow = 1; tmp_ptr += 5; } else if (!strcasecmp(tmp_ptr, "LOG")) { rule->log = 1; tmp_ptr += 3; } //or maybe you just want to assign a score else if ( (tmp_ptr[0] >= '0' && tmp_ptr[0] <= '9') || tmp_ptr[0] == '-') { score = atoi((const char *)tmp->data+2); rule->score = score; } else return (NGX_CONF_ERROR); } #ifdef score_debug unsigned int z; ngx_http_special_score_t *scr; scr = rule->sscores->elts; if (rule->sscores) { for (z = 0; z < rule->sscores->nelts; z++) { ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-score n°%d special scoring (%V) => (%d)", z, scr[z].sc_tag, scr[z].sc_score); } } else ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-no custom scores for this rule."); #endif return (NGX_CONF_OK); } //#define dummy_zone_debug void * dummy_zone(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { int tmp_len; ngx_http_custom_rule_location_t *custom_rule; char *tmp_ptr, *tmp_end; if (!rule->br) return (NGX_CONF_ERROR); /* #ifdef dummy_zone_debug */ /* ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "FEU:%V", tmp); */ /* #endif */ tmp_ptr = (char *) tmp->data+strlen(MATCH_ZONE_T); while (*tmp_ptr) { /* #ifdef dummy_zone_debug */ /* ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "FEU:%s", tmp_ptr); */ /* #endif */ if (tmp_ptr[0] == '|') tmp_ptr++; /* match global zones */ if (!strncmp(tmp_ptr, "BODY", strlen("BODY"))) { rule->br->body = 1; tmp_ptr += strlen("BODY"); continue; } else if (!strncmp(tmp_ptr, "HEADERS", strlen("HEADERS"))) { rule->br->headers = 1; tmp_ptr += strlen("HEADERS"); continue; } else if (!strncmp(tmp_ptr, "URL", strlen("URL"))) { rule->br->url = 1; tmp_ptr += strlen("URL"); continue; } else if (!strncmp(tmp_ptr, "ARGS", strlen("ARGS"))) { rule->br->args = 1; tmp_ptr += strlen("ARGS"); continue; } else /* match against variable name*/ if (!strncmp(tmp_ptr, "NAME", strlen("NAME"))) { rule->br->target_name = 1; tmp_ptr += strlen("NAME"); continue; } else /* for file_ext, just push'em in the body rules. when multipart parsing comes in, it'll tag the zone as FILE_EXT as the rule will be pushed in body rules it'll be checked !*/ if (!strncmp(tmp_ptr, "FILE_EXT", strlen("FILE_EXT"))) { rule->br->file_ext = 1; rule->br->body = 1; tmp_ptr += strlen("FILE_EXT"); continue; } else /* custom match zones */ #define MZ_GET_VAR_T "$ARGS_VAR:" #define MZ_HEADER_VAR_T "$HEADERS_VAR:" #define MZ_POST_VAR_T "$BODY_VAR:" #define MZ_SPECIFIC_URL_T "$URL:" //probably a custom zone if (tmp_ptr[0] == '$') { // tag as a custom_location rule. rule->br->custom_location = 1; if (!rule->br->custom_locations) { rule->br->custom_locations = ngx_array_create(r->pool, 1, sizeof(ngx_http_custom_rule_location_t)); if (!rule->br->custom_locations) return (NGX_CONF_ERROR); } custom_rule = ngx_array_push(rule->br->custom_locations); if (!custom_rule) return (NGX_CONF_ERROR); memset(custom_rule, 0, sizeof(ngx_http_custom_rule_location_t)); if (!strncmp(tmp_ptr, MZ_GET_VAR_T, strlen(MZ_GET_VAR_T))) { custom_rule->args_var = 1; rule->br->args_var = 1; tmp_ptr += strlen(MZ_GET_VAR_T); } else if (!strncmp(tmp_ptr, MZ_POST_VAR_T, strlen(MZ_POST_VAR_T))) { custom_rule->body_var = 1; rule->br->body_var = 1; tmp_ptr += strlen(MZ_POST_VAR_T); } else if (!strncmp(tmp_ptr, MZ_HEADER_VAR_T, strlen(MZ_HEADER_VAR_T))) { custom_rule->headers_var = 1; rule->br->headers_var = 1; tmp_ptr += strlen(MZ_HEADER_VAR_T); } else if (!strncmp(tmp_ptr, MZ_SPECIFIC_URL_T, strlen(MZ_SPECIFIC_URL_T))) { custom_rule->specific_url = 1; tmp_ptr += strlen(MZ_SPECIFIC_URL_T); } else return (NGX_CONF_ERROR); tmp_end = strchr((const char *) tmp_ptr, '|'); if (!tmp_end) tmp_end = tmp_ptr + strlen(tmp_ptr); tmp_len = tmp_end - tmp_ptr; if (tmp_len <= 0) return (NGX_CONF_ERROR); custom_rule->target.data = ngx_pcalloc(r->pool, tmp_len+1); if (!custom_rule->target.data) return (NGX_CONF_ERROR); custom_rule->target.len = tmp_len; memcpy(custom_rule->target.data, tmp_ptr, tmp_len); custom_rule->hash = ngx_hash_key_lc(custom_rule->target.data, custom_rule->target.len); #ifdef dummy_zone_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX- ZONE:[%V]", &(custom_rule->target)); #endif tmp_ptr += tmp_len; continue; } else return (NGX_CONF_ERROR); } return (NGX_CONF_OK); } void * dummy_id(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { rule->rule_id = atoi((const char *) tmp->data+strlen(ID_T)); if (rule->rule_id < 0) { ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "id: failed (%s), should be numeric only", tmp->data); return (NGX_CONF_ERROR); } return (NGX_CONF_OK); } void * dummy_str(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { ngx_str_t *str; uint i; if (!rule->br) return (NGX_CONF_ERROR); str = ngx_pcalloc(r->pool, sizeof(ngx_str_t)); if (!str) return (NGX_CONF_ERROR); str->data = tmp->data + strlen(STR_T); str->len = tmp->len - strlen(STR_T); for (i = 0; i < tmp->len; i++) str->data[i] = tolower(str->data[i]); rule->br->str = str; return (NGX_CONF_OK); } void * dummy_msg(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { ngx_str_t *str; if (!rule->br) return (NGX_CONF_ERROR); str = ngx_pcalloc(r->pool, sizeof(ngx_str_t)); if (!str) return (NGX_CONF_ERROR); str->data = tmp->data + strlen(STR_T); str->len = tmp->len - strlen(STR_T); rule->log_msg = str; return (NGX_CONF_OK); } //#define whitelist_debug void * dummy_whitelist(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { ngx_int_t *wl; unsigned int i, ct; ngx_str_t str; str.data = tmp->data + strlen(WHITELIST_T); str.len = tmp->len - strlen(WHITELIST_T); for (ct = 1, i = 0; i < str.len; i++) if (str.data[i] == ',') ct++; wl = ngx_pcalloc(r->pool, sizeof(ngx_int_t) * (ct+1)); if (!wl) return (NGX_CONF_ERROR); //as 0 means "all rules", memset to -1 memset(wl, -1, sizeof(ngx_int_t) * (ct+1)); #ifdef whitelist_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX- allocated %d elems for WL", ct); #endif for (ct = 0, i = 0; i < str.len; i++) { if (i == 0 || str.data[i-1] == ',') { wl[ct] = atoi((const char *)str.data+i); //rule ID can't be negative if (wl[ct] < 0) return (NGX_CONF_ERROR); #ifdef whitelist_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-WL[%d]= %d (idx:%d,%s)", ct, wl[ct], i, str.data); #endif ct++; } } rule->wl_id = wl; return (NGX_CONF_OK); } void * dummy_rx(ngx_conf_t *r, ngx_str_t *tmp, ngx_http_rule_t *rule) { ngx_regex_compile_t *rgc; ngx_str_t ha; if (!rule->br) return (NGX_CONF_ERROR); //just prepare a string to hold the directive without 'rx:' ha.data = tmp->data+strlen(RX_T); ha.len = tmp->len-strlen(RX_T); rgc = ngx_pcalloc(r->pool, sizeof(ngx_regex_compile_t)); if (!rgc) return (NGX_CONF_ERROR); rgc->options = PCRE_CASELESS|PCRE_MULTILINE; rgc->pattern = ha; rgc->pool = r->pool; rgc->err.len = 0; rgc->err.data = NULL; if (ngx_regex_compile(rgc) != NGX_OK) { #ifdef rx_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX-FAILED RX:%V", tmp); #endif return (NGX_CONF_ERROR); } rule->br->rx = rgc; #ifdef rx_debug ngx_conf_log_error(NGX_LOG_EMERG, r, 0, "XX- RX:[%V]", &(rule->br->rx->pattern)); #endif return (NGX_CONF_OK); } /* Parse one rule line */ /* ** in : nb elem, value array, rule to fill ** does : creates a rule struct from configuration line ** For each element name matching a tag ** (cf. rule_parser), then call the associated func. */ //#define dummy_cfg_parse_one_rule_debug void * ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf, ngx_str_t *value, ngx_http_rule_t *current_rule, ngx_int_t nb_elem) { int i, z; void *ret; int valid; if (!value || !value[0].data) return NGX_CONF_ERROR; /* ** parse basic rule */ if (!ngx_strcmp(value[0].data, TOP_CHECK_RULE_T) || !ngx_strcmp(value[0].data, TOP_CHECK_RULE_N) || !ngx_strcmp(value[0].data, TOP_BASIC_RULE_T) || !ngx_strcmp(value[0].data, TOP_BASIC_RULE_N) || !ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_T) || !ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_N)) { #ifdef dummy_cfg_parse_one_rule_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-basic rule %V", &(value[1])); #endif current_rule->type = BR; current_rule->br = ngx_pcalloc(cf->pool, sizeof(ngx_http_basic_rule_t)); if (!current_rule->br) return (NGX_CONF_ERROR); } else { #ifdef dummy_cfg_parse_one_rule_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-crit in rule %V", &(value[1])); #endif return (NGX_CONF_ERROR); } // check each word of config line against each rule for(i = 1; i < nb_elem && value[i].len > 0; i++) { valid = 0; for (z = 0; rule_parser[z].pars; z++) { if (!ngx_strncmp(value[i].data, rule_parser[z].prefix, strlen(rule_parser[z].prefix))) { ret = rule_parser[z].pars(cf, &(value[i]), current_rule); if (ret != NGX_CONF_OK) { #ifdef dummy_cfg_parse_one_rule_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-FAILED PARSING '%s'", value[i].data); #endif return (ret); } valid = 1; } } if (!valid) return (NGX_CONF_ERROR); } /* validate the structure, and fill empty fields.*/ if (!current_rule->log_msg) { current_rule->log_msg = ngx_pcalloc(cf->pool, sizeof(ngx_str_t)); current_rule->log_msg->data = NULL; current_rule->log_msg->len = 0; } return (NGX_CONF_OK); } debian/modules/naxsi/naxsi_src/naxsi.h0000644000000000000000000003025112305451333015244 0ustar /* * NAXSI, a web application firewall for NGINX * Copyright (C) 2011, Thibault 'bui' Koechlin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __FOO_H__ #define __FOO_H__ #define NAXSI_VERSION "0.50" #include #include #include #include #include #include #include #include #include extern ngx_module_t ngx_http_naxsi_module; /* ** Here is globally how the structures are organized : ** ** [[ngx_http_dummy_main_conf_t]] is the main structure for the module. ** it contains the core rules and a set of ngx_http_dummy_loc_conf_t, ** for each NGINX location. ** --- ** [[ngx_http_dummy_loc_conf_t]] is the main structure for any NGINX ** locations, that is - a web site. It contains both pointers to the core ** rules, as well as whitelists, scores, denied_url and all flags. all ** the data of a nginx location is held into the loc_conf_t struct. ** The sets of rules are actually containted into [[ngx_http_rule_t]] structs. ** --- ** [[ngx_http_rule_t]] structs are used to hold any info about a rule, as well ** as whitelists. (whitelists is just a 'kind' of rule). ** */ enum MATCH_TYPE { URI_ONLY=0, NAME_ONLY, MIXED }; enum DUMMY_MATCH_ZONE { HEADERS=0, URL, ARGS, BODY, FILE_EXT, UNKNOWN }; /* ** struct used to store a specific match zone ** in conf : MATCH_ZONE:[GET_VAR|HEADER|POST_VAR]:VAR_NAME: */ typedef struct { /* match in [name] var of body */ ngx_flag_t body_var:1; /* match in [name] var of headers */ ngx_flag_t headers_var:1; /* match in [name] var of args */ ngx_flag_t args_var:1; /* match on URL [name] */ ngx_flag_t specific_url:1; ngx_str_t target; ngx_uint_t hash; } ngx_http_custom_rule_location_t; /* ** WhiteList Rules Definition : ** A whitelist contains : ** - an URI ** ** - one or several sets containing : ** - an variable name ('foo') associated with a zone ($GET_VAR:foo) ** - one or several rules id to whitelist */ #define WEIRD_REQUEST_INTERNAL_RULE_ID 1 #define BIG_BODY_INTERNAL_RULE_ID 2 typedef struct { /* match in full body (POST DATA) */ ngx_flag_t body:1; /* match in [name] var of body */ ngx_flag_t body_var:1; /* match in all headers */ ngx_flag_t headers:1; /* match in [name] var of headers */ ngx_flag_t headers_var:1; /* match in URI */ ngx_flag_t url:1; /* match in args (bla.php?) */ ngx_flag_t args:1; /* match in [name] var of args */ ngx_flag_t args_var:1; /* match on a global flag : weird_request, big_body etc. */ ngx_flag_t flags:1; /* match on file upload extension */ ngx_flag_t file_ext:1; /* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */ ngx_array_t *ids; ngx_str_t *name; } ngx_http_whitelist_location_t; /* ** this struct is used to aggregate all whitelist ** that point to the same URI or the same VARNAME ** all the "subrules" will then be stored in the "whitelist_locations" */ typedef struct { //ngx_http_whitelist_location_t ngx_array_t *whitelist_locations; // zone to wich the WL applies enum DUMMY_MATCH_ZONE zone; // if the "name" is only an url, specify it int uri_only:1; /* does the rule targets the name instead of the content ?*/ int target_name; ngx_str_t *name; ngx_int_t hash; ngx_array_t *ids; } ngx_http_whitelist_rule_t; /* basic rule */ typedef struct { ngx_str_t *str; // string ngx_regex_compile_t *rx; // or regex ngx_int_t transform; //transform rule to apply, as flags. /* ~~~~~ match zones ~~~~~~ */ /* match in full body (POST DATA) */ ngx_flag_t body:1; ngx_flag_t body_var:1; /* match in all headers */ ngx_flag_t headers:1; ngx_flag_t headers_var:1; /* match in URI */ ngx_flag_t url:1; /* match in args (bla.php?) */ ngx_flag_t args:1; ngx_flag_t args_var:1; /* match on flags (weird_uri, big_body etc. */ ngx_flag_t flags:1; /* match on file upload extension */ ngx_flag_t file_ext:1; /* set if defined "custom" match zone (GET_VAR/POST_VAR/...) */ ngx_flag_t custom_location:1; ngx_int_t custom_location_only; /* does the rule targets variable name instead ? */ ngx_int_t target_name; /* custom location match zones list (GET_VAR/POST_VAR ...) */ ngx_array_t *custom_locations; /* ~~~~~~~ specific flags ~~~~~~~~~ */ ngx_flag_t negative:1; } ngx_http_basic_rule_t; /* define for RULE TYPE in rule_t */ #define BR 1 //#define FR 2 UNUSED //#define WR 3 UNUSED /* flags used for 'custom match rules', like $XSS > 7 */ #define SUP 1 #define SUP_OR_EQUAL 2 #define INF 3 #define INF_OR_EQUAL 4 /* ** This struct is used to store custom scores at runtime. ** ie : $XSS = 7 ** tag is the $XSS and sc_score is 7 */ typedef struct { ngx_str_t *sc_tag; ngx_int_t sc_score; ngx_flag_t block:1; ngx_flag_t allow:1; ngx_flag_t log:1; } ngx_http_special_score_t; /* ** This one is very related to the previous one, ** it's used to store a score rule comparison. ** ie : $XSS > 7 */ typedef struct { ngx_str_t sc_tag; ngx_int_t sc_score; ngx_int_t cmp; ngx_flag_t block:1; ngx_flag_t allow:1; ngx_flag_t log:1; } ngx_http_check_rule_t; /* TOP level rule structure */ typedef struct { /* type of the rule */ ngx_int_t type; // simply put a flag if it's a wlr, wl_id array will be used to store the whitelisted IDs ngx_flag_t whitelist:1; ngx_int_t *wl_id; /* "common" data for all rules */ ngx_int_t rule_id; ngx_str_t *log_msg; // a specific log message ngx_int_t score; //also handles DENY and ALLOW /* List of scores increased on rule match. */ ngx_array_t *sscores; /*ngx_str_t *sc_tag; //specific score tag ngx_int_t sc_score; //specific score value*/ ngx_flag_t sc_block:1; // ngx_flag_t sc_allow:1; // // end of specific score tag stuff ngx_flag_t block:1; ngx_flag_t allow:1; ngx_flag_t log:1; /* flag set if we're linked FROM or TO another rule */ ngx_flag_t lnk_to:1; ngx_flag_t lnk_from:1; /* pointers on specific rule stuff */ ngx_http_basic_rule_t *br; } ngx_http_rule_t; typedef struct { ngx_array_t *get_rules; /*ngx_http_rule_t*/ ngx_array_t *body_rules; ngx_array_t *header_rules; ngx_array_t *generic_rules; ngx_array_t *locations; /*ngx_http_dummy_loc_conf_t*/ ngx_log_t *log; } ngx_http_dummy_main_conf_t; /* TOP level configuration structure */ typedef struct { ngx_array_t *get_rules; ngx_array_t *body_rules; ngx_array_t *header_rules; ngx_array_t *generic_rules; ngx_array_t *check_rules; /* raw array of whitelisted rules */ ngx_array_t *whitelist_rules; /* raw array of transformed whitelists */ ngx_array_t *tmp_wlr; /* hash table of whitelisted URL rules */ ngx_hash_t *wlr_url_hash; /* hash table of whitelisted ARGS rules */ ngx_hash_t *wlr_args_hash; /* hash table of whitelisted BODY rules */ ngx_hash_t *wlr_body_hash; /* hash table of whitelisted HEADERS rules */ ngx_hash_t *wlr_headers_hash; /* rules that are globally disabled in one location */ ngx_array_t *disabled_rules; /* counters for both processed requests and blocked requests, used in naxsi_fmt */ size_t request_processed; size_t request_blocked; ngx_int_t error; ngx_array_t *persistant_data; ngx_flag_t extensive:1; ngx_flag_t learning:1; ngx_flag_t enabled:1; ngx_flag_t force_disabled:1; ngx_flag_t pushed:1; ngx_str_t *denied_url; /* precomputed hash for dynamic variable lookup, variable themselves are boolean */ ngx_uint_t flag_enable_h; ngx_uint_t flag_learning_h; ngx_uint_t flag_post_action_h; ngx_uint_t flag_extensive_log_h; } ngx_http_dummy_loc_conf_t; /* ** used to store sets of matched rules during runtime */ typedef struct { /* matched in [name] var of body */ ngx_flag_t body_var:1; /* matched in [name] var of headers */ ngx_flag_t headers_var:1; /* matched in [name] var of args */ ngx_flag_t args_var:1; /* matched on URL */ ngx_flag_t url:1; /* matched in filename [name] of args*/ ngx_flag_t file_ext:1; /* matched within the 'NAME' */ ngx_flag_t target_name:1; ngx_str_t *name; ngx_http_rule_t *rule; } ngx_http_matched_rule_t; /* ** Context structure */ typedef struct { ngx_array_t *special_scores; ngx_int_t score; /* blocking flags */ ngx_flag_t log:1; ngx_flag_t block:1; ngx_flag_t allow:1; /* state */ ngx_flag_t wait_for_body:1; ngx_flag_t ready:1; ngx_flag_t over:1; /* flag request */ ngx_flag_t weird_request:1; ngx_flag_t big_request:1; /* matched rules */ ngx_array_t *matched; /* runtime flags (modifiers) */ ngx_flag_t learning:1; ngx_flag_t enabled:1; ngx_flag_t post_action:1; ngx_flag_t extensive_log:1; } ngx_http_request_ctx_t; #define TOP_DENIED_URL_T "DeniedUrl" #define TOP_LEARNING_FLAG_T "LearningMode" #define TOP_ENABLED_FLAG_T "SecRulesEnabled" #define TOP_DISABLED_FLAG_T "SecRulesDisabled" #define TOP_CHECK_RULE_T "CheckRule" #define TOP_BASIC_RULE_T "BasicRule" #define TOP_MAIN_BASIC_RULE_T "MainRule" /* nginx-style names */ #define TOP_DENIED_URL_N "denied_url" #define TOP_LEARNING_FLAG_N "learning_mode" #define TOP_ENABLED_FLAG_N "rules_enabled" #define TOP_DISABLED_FLAG_N "rules_disabled" #define TOP_CHECK_RULE_N "check_rule" #define TOP_BASIC_RULE_N "basic_rule" #define TOP_MAIN_BASIC_RULE_N "main_rule" /*possible 'tokens' in rule */ #define ID_T "id:" #define TRANSFORM_T "t:" #define SCORE_T "s:" //#define PERSISTANT_SCORE_T "ps:" #define MSG_T "msg:" #define RX_T "rx:" #define STR_T "str:" #define MATCH_ZONE_T "mz:" #define WHITELIST_T "wl:" #define NEGATIVE_T "negative" /* name of hardcoded variables to change behavior of naxsi at runtime */ #define RT_EXTENSIVE_LOG "naxsi_extensive_log" #define RT_ENABLE "naxsi_flag_enable" #define RT_LEARNING "naxsi_flag_learning" #define RT_POST_ACTION "naxsi_flag_post_action" extern ngx_http_dummy_loc_conf_t *dummy_lc; void *ngx_http_dummy_cfg_parse_one_rule(ngx_conf_t *cf, ngx_str_t *value, ngx_http_rule_t *rule, ngx_int_t nb_elem); char *strfaststr(unsigned char *haystack, unsigned int hl, unsigned char *needle, unsigned int nl); char *strnchr(const char *s, int c, int len); char *strncasechr(const char *s, int c, int len); ngx_int_t ngx_http_dummy_create_hashtables(ngx_http_dummy_loc_conf_t *dlc, ngx_conf_t *cf); ngx_int_t ngx_http_dummy_create_hashtables_n(ngx_http_dummy_loc_conf_t *dlc, ngx_conf_t *cf); void ngx_http_dummy_data_parse(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r); ngx_int_t ngx_http_output_forbidden_page(ngx_http_request_ctx_t *ctx, ngx_http_request_t *r); void naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type); int naxsi_unescape(ngx_str_t *str); /* static ngx_int_t ngx_http_dummy_subrequest(ngx_http_request_t *r, */ /* ngx_chain_t *in); */ //ngx_int_t ngx_http_dummy_subrequest(ngx_http_request_t *r); #endif debian/modules/naxsi/naxsi_src/naxsi_skeleton.c0000644000000000000000000010546612305451333017156 0ustar /* * NAXSI, a web application firewall for NGINX * Copyright (C) 2011, Thibault 'bui' Koechlin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you * do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source * files in the program, then also delete it here. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* ** This files contains skeleton functions, ** such as registred handlers. Readers already ** aware of nginx's modules can skip most of this. */ #include "naxsi.h" #include #include /* ** Macro used to print incorrect configuration lines */ #define ngx_http_dummy_line_conf_error(cf, value) do { \ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \ "Naxsi-Config : Incorrect line %V %V (%s/%d)...", \ &(value[0]), &(value[1]), __FILE__, __LINE__); \ } while (0) /* ** Module's registred function/handlers. */ static ngx_int_t ngx_http_dummy_access_handler(ngx_http_request_t *r); static char *ngx_http_dummy_read_main_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_dummy_init(ngx_conf_t *cf); static char *ngx_http_dummy_read_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_naxsi_cr_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_naxsi_ud_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_naxsi_flags_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void *ngx_http_dummy_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_dummy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); void *ngx_http_dummy_create_main_conf(ngx_conf_t *cf); void ngx_http_dummy_payload_handler(ngx_http_request_t *r); /* command handled by the module */ static ngx_command_t ngx_http_dummy_commands[] = { /* BasicRule (in main) */ { ngx_string(TOP_MAIN_BASIC_RULE_T), NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE, ngx_http_dummy_read_main_conf, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, /* BasicRule (in main) - nginx style */ { ngx_string(TOP_MAIN_BASIC_RULE_N), NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE, ngx_http_dummy_read_main_conf, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, /* BasicRule (in loc) */ { ngx_string(TOP_BASIC_RULE_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_1MORE, ngx_http_dummy_read_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* BasicRule (in loc) - nginx style */ { ngx_string(TOP_BASIC_RULE_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_1MORE, ngx_http_dummy_read_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* DeniedUrl */ { ngx_string(TOP_DENIED_URL_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_1MORE, ngx_http_naxsi_ud_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* DeniedUrl - nginx style */ { ngx_string(TOP_DENIED_URL_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_1MORE, ngx_http_naxsi_ud_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* CheckRule */ { ngx_string(TOP_CHECK_RULE_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_1MORE, ngx_http_naxsi_cr_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* CheckRule - nginx style*/ { ngx_string(TOP_CHECK_RULE_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_1MORE, ngx_http_naxsi_cr_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* ** flag rules */ /* Learning Flag */ { ngx_string(TOP_LEARNING_FLAG_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* Learning Flag (nginx style) */ { ngx_string(TOP_LEARNING_FLAG_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* EnableFlag */ { ngx_string(TOP_ENABLED_FLAG_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* EnableFlag (nginx style) */ { ngx_string(TOP_ENABLED_FLAG_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* DisableFlag */ { ngx_string(TOP_DISABLED_FLAG_T), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, /* DisableFlag (nginx style) */ { ngx_string(TOP_DISABLED_FLAG_N), NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF |NGX_CONF_NOARGS, ngx_http_naxsi_flags_loc_conf, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command }; /* ** handlers for configuration phases of the module */ static ngx_http_module_t ngx_http_dummy_module_ctx = { NULL, /* preconfiguration */ ngx_http_dummy_init, /* postconfiguration */ ngx_http_dummy_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_dummy_create_loc_conf, /* create location configuration */ ngx_http_dummy_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_naxsi_module = { NGX_MODULE_V1, &ngx_http_dummy_module_ctx, /* module context */ ngx_http_dummy_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; #define DEFAULT_MAX_LOC_T 10 void * ngx_http_dummy_create_main_conf(ngx_conf_t *cf) { ngx_http_dummy_main_conf_t *mc; mc = ngx_pcalloc(cf->pool, sizeof(ngx_http_dummy_main_conf_t)); if (!mc) return (NGX_CONF_ERROR); mc->locations = ngx_array_create(cf->pool, DEFAULT_MAX_LOC_T, sizeof(ngx_http_dummy_loc_conf_t *)); if (!mc->locations) return (NGX_CONF_ERROR); return (mc); } /* create log conf struct */ static void * ngx_http_dummy_create_loc_conf(ngx_conf_t *cf) { ngx_http_dummy_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dummy_loc_conf_t)); if (conf == NULL) return NULL; dummy_lc = conf; return (conf); } /* merge loc conf */ /* NOTE/WARNING : This function wasn't tested correctly. Actually, we shouldn't merge anything, as configuration is specific 'per' location ? */ static char * ngx_http_dummy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_dummy_loc_conf_t *prev = parent; ngx_http_dummy_loc_conf_t *conf = child; if (conf->whitelist_rules == NULL) conf->whitelist_rules = prev->whitelist_rules; if (conf->check_rules == NULL) conf->check_rules = prev->check_rules; if (conf->body_rules == NULL) conf->body_rules = prev->body_rules; if (conf->header_rules == NULL) conf->header_rules = prev->header_rules; if (conf->generic_rules == NULL) conf->generic_rules = prev->generic_rules; return NGX_CONF_OK; } /* ** This function sets up handlers for ACCESS_PHASE, ** and will call the hashtable creation function ** (whitelist aggregation) */ static ngx_int_t ngx_http_dummy_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_http_dummy_main_conf_t *main_cf; ngx_http_dummy_loc_conf_t **loc_cf; unsigned int i; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module); if (cmcf == NULL || main_cf == NULL) return (NGX_ERROR); /* Register for access phase */ h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) return (NGX_ERROR); *h = ngx_http_dummy_access_handler; /* Go with each locations registred in the srv_conf. */ loc_cf = main_cf->locations->elts; for (i = 0; i < main_cf->locations->nelts; i++) { loc_cf[i]->flag_enable_h = ngx_hash_key_lc((u_char *)RT_ENABLE, strlen(RT_ENABLE)); loc_cf[i]->flag_learning_h = ngx_hash_key_lc((u_char *)RT_LEARNING, strlen(RT_LEARNING)); loc_cf[i]->flag_post_action_h = ngx_hash_key_lc((u_char *)RT_POST_ACTION, strlen(RT_POST_ACTION)); loc_cf[i]->flag_extensive_log_h = ngx_hash_key_lc((u_char *)RT_EXTENSIVE_LOG, strlen(RT_EXTENSIVE_LOG)); if(ngx_http_dummy_create_hashtables_n(loc_cf[i], cf) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "WhiteList Hash building failed"); return (NGX_ERROR); } } return (NGX_OK); } /* ** my hugly configuration parsing function. ** should be rewritten, cause code is hugly and not bof proof at all ** does : top level parsing config function, ** see foo_cfg_parse.c for stuff */ static char * ngx_http_dummy_read_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_dummy_loc_conf_t *alcf = conf, **bar; ngx_http_dummy_main_conf_t *main_cf; ngx_str_t *value; ngx_http_rule_t rule, *rule_r; ngx_http_custom_rule_location_t *location; unsigned int i; #ifdef readconf_debug if (cf) { value = cf->args->elts; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "TOP READ CONF %V %V", &(value[0]), &(value[1])); } #endif if (!alcf || !cf) return (NGX_CONF_ERROR); value = cf->args->elts; main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module); if (!alcf->pushed) { bar = ngx_array_push(main_cf->locations); if (!bar) return (NGX_CONF_ERROR); *bar = alcf; alcf->pushed = 1; } if (!ngx_strcmp(value[0].data, TOP_BASIC_RULE_T)) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-TOP READ CONF %s", value[0].data); #endif memset(&rule, 0, sizeof(ngx_http_rule_t)); if (ngx_http_dummy_cfg_parse_one_rule(cf, value, &rule, cf->args->nelts) != NGX_CONF_OK) { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } /* push in whitelist rules, as it have a whitelist ID array */ if (rule.wl_id) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in whitelist rules", rule.rule_id); #endif if (alcf->whitelist_rules == NULL) { alcf->whitelist_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->whitelist_rules == NULL) { return NGX_CONF_ERROR; } } rule_r = ngx_array_push(alcf->whitelist_rules); if (!rule_r) { return (NGX_CONF_ERROR); } memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* else push in appropriate ruleset */ else { if (rule.br->headers) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in header rules", rule.rule_id); #endif if (alcf->header_rules == NULL) { alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->header_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->header_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in body match rules (POST/PUT) */ if (rule.br->body || rule.br->body_var) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in body rules", rule.rule_id); #endif if (alcf->body_rules == NULL) { alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->body_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->body_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in generic rules, as it's matching the URI */ if (rule.br->url) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in generic rules", rule.rule_id); #endif if (alcf->generic_rules == NULL) { alcf->generic_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->generic_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->generic_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in GET arg rules, but we should push in POST rules too */ if (rule.br->args_var || rule.br->args) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in GET rules", rule.rule_id); #endif if (alcf->get_rules == NULL) { alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->get_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->get_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in custom locations. It's a rule matching a VAR_NAME or an EXACT_URI : - GET_VAR, POST_VAR, URI */ if (rule.br->custom_location) { #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in custom_location rules", rule.rule_id); #endif location = rule.br->custom_locations->elts; for (i = 0; i < rule.br->custom_locations->nelts; i++) { if (location[i].args_var) { if (alcf->get_rules == NULL) { alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->get_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->get_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } if (location[i].body_var) { if (alcf->body_rules == NULL) { alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->body_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->body_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } if (location[i].headers_var) { if (alcf->header_rules == NULL) { alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->header_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->header_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } } } } return (NGX_CONF_OK); } ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } static char * ngx_http_naxsi_cr_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_dummy_loc_conf_t *alcf = conf, **bar; ngx_http_dummy_main_conf_t *main_cf; ngx_str_t *value; ngx_http_check_rule_t *rule_c; unsigned int i; u_char *var_end; if (!alcf || !cf) return (NGX_CONF_ERROR); value = cf->args->elts; main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module); if (!alcf->pushed) { bar = ngx_array_push(main_cf->locations); if (!bar) return (NGX_CONF_ERROR); *bar = alcf; alcf->pushed = 1; } if (ngx_strcmp(value[0].data, TOP_CHECK_RULE_T) && ngx_strcmp(value[0].data, TOP_CHECK_RULE_N)) return (NGX_CONF_ERROR); #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in check rules", rule.rule_id); #endif i = 0; if (!alcf->check_rules) alcf->check_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_check_rule_t)); if (!alcf->check_rules) return (NGX_CONF_ERROR); rule_c = ngx_array_push(alcf->check_rules); if (!rule_c) return (NGX_CONF_ERROR); memset(rule_c, 0, sizeof(ngx_http_check_rule_t)); /* process the first word : score rule */ if (value[1].data[i] == '$') { #ifdef MDBG ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-special score rule !"); #endif var_end = (u_char *) ngx_strchr((value[1].data)+i, ' '); if (!var_end) { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } rule_c->sc_tag.data = ngx_pcalloc(cf->pool, var_end - value[1].data +1); if (!rule_c->sc_tag.data) return (NGX_CONF_ERROR); memcpy(rule_c->sc_tag.data, value[1].data, (var_end - value[1].data)); i += (var_end - value[1].data) + 1; rule_c->sc_tag.len = (var_end - value[1].data); } else { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } // move to next word while (value[1].data[i] && value[1].data[i] == ' ') i++; // get the comparison type if (value[1].data[i] == '>' && value[1].data[i+1] == '=') rule_c->cmp = SUP_OR_EQUAL; else if (value[1].data[i] == '>' && value[1].data[i+1] != '=') rule_c->cmp = SUP; else if (value[1].data[i] == '<' && value[1].data[i+1] == '=') rule_c->cmp = INF_OR_EQUAL; else if (value[1].data[i] == '<' && value[1].data[i+1] != '=') rule_c->cmp = INF; else { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } // move to next word while (value[1].data[i] && !(value[1].data[i] >= '0' && value[1].data[i] <= '9') && (value[1].data[i] != '-')) i++; #ifdef readconf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-special score in checkrule:%s from (%d)", value[1].data, atoi((const char *)value[1].data+i)); #endif // get the score rule_c->sc_score = atoi((const char *)(value[1].data+i)); /* process the second word : Action rule */ if (ngx_strstr(value[2].data, "BLOCK")) rule_c->block = 1; else if (ngx_strstr(value[2].data,"ALLOW")) rule_c->allow = 1; else if (ngx_strstr(value[2].data, "LOG")) rule_c->log = 1; else { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } return (NGX_CONF_OK); } static char * ngx_http_naxsi_ud_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_dummy_loc_conf_t *alcf = conf, **bar; ngx_http_dummy_main_conf_t *main_cf; ngx_str_t *value; if (!alcf || !cf) return (NGX_CONF_ERROR); value = cf->args->elts; main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module); if (!alcf->pushed) { bar = ngx_array_push(main_cf->locations); if (!bar) return (NGX_CONF_ERROR); *bar = alcf; alcf->pushed = 1; } /* store denied URL for location */ if ( (!ngx_strcmp(value[0].data, TOP_DENIED_URL_N) || !ngx_strcmp(value[0].data, TOP_DENIED_URL_T)) && value[1].len) { alcf->denied_url = ngx_pcalloc(cf->pool, sizeof(ngx_str_t)); if (!alcf->denied_url) return (NGX_CONF_ERROR); alcf->denied_url->data = ngx_pcalloc(cf->pool, value[1].len+1); if (!alcf->denied_url->data) return (NGX_CONF_ERROR); memcpy(alcf->denied_url->data, value[1].data, value[1].len); alcf->denied_url->len = value[1].len; return (NGX_CONF_OK); } else return NGX_CONF_ERROR; } static char * ngx_http_naxsi_flags_loc_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_dummy_loc_conf_t *alcf = conf, **bar; ngx_http_dummy_main_conf_t *main_cf; ngx_str_t *value; if (!alcf || !cf) return (NGX_CONF_ERROR); value = cf->args->elts; main_cf = ngx_http_conf_get_module_main_conf(cf, ngx_http_naxsi_module); if (!alcf->pushed) { bar = ngx_array_push(main_cf->locations); if (!bar) return (NGX_CONF_ERROR); *bar = alcf; alcf->pushed = 1; } /* it's a flagrule, just a hack to enable/disable mod */ if (!ngx_strcmp(value[0].data, TOP_ENABLED_FLAG_T) || !ngx_strcmp(value[0].data, TOP_ENABLED_FLAG_N)) { alcf->enabled = 1; return (NGX_CONF_OK); } else /* it's a flagrule, just a hack to enable/disable mod */ if (!ngx_strcmp(value[0].data, TOP_DISABLED_FLAG_T) || !ngx_strcmp(value[0].data, TOP_DISABLED_FLAG_N)) { alcf->force_disabled = 1; return (NGX_CONF_OK); } else /* it's a flagrule, currently just a hack to enable/disable learning mode */ if (!ngx_strcmp(value[0].data, TOP_LEARNING_FLAG_T) || !ngx_strcmp(value[0].data, TOP_LEARNING_FLAG_N)) { alcf->learning = 1; return (NGX_CONF_OK); } else return (NGX_CONF_ERROR); } //#define main_conf_debug static char * ngx_http_dummy_read_main_conf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_dummy_main_conf_t *alcf = conf; ngx_str_t *value; ngx_http_rule_t rule, *rule_r; ngx_http_custom_rule_location_t *location; unsigned int i; if (!alcf || !cf) return (NGX_CONF_ERROR); /* alloc a new rule */ value = cf->args->elts; /* parse the line, fill rule struct */ #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "XX-TOP READ CONF %s", value[0].data); #endif if (ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_T) && ngx_strcmp(value[0].data, TOP_MAIN_BASIC_RULE_N)) { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } memset(&rule, 0, sizeof(ngx_http_rule_t)); if (ngx_http_dummy_cfg_parse_one_rule(cf/*, alcf*/, value, &rule, cf->args->nelts) != NGX_CONF_OK) { ngx_http_dummy_line_conf_error(cf, value); return (NGX_CONF_ERROR); } if (rule.br->headers) { #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in header rules", rule.rule_id); #endif if (alcf->header_rules == NULL) { alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->header_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->header_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in body match rules (POST/PUT) */ if (rule.br->body || rule.br->body_var) { #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in body rules", rule.rule_id); #endif if (alcf->body_rules == NULL) { alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->body_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->body_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in generic rules, as it's matching the URI */ if (rule.br->url) { #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in generic rules", rule.rule_id); #endif if (alcf->generic_rules == NULL) { alcf->generic_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->generic_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->generic_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in GET arg rules, but we should push in POST rules too */ if (rule.br->args_var || rule.br->args) { #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in GET rules", rule.rule_id); #endif if (alcf->get_rules == NULL) { alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->get_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->get_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } /* push in custom locations. It's a rule matching a VAR_NAME or an EXACT_URI : - GET_VAR, POST_VAR, URI */ if (rule.br->custom_location) { #ifdef main_conf_debug ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pushing rule %d in custom_location rules", rule.rule_id); #endif location = rule.br->custom_locations->elts; for (i = 0; i < rule.br->custom_locations->nelts; i++) { if (location[i].args_var) { if (alcf->get_rules == NULL) { alcf->get_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->get_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->get_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } if (location[i].body_var) { if (alcf->body_rules == NULL) { alcf->body_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->body_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->body_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } if (location[i].headers_var) { if (alcf->header_rules == NULL) { alcf->header_rules = ngx_array_create(cf->pool, 2, sizeof(ngx_http_rule_t)); if (alcf->header_rules == NULL) return NGX_CONF_ERROR; } rule_r = ngx_array_push(alcf->header_rules); if (!rule_r) return (NGX_CONF_ERROR); memcpy(rule_r, &rule, sizeof(ngx_http_rule_t)); } } } return (NGX_CONF_OK); } /* ** [ENTRY POINT] does : this is the function called by nginx : ** - Set up the context for the request ** - Check if the job is done and we're called again ** - if it's a POST/PUT request, setup hook for body dataz ** - call dummy_data_parse ** - check our context struct (with scores & stuff) against custom check rules ** - check if the request should be denied */ //#define mechanics_debug 1 //#define naxsi_modifiers_debug 1 static ngx_int_t ngx_http_dummy_access_handler(ngx_http_request_t *r) { ngx_http_request_ctx_t *ctx; ngx_int_t rc; ngx_http_dummy_loc_conf_t *cf; ngx_http_core_loc_conf_t *clcf; struct tms tmsstart, tmsend; clock_t start, end; ngx_http_variable_value_t *lookup; static ngx_str_t learning_flag = ngx_string(RT_LEARNING); static ngx_str_t enable_flag = ngx_string(RT_ENABLE); static ngx_str_t post_action_flag = ngx_string(RT_POST_ACTION); static ngx_str_t extensive_log_flag = ngx_string(RT_EXTENSIVE_LOG); ctx = ngx_http_get_module_ctx(r, ngx_http_naxsi_module); cf = ngx_http_get_module_loc_conf(r, ngx_http_naxsi_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, */ /* "naxsi_entry_point"); */ if (ctx && ctx->over) return (NGX_DECLINED); if (ctx && ctx->wait_for_body) { #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "naxsi:NGX_AGAIN"); #endif return (NGX_DONE); } if (!cf) return (NGX_ERROR); /* the module is not enabled here */ /* if enable directive is not present at all in the location, don't try to do dynamic lookup for "live" enabled naxsi, this would be very rude. */ if (!cf->enabled) return (NGX_DECLINED); /* On the other hand, if naxsi has been explicitly disabled in this location (using naxsi directive), user is probably trying to do something. */ if (cf->force_disabled) { /* Look if the user did not try to enable naxsi dynamically */ lookup = ngx_http_get_variable(r, &enable_flag, cf->flag_enable_h); if (lookup && !lookup->not_found && lookup->len > 0) { ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "live enable is present %d", lookup->data[0] - '0'); if (lookup->data[0] - '0' != 1) { return (NGX_DECLINED);} } else return (NGX_DECLINED); } /* don't process internal requests. */ if (r->internal) { #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-DON'T PROCESS (%V)|CTX:%p|ARGS:%V|METHOD=%s|INTERNAL:%d", &(r->uri), ctx, &(r->args), r->method == NGX_HTTP_POST ? "POST" : r->method == NGX_HTTP_PUT ? "PUT" : r->method == NGX_HTTP_GET ? "GET" : "UNKNOWN!!", r->internal); #endif return (NGX_DECLINED); } #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-processing (%V)|CTX:%p|ARGS:%V|METHOD=%s|INTERNAL:%d", &(r->uri), ctx, &(r->args), r->method == NGX_HTTP_POST ? "POST" : r->method == NGX_HTTP_PUT ? "PUT" : r->method == NGX_HTTP_GET ? "GET" : "UNKNOWN!!", r->internal); #endif if (!ctx) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_request_ctx_t)); /* might have been set by a previous trigger */ if (ctx->learning) { clcf->post_action.data = 0; //cf->denied_url->data; clcf->post_action.len = 0; //cf->denied_url->len; } if (ctx == NULL) return NGX_ERROR; ngx_http_set_ctx(r, ctx, ngx_http_naxsi_module); #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : orig learning : %d", cf->learning ? 1 : 0); #endif /* it seems that nginx will - in some cases - have a variable with empty content but with lookup->not_found set to 0, so check len as well */ ctx->learning = cf->learning; lookup = ngx_http_get_variable(r, &learning_flag, cf->flag_learning_h); if (lookup && !lookup->not_found && lookup->len > 0) { ctx->learning = lookup->data[0] - '0'; #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : override learning : %d (raw=%d)", ctx->learning ? 1 : 0, lookup->len); #endif } #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : [final] learning : %d", ctx->learning ? 1 : 0); #endif ctx->enabled = cf->enabled; #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : orig enabled : %d", ctx->enabled ? 1 : 0); #endif lookup = ngx_http_get_variable(r, &enable_flag, cf->flag_enable_h); if (lookup && !lookup->not_found && lookup->len > 0) { ctx->enabled = lookup->data[0] - '0'; #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : override enable : %d", ctx->enabled ? 1 : 0); #endif } #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : [final] enabled : %d", ctx->enabled ? 1 : 0); #endif if (cf->learning) ctx->post_action = 1; else ctx->post_action = 0; #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : orig post_action : %d", ctx->post_action ? 1 : 0); #endif lookup = ngx_http_get_variable(r, &post_action_flag, cf->flag_post_action_h); if (lookup && !lookup->not_found && lookup->len > 0) { ctx->post_action = lookup->data[0] - '0'; #ifdef naxsi_modifier_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : override post_action : %d", ctx->post_action ? 1 : 0); #endif } #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : [final] post_action : %d", ctx->post_action ? 1 : 0); #endif #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : orig extensive_log : %d", ctx->extensive_log ? 1 : 0); #endif lookup = ngx_http_get_variable(r, &extensive_log_flag, cf->flag_extensive_log_h); if (lookup && !lookup->not_found && lookup->len > 0) { ctx->extensive_log = lookup->data[0] - '0'; #ifdef naxsi_modifier_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : override extensive_log : %d", ctx->extensive_log ? 1 : 0); #endif } #ifdef naxsi_modifiers_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : [final] extensive_log : %d", ctx->extensive_log ? 1 : 0); #endif //--- /* the module is not enabled here */ if (!ctx->enabled) return (NGX_DECLINED); if ((r->method == NGX_HTTP_POST || r->method == NGX_HTTP_PUT) && !ctx->ready) { #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : body_request : before !"); #endif rc = ngx_http_read_client_request_body(r, ngx_http_dummy_payload_handler); /* this might happen quite often, especially with big files / ** low network speed. our handler is called when headers are read, ** but, often, the full body request hasn't yet, so ** read client request body will return ngx_again. Then we need ** to return ngx_done, wait for our handler to be called once ** body request arrived, and let him call core_run_phases ** to be able to process the request. */ if (rc == NGX_AGAIN) { ctx->wait_for_body = 1; #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : body_request : NGX_AGAIN !"); #endif return (NGX_DONE); } else if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { /* this debug print should be commented, but the thing is that ** according to what I read into nginx src code, it may happen ** and I haven't been abble to trigger this, so I just let it ** here to know when this special case will be triggered */ ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : SPECIAL RESPONSE !!!!"); return rc; } } else ctx->ready = 1; } if (ctx && ctx->ready && !ctx->over) { if ((start = times(&tmsstart)) == (clock_t)-1) ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : Failed to get time"); ngx_http_dummy_data_parse(ctx, r); cf->request_processed++; if ((end = times(&tmsend)) == (clock_t)-1) ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "XX-dummy : Failed to get time"); if (end - start > 0) ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[MORE THAN 1MS] times : start:%l end:%l diff:%l", start, end, (end-start)); ctx->over = 1; if (ctx->block) { cf->request_blocked++; rc = ngx_http_output_forbidden_page(ctx, r); //nothing: return (NGX_OK); //redirect : return (NGX_HTTP_OK); return (rc); } else if (ctx->log) rc = ngx_http_output_forbidden_page(ctx, r); } #ifdef mechanics_debug ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "NGX_FINISHED !"); #endif return (NGX_DECLINED); } debian/modules/naxsi/t/0000755000000000000000000000000012305451341012221 5ustar debian/modules/naxsi/t/11naxsi_newstyle_config.t0000644000000000000000000051053612305451333017164 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; repeat_each(3); plan tests => repeat_each(1) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TEST 1: Basic GET request --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; # return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 2: DENY : Obvious GET XSS --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a="> --- error_code: 412 === TEST 2.1: DENY : Obvious RFI --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 2" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=http://evil.com/eva.txt --- error_code: 412 === TEST 2.3: DENY : Obvious LFI --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 2" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=../../../../../bar.txt --- error_code: 412 === TEST 3: OBVIOUS GET SQL INJECTION --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1'+Or+'1'='1 --- error_code: 412 === TEST 3bis: OBVIOUS (quoteless) GET SQL INJECTION --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1+UnIoN+SeLeCt+1 --- error_code: 412 === TEST 4: VERY STRANGE GET REQUEST --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=[]();-- --- error_code: 412 === TEST 5: SIMPLE POST (www-form style) --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=bar1&foo2=bar2" --- error_code: 200 === TEST 7 : SQLi POST (www-form style) --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=' OR '1'='1" --- error_code: 412 === TEST 8 : XSS POST (www-form style) --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1='>" --- error_code: 412 === TEST 9: Adding a test rule in http_config (ARGS zone). --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === TEST 10: Adding a test rule in http_config (URL zone). --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 412 === TEST 11: Adding a test rule in http_config (BODY zone). --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=foobar" --- error_code: 412 === TEST 17: Negative RX rule on header:content-type. --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 17: Negative RX rule on header:content-type. --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 18: Negative RX rule on header:content-type (again). --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/OBSCURE_EVIL_CONTENT_TYPE --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 412 === TEST 19: Negative RX rule on header:content-type (again & last, I promise !). --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 19.2: Negative RX rule on header:content-type (I LIED !). --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: ApPlIcaTiOn/x-wWw-fORm-urlEnCoDed --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 22: CUSTOM SCORE RULES ! --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:1998; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 412 === TEST 23: CUSTOM SCORE RULES, bis --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:2999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:2998; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 412 === TEST 24: Testing MULTIPART POSTs -- INVALID FORMAT --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 412 === TEST 24: Testing MULTIPART POSTs --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 355 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 200 === TEST 25: Testing MULTIPART POSTs (NO CONTENT LEN) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 200 === TEST 26: Testing MULTIPART POSTs (BAD CONTENT LEN) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 42 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 26.1: Testing MULTIPART POSTs (BAD CONTENT LEN) #nginx changed his way, no data is cut to content lenght header, so this test is obsolete --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 42 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 27: Obvious POST XSS (multipart) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\naz\">\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 28: Obvious POST SQLi (multipart) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\naz\" OR \"1\"=\"1\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 29: Malformed POST / BoF try #1 (missing some boundaries) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nNaaaaaa\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 30 : Malformed POST / BoF try #3 (random overflow trigger n1) --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nazzzo\r\n\r\n\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n-----------------------------103832778631715--\r\n" --- error_code: 200 === TEST 31: enc0ding phun ? --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=ba%%2f%3c%3D%3%D%33%DD%FF%2F%3cr1&foo2=bar2" --- error_code: 412 === TEST 32: fucked up URLs #1 --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a&&z=yesone&& --- error_code: 412 === TEST 33: fucked up URLs #2 --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&yesone --- error_code: 412 === TEST 33: fucked up URLs #2bis --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&==yesone&&& --- error_code: 412 === TEST 33: fucked up URLs #2ter --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?==yesone&&& --- error_code: 412 === TEST 33: fucked up URLs #3 --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbuiyesone&& --- error_code: 412 === TEST 33: fucked up URLs #4 --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&%00yesone --- error_code: 412 === TEST 33: fucked up URLs #4 --- user_files >>> foobar eh yo --- http_config main_rule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; main_rule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; main_rule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; main_rule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; main_rule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; main_rule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; main_rule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; main_rule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; main_rule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; main_rule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; main_rule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; main_rule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; main_rule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; main_rule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; main_rule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; main_rule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; main_rule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; main_rule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; main_rule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; main_rule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; main_rule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; main_rule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; main_rule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; main_rule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; main_rule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; main_rule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; main_rule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; main_rule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; main_rule "str:~" "msg:~ character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; main_rule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; main_rule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; main_rule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; main_rule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; main_rule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; main_rule "rx:.ph|.asp" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1501; main_rule "str:phpmyadmin" "msg:phpmyadmin bot ?" "mz:URL" "s:LOG" id:1502; #MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; rules_enabled; denied_url "/RequestDenied"; check_rule "$SQL >= 8" BLOCK; check_rule "$RFI >= 8" BLOCK; check_rule "$TRAVERSAL >= 4" BLOCK; check_rule "$XSS >= 8" BLOCK; check_rule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&y%00esone --- error_code: 412 debian/modules/naxsi/t/12naxsi_argnames_extended.t0000644000000000000000000000305712305451333017436 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === WL TEST 1.0: Obvious test in arg --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 412 === WL TEST 1.1: Obvious test in arg --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 200 debian/modules/naxsi/t/00naxsi_base.t0000644000000000000000000010220112305451333014657 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; repeat_each(3); plan tests => repeat_each(1) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TEST 1: Basic GET request --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; # return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 2: DENY : Obvious GET XSS --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a="> --- error_code: 412 === TEST 2.1: DENY : Obvious RFI --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 2" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=http://evil.com/eva.txt --- error_code: 412 === TEST 2.3: DENY : Obvious LFI --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 2" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=../../../../../bar.txt --- error_code: 412 === TEST 3: OBVIOUS GET SQL INJECTION --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1'+Or+'1'='1 --- error_code: 412 === TEST 3bis: OBVIOUS (quoteless) GET SQL INJECTION --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1+UnIoN+SeLeCt+1 --- error_code: 412 === TEST 4: VERY STRANGE GET REQUEST --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=[]();-- --- error_code: 412 === TEST 5: SIMPLE POST (www-form style) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=bar1&foo2=bar2" --- error_code: 200 === TEST 7 : SQLi POST (www-form style) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=' OR '1'='1" --- error_code: 412 === TEST 8 : XSS POST (www-form style) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1='>" --- error_code: 412 === TEST 9: Adding a test rule in http_config (ARGS zone). --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === TEST 10: Adding a test rule in http_config (URL zone). --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 412 === TEST 11: Adding a test rule in http_config (BODY zone). --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:BODY" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=foobar" --- error_code: 412 === TEST 17: Negative RX rule on header:content-type. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 17: Negative RX rule on header:content-type. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 18: Negative RX rule on header:content-type (again). --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/OBSCURE_EVIL_CONTENT_TYPE --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 412 === TEST 19: Negative RX rule on header:content-type (again & last, I promise !). --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 19.2: Negative RX rule on header:content-type (I LIED !). --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:application/x-www-form-urlencoded|multipart/form-data" "msg:foobar test pattern" "mz:$HEADERS_VAR:Content-type" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: ApPlIcaTiOn/x-wWw-fORm-urlEnCoDed --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 200 === TEST 22: CUSTOM SCORE RULES ! --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:1998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 412 === TEST 23: CUSTOM SCORE RULES, bis --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:2999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:21" id:2998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 412 === TEST 24: Testing MULTIPART POSTs -- INVALID FORMAT --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data --- request eval use URI::Escape; "POST / a1=trolol" --- error_code: 412 === TEST 24: Testing MULTIPART POSTs --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 355 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n\r\n" --- error_code: 200 === TEST 25: Testing MULTIPART POSTs (NO CONTENT LEN) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n\r\n" --- error_code: 200 === TEST 26: Testing MULTIPART POSTs (BAD CONTENT LEN) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 42 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 26.1: Testing MULTIPART POSTs (BAD CONTENT LEN) #nginx changed his way, no data is cut to content lenght header, so this test is obsolete --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 Content-Length: 42 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nMyName\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 27: Obvious POST XSS (multipart) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\naz\">\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 28: Obvious POST SQLi (multipart) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\naz\" OR \"1\"=\"1\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 29: Malformed POST / BoF try #1 (missing some boundaries) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nNaaaaaa\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nyes\r\n-----------------------------103832778631715--\r\n" --- error_code: 412 === TEST 30 : Malformed POST / BoF try #3 (random overflow trigger n1) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE > 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: multipart/form-data; boundary=---------------------------103832778631715 --- request eval use URI::Escape; "POST /\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nazzzo\r\n\r\n\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\nnot single\r\n-----------------------------103832778631715\r\nContent-Disposition: form-data; name=\"male\"\r\n\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n-----------------------------103832778631715--\r\n" --- error_code: 200 === TEST 31: enc0ding phun ? --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST / foo1=ba%%2f%3c%3D%3%D%33%DD%FF%2F%3cr1&foo2=bar2" --- error_code: 412 === TEST 32: fucked up URLs #1 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a&&z=yesone&& --- error_code: 412 === TEST 33: fucked up URLs #2 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&yesone --- error_code: 412 === TEST 33: fucked up URLs #2bis --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&==yesone&&& --- error_code: 412 === TEST 33: fucked up URLs #2ter --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?==yesone&&& --- error_code: 412 === TEST 33: fucked up URLs #3 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbuiyesone&& --- error_code: 412 === TEST 33: fucked up URLs #4 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&%00yesone --- error_code: 412 === TEST 33: fucked up URLs #4 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; #MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$TESTSCORE:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$TESTSCORE >= 42" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?z=&y%00esone --- error_code: 412 debian/modules/naxsi/t/05naxsi_advanced_whitelists.t0000644000000000000000000002154012305451333020004 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === WL TEST 5.0: Two whitelists on two named arguments, same URL --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /buixor?bla=1999 --- error_code: 200 === WL TEST 5.1: Two whitelists on two named arguments, same URL --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /buixor?blu=1999 --- error_code: 412 === WL TEST 5.2: Two whitelists on two named arguments, same URL --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /buixor?bla=1999&blu=1998 --- error_code: 200 === WL TEST 5.3: Two whitelists on two named arguments, same URL --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:bla|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /?buixor=1998 --- error_code: 412 === WL TEST 5.4: Whitelists on ARGS/URLs that are URLencoded --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:b_@_la|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /buixor?b_@_la=1999 --- error_code: 200 === WL TEST 5.5: Whitelists on ARGS/URLs that are URLencoded --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:b[]la|$URL:/buixor"; BasicRule wl:1998 "mz:$ARGS_VAR:blu|$URL:/buixor"; } location /RequestDenied { return 412; } --- request GET /buixor?b]la=1999 --- error_code: 412 === WL TEST 6: Whitelists trying to provoke collisions --- user_files >>> buixor eh yo >>> bla eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; # BasicRule wl:1999 "mz:$ARGS_VAR:/bla"; BasicRule wl:1998 "mz:$URL:/bla|ARGS"; } location /RequestDenied { return 412; } --- request GET /bla?1998 --- error_code: 200 === WL TEST 6.0: Whitelists trying to provoke collisions --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; # BasicRule wl:1999 "mz:$ARGS_VAR:/bla"; BasicRule wl:1998 "mz:$URL:/bla|ARGS"; } location /RequestDenied { return 412; } --- request GET /?/bla=1998 --- error_code: 412 === WL TEST 6.1: Whitelists trying to provoke collisions --- user_files >>> buixor eh yo >>> bla eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:/bla"; BasicRule wl:1998 "mz:$URL:/bla|ARGS"; } location /RequestDenied { return 412; } --- request GET /bla?bla=1999&toto=1998 --- error_code: 200 === WL TEST 6.2: Whitelists trying to provoke collisions --- user_files >>> buixor eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:/bla"; BasicRule wl:1998 "mz:$URL:/bla|ARGS"; } location /RequestDenied { return 412; } --- request GET /buixor?/bla=1999 --- error_code: 200 === WL TEST 6.3: Whitelists trying to provoke collisions --- user_files >>> buixor eh yo >>> bla eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:1998" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1998; MainRule "str:1999" "msg:foobar test pattern #2" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:/bla"; BasicRule wl:1998 "mz:$URL:/bla|ARGS"; } location /RequestDenied { return 412; } --- request GET /bla?/bla=1999&bu=1998 --- error_code: 200 debian/modules/naxsi/t/08negative_whitelists.t0000644000000000000000000002352512305451333016647 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); # === WL TEST 1.01 # --- http_config # include /etc/nginx/naxsi_core.rules; # MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; # --- config # location / { # #LearningMode; # SecRulesEnabled; # DeniedUrl "/RequestDenied"; # CheckRule "$SQL >= 8" BLOCK; # CheckRule "$RFI >= 8" BLOCK; # CheckRule "$TRAVERSAL >= 4" BLOCK; # CheckRule "$XSS >= 8" BLOCK; # root $TEST_NGINX_SERVROOT/html/; # index index.html index.htm; # BasicRule wl:1999; # } # location /RequestDenied { # return 412; # } # --- request # GET /?b=foobar # --- error_code: 412 # === WL TEST 2.0 # --- http_config # include /etc/nginx/naxsi_core.rules; # MainRule negative "rx:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; # --- config # location / { # #LearningMode; # SecRulesEnabled; # DeniedUrl "/RequestDenied"; # CheckRule "$SQL >= 8" BLOCK; # CheckRule "$RFI >= 8" BLOCK; # CheckRule "$TRAVERSAL >= 4" BLOCK; # CheckRule "$XSS >= 8" BLOCK; # root $TEST_NGINX_SERVROOT/html/; # index index.html index.htm; # BasicRule wl:1999; # } # location /RequestDenied { # return 412; # } # --- request # GET /?b=foobar # --- error_code: 200 # === WL TEST 2.01 # --- http_config # include /etc/nginx/naxsi_core.rules; # MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; # --- config # location / { # #LearningMode; # SecRulesEnabled; # DeniedUrl "/RequestDenied"; # CheckRule "$SQL >= 8" BLOCK; # CheckRule "$RFI >= 8" BLOCK; # CheckRule "$TRAVERSAL >= 4" BLOCK; # CheckRule "$XSS >= 8" BLOCK; # root $TEST_NGINX_SERVROOT/html/; # index index.html index.htm; # BasicRule wl:1999; # } # location /RequestDenied { # return 412; # } # --- request # GET /?b=foobarr # --- error_code: 412 # === WL TEST 2.02 # --- http_config # include /etc/nginx/naxsi_core.rules; # MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; # --- config # location / { # #LearningMode; # SecRulesEnabled; # DeniedUrl "/RequestDenied"; # CheckRule "$SQL >= 8" BLOCK; # CheckRule "$RFI >= 8" BLOCK; # CheckRule "$TRAVERSAL >= 4" BLOCK; # CheckRule "$XSS >= 8" BLOCK; # root $TEST_NGINX_SERVROOT/html/; # index index.html index.htm; # BasicRule wl:1999; # } # location /RequestDenied { # return 412; # } # --- request # GET /?b=ffoobar # --- error_code: 412 __DATA__ === WL TEST 1.0 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?b=toto --- error_code: 412 === WL TEST 1.01 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?b=foobar --- error_code: 200 === WL TEST 1.03 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$URL:/|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobar --- error_code: 404 === WL TEST 1.04 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "str:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobrar --- error_code: 412 === WL TEST 2.0 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobrar --- error_code: 412 === WL TEST 2.01 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobar --- error_code: 404 === WL TEST 2.02 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?b=foobar --- error_code: 200 === WL TEST 2.03 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=rfoobar --- error_code: 412 === WL TEST 2.04 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^foobar" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobar --- error_code: 404 === WL TEST 2.05 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobar --- error_code: 404 === WL TEST 2.06 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^foobar$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobara --- error_code: 412 === WL TEST 2.07 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^[0-9]+$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=foobara --- error_code: 412 === WL TEST 2.08 --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:^[0-9]+$" "msg:foobar test pattern" "mz:$URL:/a|$ARGS_VAR:b" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /a?b=1234 --- error_code: 404 debian/modules/naxsi/t/10naxsi_modifiers.t0000644000000000000000000002035612305451333015741 0ustar #vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; repeat_each(3); plan tests => repeat_each(1) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TEST 1.0 : Runtime Learning force (per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.1") { set $naxsi_flag_learning 1; } location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 1.01 : Runtime Learning force (absolute) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_learning 1; location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 1.1: Runtime Learning force (fail - per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.42") { set $naxsi_flag_learning 1; } location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 412 === TEST 1.2: Runtime Learning force (fail - in location) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { # this will not work, as naxsi # is processed before var set in location. set $naxsi_flag_learning 1; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 412 === TEST 1.3: Runtime Learning disable (per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.1") { set $naxsi_flag_learning 0; } location / { SecRulesEnabled; LearningMode; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 412 === TEST 1.4: Runtime Learning disable (fail - per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.42") { set $naxsi_flag_learning 0; } location / { SecRulesEnabled; LearningMode; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 2.00 : Check that SecRulesDisabled correctly works --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 2: Runtime disable force (absolute) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_enable 0; location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 2.2: Runtime enable force --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_enable 1; location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 412 === TEST 2.3: Runtime enable force, with static learning (which is pointless) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_enable 1; location / { LearningMode; SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 2.4: Runtime enable + learning mode (absolute) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_learning 1; set $naxsi_flag_enable 1; location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 3.0: Runtime enable + learning mode (per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.1") { set $naxsi_flag_enable 1; set $naxsi_flag_learning 1; } location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 3.1: Runtime enable + learning mode (per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config if ($remote_addr = "127.0.0.42") { set $naxsi_flag_enable 1; set $naxsi_flag_learning 1; } location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 3.2: Runtime enable + learning mode (per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_enable 1; if ($remote_addr = "127.0.0.1") { set $naxsi_flag_learning 1; } location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 200 === TEST 3.3: Runtime enable (success) + learning mode (fail - per ip) --- http_config include /etc/nginx/naxsi_core.rules; --- config set $naxsi_flag_enable 1; if ($remote_addr = "127.0.0.42") { set $naxsi_flag_learning 1; } location / { SecRulesEnabled; SecRulesDisabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=<> --- error_code: 412 debian/modules/naxsi/t/03naxsi_profile.t0000644000000000000000000000617112305451333015421 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TEST 1: Basic GET request --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 2: DENY : Obvious GET XSS --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a="> --- error_code: 412 === TEST 2.1: DENY : Obvious RFI --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 2" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=http://evil.com/eva.txt --- error_code: 412 === TEST 2.3: DENY : Obvious LFI --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 2" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=../../../../../bar.txt --- error_code: 412 === TEST 3: OBVIOUS GET SQL INJECTION --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1'+Or+'1'='1 --- error_code: 412 === TEST 3bis: OBVIOUS (quoteless) GET SQL INJECTION --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=1+UnIoN+SeLeCt+1 --- error_code: 412 debian/modules/naxsi/t/09sqlmap_tamper.t0000644000000000000000000004175512305451333015441 0ustar use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TODO: naxsi does not support utf8, potential bypass. Still too marginal to be worth checking --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=AND+%EF%BC%871%EF%BC%87=%EF%BC%871%EF%BC%87 HTTP/1.0 " --- error_code: 200 === TEST 1: hey 2 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=AND+%00%271%00%27=%00%271%00%27 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 3 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=AND+1=1%00 Union select 1 HTTP/1.0 " --- error_code: 412 === NOT TODO: base64, not worthing checking --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=MScgQU5EIFNMRUVQKDUpIw== HTTP/1.0 " --- error_code: 200 === TEST 1: hey 5 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a='A+NOT+BETWEEN+0+AND+B' HTTP/1.0 " --- error_code: 412 === TEST 1: hey 6 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=%2553%2545%254c%2545%2543%2554%2520%2546%2549%2545%254c%2544%2520%2546%2552%254f%254d%2520%2554%2541%2542%254c%2545 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 7 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=%53%45%4c%45%43%54%20%46%49%45%4c%44%20%46%52%4f%4d%20%54%41%42%4c%45 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 8 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=%u0053%u0045%u004c%u0045%u0043%u0054%u0020%u0046%u0049%u0045%u004c%u0044%u0020%u0046%u0052%u004f%u004d%u0020%u0054%u0041%u0042%u004c%u0045' HTTP/1.0 " --- error_code: 412 === TEST 1: hey 9 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=SELECT+*+FROM+users+WHERE+id+LIKE+1 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 10 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=value'/*!0UNION/*!0ALL/*!0SELECT/*!0CONCAT(/*!0CHAR(58,107,112,113,58),/*!0IFNULL(CAST(/*!0CURRENT_USER()/*!0AS/*!0CHAR),/*!0CHAR(32)),/*!0CHAR(58,97,110,121,58)),+NULL,+NULL#/*!0AND+'QDWa'='QDWa HTTP/1.0 " --- error_code: 412 === TEST 1: hey 11 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=IF(ISNULL(1),+2,+1) HTTP/1.0 " --- error_code: 412 === TEST 1: hey 12 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1+/*!30000AND+2>1*/-- HTTP/1.0 " --- error_code: 412 === TEST 1: hey 13 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1+/*!00000AND+2>1*/-- HTTP/1.0 " --- error_code: 412 === TEST 1: hey 14 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=+UNION+++SELECT++ HTTP/1.0 " --- error_code: 412 === NOT TODO: I don't know any server/interpreter decoding this ? --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=%S%E%L%E%C%T+%F%I%E%L%D+%F%R%O%M+%T%A%B%L%E HTTP/1.0 " --- error_code: 200 === TEST 1: hey 16 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1 UnioN SeLEct 1 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 17 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=AND+1=1+and+'0having'='0having' HTTP/1.0 " --- error_code: 412 === TEST 1: hey 18 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=SELECT/**/id/**/FROM/**/users HTTP/1.0 " --- error_code: 412 === TEST 1: hey 19 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1--PTTmJopxdWJ%0AAND--cWfcVRPV%0A9227=9227 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 20 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1%23PTTmJopxdWJ%0AAND%23cWfcVRPV%0A9227=9227 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 21 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1%23PTTmJopxdWJ%0AAND%23cWfcVRPV%0A9227=9227 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 22 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=SELECT%08id%02FROM%0Fusers HTTP/1.0 " --- error_code: 412 === TEST 1: hey 23 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1%23%0A9227=922%237 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 24 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=SELECT%0Bid%0BFROM%A0users HTTP/1.0 " --- error_code: 412 === TEST 1: hey 25 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1--%0AAND--%0A9227=9227 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 26 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=SELECT+id+FROM+users HTTP/1.0 " --- error_code: 412 === TEST 1: hey 28 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1%bf%27+AND+1=1--%20 HTTP/1.0 " --- error_code: 412 === TEST 1: hey 29 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1/*!UNION*//*!ALL*//*!SELECT*//*!NULL*/,/*!NULL*/,+CONCAT(CHAR(58,104,116,116,58),IFNULL(CAST(CURRENT_USER()/*!AS*//*!CHAR*/),CHAR(32)),CHAR(58,100,114,117,58))# HTTP/1.0 " --- error_code: 412 === TEST 1: hey 30 --- main_config working_directory /tmp/; worker_rlimit_core 25M; --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- raw_request eval "GET /?a=1/*!UNION*//*!ALL*//*!SELECT*//*!NULL*/,/*!NULL*/,/*!CONCAT*/(/*!CHAR*/(58,122,114,115,58),/*!IFNULL*/(CAST(/*!CURRENT_USER*/()/*!AS*//*!CHAR*/),/*!CHAR*/(32)),/*!CHAR*/(58,115,114,121,58))# HTTP/1.0 " --- error_code: 412 debian/modules/naxsi/t/02naxsi_bypass.t0000644000000000000000000000333512305451333015260 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === TEST 1: Basic GET request --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=buibui --- error_code: 200 === TEST 2: DENY : XSS bypass vector 1 (basic url encode) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=%2f%3cSc%3E --- error_code: 412 === TEST 2.1: DENY : XSS bypass vector 2 (\x encode) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 2" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=\x2f\x3cSc\x3E --- error_code: 412 debian/modules/naxsi/t/06naxsi_weirds.t0000644000000000000000000000526512305451333015264 0ustar #vi:filetype=perl use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === WL TEST 1.0: weird request in URL --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?&&&&a&&&&& --- error_code: 412 === WL TEST 1.01: weird request in URL (wl on fullzone) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:12 "mz:ARGS"; } location /RequestDenied { return 412; } --- request GET /?&&&&a&&&&& --- error_code: 200 === WL TEST 1.02: weird request in URL (wl on zone+URL) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:12 "mz:$URL:/|ARGS"; } location /RequestDenied { return 412; } --- request GET /?&&&&a&&&&& --- error_code: 200 === WL TEST 1.03: weird request in URL (fail wl on zone+bad URL) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:12 "mz:$URL:/a|ARGS"; } location /RequestDenied { return 412; } --- request GET /?&&&&a&&&&& --- error_code: 412 === WL TEST 1.04: weird request in URL (fail wl on bad zone+URL) --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:12 "mz:$URL:/|URL"; } location /RequestDenied { return 412; } --- request GET /?&&&&a&&&&& --- error_code: 412 debian/modules/naxsi/t/07naxsi_argnames.t0000644000000000000000000003140712305451333015562 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === WL TEST 1.0: Obvious test in arg --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 412 === WL TEST 1.01: Check non-collision of zone and 'name' flag --- http_config include /etc/nginx/naxsi_core.rules; MainRule id:5 "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42"; --- config location / { SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === WL TEST 1.1: Generic whitelist in ARGS_NAME --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 200 === WL TEST 1.11: Generic whitelist in ARGS_NAME, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:ARGS"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 412 === WL TEST 1.12: Generic whitelist in ARGS_NAME, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === WL TEST 1.2: whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 200 === WL TEST 1.21: whitelist in ARGS_NAME+$URL, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 200 === WL TEST 1.22: whitelist in ARGS_NAME+$URL, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === WL TEST 1.3: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/z|ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 412 === WL TEST 1.31: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS|NAME"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === WL TEST 1.32: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:b|NAME"; } location /RequestDenied { return 412; } --- request GET /?b=foobar --- error_code: 412 === WL TEST 1.33: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=bui --- error_code: 200 === WL TEST 1.34: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar"; } location /RequestDenied { return 412; } --- request GET /?foobar=foobra --- error_code: 200 === WL TEST 1.35: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar"; } location /RequestDenied { return 412; } --- request GET /?foobar=foobar --- error_code: 412 === WL TEST 1.36: failed whitelist in ARGS_NAME+$URL --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; MainRule "str:foobra" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:2999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; BasicRule wl:2999 "mz:$URL:/|$ARGS_VAR:foobar"; } location /RequestDenied { return 412; } --- request GET /?foobar=foobar --- error_code: 412 === WL TEST 1.4: whitelist in ARGS_NAME+$URL+$ARGS_VAR --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=a --- error_code: 200 === WL TEST 1.41: whitelist in ARGS_NAME+$URL+$ARGS_VAR --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 412 === WL TEST 1.5: whitelist in ARGS_NAME+$URL+$ARGS_VAR, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=foobar --- error_code: 412 === WL TEST 1.51: whitelist in ARGS_NAME+$URL+$ARGS_VAR, limit --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; } location /RequestDenied { return 412; } --- request GET /?foobar=foo --- error_code: 200 === WL TEST 1.6: whitelist in ARGS_NAME+$URL+$ARGS_VAR, (collision) --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar|NAME"; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:foobar"; } location /RequestDenied { return 412; } --- request GET /?foobar=foobar --- error_code: 200 debian/modules/naxsi/t/04naxsi_files.t0000644000000000000000000014510512305451333015065 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; #repeat_each(3); plan tests => repeat_each(1) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); #=== TEST 1: multipart, legit file #=== TEST 2: multipart, forbidden file (.php) #=== TEST 3: multipart, forbidden file (.pht) #=== TEST 4: multipart, legit file, forbidden var #=== TEST 5: multipart, forbidden file, legit var #=== TEST 6: multipart, forbidden file, forbidden var #=== TEST 7: multipart, forbidden file, legit var __DATA__ === TEST 0: multipart, legit file --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /foobar HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/\r Content-Type: multipart/form-data; boundary=---------------------------1919886344942015258287623957\r Content-Length: 378\r \r -----------------------------1919886344942015258287623957\r Content-Disposition: form-data; name=\"textline\"\r \r valid text and small file\r -----------------------------1919886344942015258287623957\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla.txt\"\r Content-Type: text/plain\r \r buibuibubi buibuibuib \r -----------------------------1919886344942015258287623957--\r " --- error_code: 200 === TEST 1: multipart, bad file --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.phx\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 412 === TEST 2: multipart, bad file --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------50033619112485582381293259069\r Content-Length: 3253\r \r -----------------------------50033619112485582381293259069\r Content-Disposition: form-data; name=\"textline\"\r \r valid text medium file\r -----------------------------50033619112485582381293259069\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txt\"\r Content-Type: text/plain\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------50033619112485582381293259069--\r " --- error_code: 200 === TEST 3: multipart, bad file \" injection --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------210801732320936925511223486236\r Content-Length: 3272\r \r -----------------------------210801732320936925511223486236\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------210801732320936925511223486236\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_m\\\"ed.jpg.php\"\r Content-Type: application/x-httpd-php\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------210801732320936925511223486236--\r " --- error_code: 412 === TEST 4: multipart, bad file %00 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------210801732320936925511223486236\r Content-Length: 3272\r \r -----------------------------210801732320936925511223486236\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------210801732320936925511223486236\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_m%00ed.jpg.php\"\r Content-Type: application/x-httpd-php\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------210801732320936925511223486236--\r " --- error_code: 412 === TEST 5: multipart, bad file on rx --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:42" "s:BLOCK" "rx:.tx*"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 412 === TEST 6: multipart, bad file on rx + whitelist on uri --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/index.html|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 200 === TEST 7: multipart, bad file on rx + whitelist on uri + arg_name --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/index.html|$BODY_VAR:datafile|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 200 === TEST 6: multipart, bad file on rx + whitelist on uri --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/index.html|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 200 === TEST 7: multipart, bad file on rx + bad whitelist on uri --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 412 === TEST 8: multipart, bad file on rx + whitelist on uri + arg_name --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/index.html|$BODY_VAR:datafile|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 200 === TEST 9: multipart, bad file on rx + whitelist on uri + bad arg_name --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; CheckRule "$UPLOAD >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "mz:FILE_EXT" "id:1337" "s:BLOCK" "rx:.tx"; BasicRule wl:1337 "mz:$URL:/index.html|$BODY_VAR:datafil|FILE_EXT"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- raw_request eval "POST /index.html HTTP/1.1\r Host: 127.0.0.1\r Connection: Close\r User-Agent: Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10\r Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r Accept-Language: en-us,en;q=0.5\r Accept-Encoding: gzip, deflate\r Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r Referer: http://127.0.0.1/index.html\r Content-Type: multipart/form-data; boundary=---------------------------85477017311078916741744433009\r Content-Length: 3264\r \r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"textline\"\r \r valid text bad file\r -----------------------------85477017311078916741744433009\r Content-Disposition: form-data; name=\"datafile\"; filename=\"bla_med.txa\"\r Content-Type: application/octet-stream\r \r 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000\r -----------------------------85477017311078916741744433009--\r " --- error_code: 412 debian/modules/naxsi/t/01naxsi_whitelists.t0000644000000000000000000006514612305451333016165 0ustar #vi:filetype=perl # A AJOUTER : # TEST CASE AVEC UNE REGLE SUR UN HEADER GENERIQUE # La même sur des arguments :) use lib 'lib'; use Test::Nginx::Socket; plan tests => repeat_each(2) * blocks(); no_root_location(); no_long_string(); $ENV{TEST_NGINX_SERVROOT} = server_root(); run_tests(); __DATA__ === WL TEST 1.0: [ARGS zone WhiteList] Adding a test rule in http_config (ARGS zone) and disable rule. --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 200 === WL TEST 1.1: Adding a test rule in http_config (ARGS zone) and WL it on arg name only. --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:a"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 200 === WL TEST 1.2: Adding a test rule in http_config (ARGS zone) and WL it on arg name only (case sensitiveness check). --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:AbCd"; } location /RequestDenied { return 412; } --- request GET /?abcd=foobar --- error_code: 200 === WL TEST 1.3: Adding a test rule in http_config (ARGS zone) and WL it on arg name only (case sensitiveness check #2). --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:abcd"; } location /RequestDenied { return 412; } --- request GET /?AbCd=foobar --- error_code: 200 === WL TEST 1.4: Adding a test rule in http_config (ARGS zone) and WL it on $URL + ZONE. --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS"; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 200 === WL TEST 1.5: Adding a test rule in http_config (ARGS zone) and WL it on $URL + ZONE (wrong URL). --- user_files >>> index2 eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|ARGS"; } location /RequestDenied { return 412; } --- request GET /index2?a=foobar --- error_code: 412 === WL TEST 1.6: Adding a test rule in http_config (ARGS zone) and WL it on $URL + $ARG_VAR. --- user_files >>> index2 eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/|$ARGS_VAR:AbCd"; } location /RequestDenied { return 412; } --- request GET /index2?ABCD=foobar --- error_code: 412 === WL TEST 2.0: Adding a rule that will match on headers --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- more_headers Cookie: foobar --- request GET / --- error_code: 412 === WL TEST 2.1: Adding a rule that will match on headers, WL it on $HEADERS_VAR --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$HEADERS_VAR:cookie"; } location /RequestDenied { return 412; } --- more_headers Cookie: foobar --- request GET /another-page --- error_code: 200 === WL TEST 2.2: Adding a rule that will match on headers specific header name --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:$HEADERS_VAR:cookie" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- more_headers COOKIE: foobar --- request GET /another-page --- error_code: 412 === WL TEST 2.3: Adding a rule that will match on headers, WL it by $URL + zone --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule "wl:1999" "mz:$URL:/another-page|HEADERS"; } location /RequestDenied { return 412; } --- more_headers COOKIE: foobar --- request GET /another-page --- error_code: 200 === WL TEST 2.4 : Adding a rule that will match on headers, WL it by $URL + $HEADERS_VAR --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/another-page|$HEADERS_VAR:cookie"; } location /RequestDenied { return 412; } --- more_headers COOKIE: foobar --- request GET /another-page --- error_code: 200 === WL TEST 2.5 : Adding a rule that will match on headers, WL it by $URL + $HEADERS_VAR (WRONG URL) --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/another-page|$HEADERS_VAR:cookie"; } location /RequestDenied { return 412; } --- more_headers COOKIE: foobar --- request GET /another-pag --- error_code: 412 === WL TEST 2.6 : Adding a rule that will match on headers, WL it by $URL + $HEADERS_VAR (WRONG HEADER NAME) --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:HEADERS" "s:$SQL:42" id:1999; --- user_files >>> another-page ANOTHER CONTENT --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/another-page|$HEADERS_VAR:cookie"; } location /RequestDenied { return 412; } --- more_headers COOKI: foobar --- request GET /another-page --- error_code: 412 === URL WL TEST 3.0: Adding a test rule on ARGS (testing case sensitivness) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:bra" "msg:test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /foobar?a=BrA --- error_code: 412 === URL WL TEST 3.1: Adding a test rule on ARGS (testing case sensitivness #2) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:BrA" "msg:test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /foobar?a=bRa --- error_code: 412 === URL WL TEST 3.2: Adding a test rule on URI (testing case sensitivness #2) --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:BrA" "msg:test pattern" "mz:$URL:/foobar|ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /FoObar?a=bRa --- error_code: 412 === WL TEST 5.0: Testing the POST content-type rule ! --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:Content-typz" "s:BLOCK" id:1402; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Typz: application/x-www-form-urlencoded Content-Type: application/x-www-form-urlencoded --- request eval use URI::Escape; "POST /foobar foo1=bar1&foo2=bar2" --- error_code: 200 === WL TEST 5.1: Testing the POST content-type rule #2 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:content-typz" "s:BLOCK" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded Content-Typz: application/z-www-form-urlencoded --- request eval use URI::Escape; "POST /foobar foo1=bar1&foo2=bar2" --- error_code: 412 === WL TEST 5.1: Testing the POST content-type rule #3 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither mulipart/x-www-form.." "mz:$HEADERS_VAR:content-typz" "s:BLOCK" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- more_headers Content-Type: application/x-www-form-urlencoded cOnTeNT-TYpZ: application/x-www-form-evilencoded --- request eval use URI::Escape; "POST /foobar foo1=bar1&foo2=bar2" --- error_code: 412 === WL TEST 5: Adding a test rule in http_config (ARGS zone) and WL it on url + wrong arg name. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobar?baron=foobar --- error_code: 412 === WL TEST 6: Adding a test rule in http_config (ARGS zone) and WL it. --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999; } location /RequestDenied { return 412; } --- request GET /?a=foobar --- error_code: 200 === WL TEST 7: Adding a test rule in http_config (URL zone) and WL it on url + zone. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|URL"; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 200 === WL TEST 8: Adding a test rule in http_config (URL zone). --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 412 === WL TEST 8.1 : Adding a test rule in http_config (URL zone) and whitelist it with $URL:|URL. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|URL"; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 200 === WL TEST 8.2 : Adding a test rule in http_config (URL zone) and whitelist it with URL and no $URL. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:URL" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:URL"; } location /RequestDenied { return 412; } --- request GET /foobar?aa --- error_code: 200 === WL TEST 8: Adding a test rule in http_config (ARGS zone) and WL it on url + arg name. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobar?barone=foobar --- error_code: 200 === WL TEST 9: Adding a test rule in http_config (ARGS zone) and WL it on $ARGS_VAR only. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobar?barone=foobar --- error_code: 200 === WL TEST 10: Adding a test rule in http_config (ARGS zone) and WL it on url + wrong arg name. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobar?baron=foobar --- error_code: 412 === WL TEST 11: Adding a test rule in http_config (ARGS zone) and WL it on url + wrong URL. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobarx?baron=foobar --- error_code: 412 === WL TEST 12: Adding a test rule in http_config (ARGS zone) and WL it on url + wrong arg name. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:foobar" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999 "mz:$URL:/foobar|$ARGS_VAR:barone"; } location /RequestDenied { return 412; } --- request GET /foobar?baron=foobar --- error_code: 412 === WL TEST 13: Whitelisting multiple rules in one WL. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 412 === WL TEST 14 : Whitelist on ARG_NAME. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; BasicRule wl:1999 "mz:$ARGS_VAR:b"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?b=yestwo --- error_code: 200 === WL TEST 14.1 : Whitelist on ARG_NAME. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:BLOCK" id:1999; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1002 "mz:ARGS"; } location /RequestDenied { return 412; } --- request GET /?b=yesone --- error_code: 412 === WL TEST 15 : Whitelisting multiple rules in one WL. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:1999,1998; } location /RequestDenied { return 412; } --- request GET /?a=yesone&b=yestwo --- error_code: 200 === WL TEST 16 : Whitelisting all rules on one arg (wl:0). --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; BasicRule wl:0 "mz:$ARGS_VAR:a"; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesoneyestwo --- error_code: 200 === WL TEST 17 : Whitelisting all rules on one arg (wl:0) NOT. --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1999; MainRule "str:yestwo" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:4" id:1998; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; } location /RequestDenied { return 412; } --- request GET /?a=yesoneyestwo --- error_code: 412 === WL TEST 18 : Whitelisting rule id 1 --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; error_page 405 = $uri; } location /RequestDenied { return 412; } --- request POST / --- error_code: 412 === WL TEST 18.1 : Whitelisting internal rule --- user_files >>> foobar eh yo --- http_config include /etc/nginx/naxsi_core.rules; --- config location / { #LearningMode; SecRulesEnabled; DeniedUrl "/RequestDenied"; CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; root $TEST_NGINX_SERVROOT/html/; index index.html index.htm; BasicRule wl:11 "mz:$URL:/|BODY"; error_page 405 = $uri; } location /RequestDenied { return 412; } --- request POST / --- error_code: 200 debian/modules/naxsi/COPYING0000644000000000000000000000257512305451332013022 0ustar NAXSI, a web application firewall for NGINX Copyright (C) 2011, Thibault 'bui' Koechlin This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . debian/modules/nginx-dav-ext-module/0000755000000000000000000000000012305451341014610 5ustar debian/modules/nginx-dav-ext-module/config0000644000000000000000000000032212305451337016002 0ustar ngx_addon_name="ngx_http_dav_ext_module" HTTP_MODULES="$HTTP_MODULES \ ngx_http_dav_ext_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_http_dav_ext_module.c" CORE_LIBS="$CORE_LIBS -lexpat" debian/modules/nginx-dav-ext-module/README0000644000000000000000000000103112305451337015470 0ustar == nginx-dav-ext-module == NGINX WebDAV missing commands support (PROPFIND & OPTIONS) (c) 2012 Arutyunyan Roman (arut@qip.ru) For full WebDAV support in NGINX you need to turn on standard NGINX WebDAV module (providing partial WebDAV implementation) as well as this module for missing methods ./configure --with-http_dav_module --add-module= Requirements: libexpat-dev Example config: location / { dav_methods PUT DELETE MKCOL COPY MOVE; dav_ext_methods PROPFIND OPTIONS; root /var/root/; } debian/modules/nginx-dav-ext-module/ngx_http_dav_ext_module.c0000644000000000000000000004570012305451337021701 0ustar /****************************************************************************** Copyright (c) 2012, Roman Arutyunyan (arut@qip.ru) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /* NGINX missing WebDAV commands support *PROPFIND & OPTIONS* */ #include #include #include #include #include #include #include #define NGX_HTTP_DAV_EXT_OFF 2 typedef struct { ngx_uint_t methods; } ngx_http_dav_ext_loc_conf_t; static ngx_int_t ngx_http_dav_ext_init(ngx_conf_t *cf); static void * ngx_http_dav_ext_create_loc_conf(ngx_conf_t *cf); static char * ngx_http_dav_ext_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_conf_bitmask_t ngx_http_dav_ext_methods_mask[] = { { ngx_string("off"), NGX_HTTP_DAV_EXT_OFF }, { ngx_string("propfind"), NGX_HTTP_PROPFIND }, { ngx_string("options"), NGX_HTTP_OPTIONS }, { ngx_null_string, 0 } }; static ngx_command_t ngx_http_dav_ext_commands[] = { { ngx_string("dav_ext_methods"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_dav_ext_loc_conf_t, methods), &ngx_http_dav_ext_methods_mask }, ngx_null_command }; static ngx_http_module_t ngx_http_dav_ext_module_ctx = { NULL, /* preconfiguration */ ngx_http_dav_ext_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_dav_ext_create_loc_conf, /* create location configuration */ ngx_http_dav_ext_merge_loc_conf, /* merge location configuration */ }; ngx_module_t ngx_http_dav_ext_module = { NGX_MODULE_V1, &ngx_http_dav_ext_module_ctx, /* module context */ ngx_http_dav_ext_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; #define NGX_HTTP_DAV_EXT_NODE_propfind 0x001 #define NGX_HTTP_DAV_EXT_NODE_prop 0x002 #define NGX_HTTP_DAV_EXT_NODE_propname 0x004 #define NGX_HTTP_DAV_EXT_NODE_allprop 0x008 #define NGX_HTTP_DAV_EXT_PROP_creationdate 0x001 #define NGX_HTTP_DAV_EXT_PROP_displayname 0x002 #define NGX_HTTP_DAV_EXT_PROP_getcontentlanguage 0x004 #define NGX_HTTP_DAV_EXT_PROP_getcontentlength 0x008 #define NGX_HTTP_DAV_EXT_PROP_getcontenttype 0x010 #define NGX_HTTP_DAV_EXT_PROP_getetag 0x020 #define NGX_HTTP_DAV_EXT_PROP_getlastmodified 0x040 #define NGX_HTTP_DAV_EXT_PROP_lockdiscovery 0x080 #define NGX_HTTP_DAV_EXT_PROP_resourcetype 0x100 #define NGX_HTTP_DAV_EXT_PROP_source 0x200 #define NGX_HTTP_DAV_EXT_PROP_supportedlock 0x400 #define NGX_HTTP_DAV_EXT_PROPFIND_SELECTED 1 #define NGX_HTTP_DAV_EXT_PROPFIND_NAMES 2 #define NGX_HTTP_DAV_EXT_PROPFIND_ALL 3 typedef struct { ngx_uint_t nodes; ngx_uint_t props; ngx_uint_t propfind; } ngx_http_dav_ext_ctx_t; static int ngx_http_dav_ext_xmlcmp(const char *xname, const char *sname) { const char *c; c = strrchr(xname, ':'); return strcmp(c ? c + 1 : xname, sname); } static void ngx_http_dav_ext_start_xml_elt(void *user_data, const XML_Char *name, const XML_Char **atts) { ngx_http_dav_ext_ctx_t *ctx = user_data; #define NGX_HTTP_DAV_EXT_SET_NODE(nm) \ if (!ngx_http_dav_ext_xmlcmp(name, #nm)) \ ctx->nodes ^= NGX_HTTP_DAV_EXT_NODE_##nm NGX_HTTP_DAV_EXT_SET_NODE(propfind); NGX_HTTP_DAV_EXT_SET_NODE(prop); NGX_HTTP_DAV_EXT_SET_NODE(propname); NGX_HTTP_DAV_EXT_SET_NODE(allprop); } static void ngx_http_dav_ext_end_xml_elt(void *user_data, const XML_Char *name) { ngx_http_dav_ext_ctx_t *ctx = user_data; if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_propfind) { if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_prop) { ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_SELECTED; #define NGX_HTTP_DAV_EXT_SET_PROP(nm) \ if (!ngx_http_dav_ext_xmlcmp(name, #nm)) \ ctx->props |= NGX_HTTP_DAV_EXT_PROP_##nm NGX_HTTP_DAV_EXT_SET_PROP(creationdate); NGX_HTTP_DAV_EXT_SET_PROP(displayname); NGX_HTTP_DAV_EXT_SET_PROP(getcontentlanguage); NGX_HTTP_DAV_EXT_SET_PROP(getcontentlength); NGX_HTTP_DAV_EXT_SET_PROP(getcontenttype); NGX_HTTP_DAV_EXT_SET_PROP(getetag); NGX_HTTP_DAV_EXT_SET_PROP(getlastmodified); NGX_HTTP_DAV_EXT_SET_PROP(lockdiscovery); NGX_HTTP_DAV_EXT_SET_PROP(resourcetype); NGX_HTTP_DAV_EXT_SET_PROP(source); NGX_HTTP_DAV_EXT_SET_PROP(supportedlock); } if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_propname) { ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_NAMES; } if (ctx->nodes & NGX_HTTP_DAV_EXT_NODE_allprop) { ctx->propfind = NGX_HTTP_DAV_EXT_PROPFIND_ALL; } } ngx_http_dav_ext_start_xml_elt(user_data, name, NULL); } #define NGX_HTTP_DAV_EXT_COPY 0x01 #define NGX_HTTP_DAV_EXT_ESCAPE 0x02 static void ngx_http_dav_ext_output(ngx_http_request_t *r, ngx_chain_t **ll, ngx_int_t flags, u_char *data, ngx_uint_t len) { ngx_chain_t *cl; ngx_buf_t *b; if (!len) { return; } if (flags & NGX_HTTP_DAV_EXT_ESCAPE) { b = ngx_create_temp_buf(r->pool, len + ngx_escape_html(NULL, data, len)); b->last = (u_char*)ngx_escape_html(b->pos, data, len); } else if (flags & NGX_HTTP_DAV_EXT_COPY) { b = ngx_create_temp_buf(r->pool, len); b->last = ngx_cpymem(b->pos, data, len); } else { b = ngx_calloc_buf(r->pool); b->memory = 1; b->pos = data; b->start = data; b->last = b->pos + len; b->end = b->last; } cl = ngx_alloc_chain_link(r->pool); cl->buf = b; cl->next = NULL; if (*ll != NULL) { cl->next = (*ll)->next; (*ll)->next = cl; *ll = cl; } else { *ll = cl; cl->next = cl; } } static void ngx_http_dav_ext_flush(ngx_http_request_t *r, ngx_chain_t **ll) { ngx_chain_t *cl; cl = (*ll)->next; (*ll)->next = NULL; ngx_http_output_filter(r, cl); *ll = NULL; } /* output shortcuts NB: these shortcuts assume 2 variables exist in current context: r - request ptr ll - chain ptr ptr output chains are buffered in circular list & flushed on demand */ /* output buffer copy */ #define NGX_HTTP_DAV_EXT_OUTCB(data, len) \ ngx_http_dav_ext_output(r, ll, NGX_HTTP_DAV_EXT_COPY, (data), (len)) /* output string (no copy) */ #define NGX_HTTP_DAV_EXT_OUTS(s) \ ngx_http_dav_ext_output(r, ll, 0, (s)->data, (s)->len) /* output escaped string */ #define NGX_HTTP_DAV_EXT_OUTES(s) \ ngx_http_dav_ext_output(r, ll, NGX_HTTP_DAV_EXT_ESCAPE, (s)->data, (s)->len) /* output literal */ #define NGX_HTTP_DAV_EXT_OUTL(s) \ ngx_http_dav_ext_output(r, ll, 0, (u_char*)(s), sizeof(s) - 1) static ngx_int_t ngx_http_dav_ext_send_propfind_atts(ngx_http_request_t *r, char *path, ngx_str_t *uri, ngx_chain_t **ll, ngx_uint_t props) { struct stat st; struct tm stm; u_char buf[256]; ngx_str_t name; if (stat(path, &st)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, "dav_ext stat failed on '%s'", path); return NGX_HTTP_NOT_FOUND; } if (props & NGX_HTTP_DAV_EXT_PROP_creationdate) { /* output file ctime (attr change time) as creation time */ if (gmtime_r(&st.st_ctime, &stm) == NULL) return NGX_ERROR; /* ISO 8601 time format 2012-02-20T16:15:00Z */ NGX_HTTP_DAV_EXT_OUTCB(buf, strftime((char*)buf, sizeof(buf), "" "%Y-%m-%dT%TZ" "\n", &stm)); } if (props & NGX_HTTP_DAV_EXT_PROP_displayname) { NGX_HTTP_DAV_EXT_OUTL( "" ); if (uri->len) { for(name.data = uri->data + uri->len; name.data >= uri->data + 1 && name.data[-1] != '/'; --name.data); name.len = uri->data + uri->len - name.data; NGX_HTTP_DAV_EXT_OUTES(&name); } NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_getcontentlanguage) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_getcontentlength) { NGX_HTTP_DAV_EXT_OUTCB(buf, ngx_snprintf(buf, sizeof(buf), "" "%O" "\n", st.st_size) - buf); } if (props & NGX_HTTP_DAV_EXT_PROP_getcontenttype) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_getetag) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_getlastmodified) { if (gmtime_r(&st.st_mtime, &stm) == NULL) return NGX_ERROR; /* RFC 2822 time format */ NGX_HTTP_DAV_EXT_OUTCB(buf, strftime((char*)buf, sizeof(buf), "" "%a, %d %b %Y %T GMT" "\n", &stm)); } if (props & NGX_HTTP_DAV_EXT_PROP_lockdiscovery) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_resourcetype) { if (S_ISDIR(st.st_mode)) { NGX_HTTP_DAV_EXT_OUTL( "" "" "\n" ); } else { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } } if (props & NGX_HTTP_DAV_EXT_PROP_source) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } if (props & NGX_HTTP_DAV_EXT_PROP_supportedlock) { NGX_HTTP_DAV_EXT_OUTL( "\n" ); } return NGX_OK; } static ngx_int_t ngx_http_dav_ext_send_propfind_item(ngx_http_request_t *r, char *path, ngx_str_t *uri) { ngx_http_dav_ext_ctx_t *ctx; ngx_chain_t *l = NULL, **ll = &l; u_char vbuf[8]; ngx_str_t status_line = ngx_string("200 OK"); ctx = ngx_http_get_module_ctx(r, ngx_http_dav_ext_module); NGX_HTTP_DAV_EXT_OUTL( "\n" "" ); NGX_HTTP_DAV_EXT_OUTES(uri); NGX_HTTP_DAV_EXT_OUTL( "\n" "\n" "\n" ); if (ctx->propfind == NGX_HTTP_DAV_EXT_PROPFIND_NAMES) { NGX_HTTP_DAV_EXT_OUTL( "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ); } else { switch (ngx_http_dav_ext_send_propfind_atts(r, path, uri, ll, ctx->propfind == NGX_HTTP_DAV_EXT_PROPFIND_SELECTED ? ctx->props : (ngx_uint_t)-1)) { case NGX_HTTP_NOT_FOUND: ngx_str_set(&status_line, "404 Not Found"); break; case NGX_OK: case NGX_HTTP_OK: break; default: ngx_str_set(&status_line, "500 Internal Server Error"); } } NGX_HTTP_DAV_EXT_OUTL( "\n" "HTTP/" ); NGX_HTTP_DAV_EXT_OUTCB(vbuf, ngx_snprintf(vbuf, sizeof(vbuf), "%d.%d ", r->http_major, r->http_minor) - vbuf); NGX_HTTP_DAV_EXT_OUTS(&status_line); NGX_HTTP_DAV_EXT_OUTL( "\n" "\n" "\n" ); ngx_http_dav_ext_flush(r, ll); return NGX_OK; } /* path returned by this function is terminated with a hidden (out-of-len) null */ static void ngx_http_dav_ext_make_child(ngx_pool_t *pool, ngx_str_t *parent, u_char *child, size_t chlen, ngx_str_t *path) { u_char *s; path->data = ngx_palloc(pool, parent->len + 2 + chlen); s = path->data; s = ngx_cpymem(s, parent->data, parent->len); if (parent->len > 0 && s[-1] != '/') *s++ = '/'; s = ngx_cpymem(s, child, chlen); path->len = s - path->data; *s = 0; } #define DAV_EXT_INFINITY (-1) static ngx_int_t ngx_http_dav_ext_send_propfind(ngx_http_request_t *r) { size_t root; ngx_str_t path, spath, suri; ngx_chain_t *l = NULL, **ll = &l; DIR *dir; int depth; struct dirent *de; size_t len; ngx_http_variable_value_t vv; ngx_str_t depth_name = ngx_string("depth"); u_char *p; if (ngx_http_variable_unknown_header(&vv, &depth_name, &r->headers_in.headers.part, 0) == NGX_OK && vv.valid) { if (vv.len == sizeof("infinity") -1 && !ngx_strncasecmp(vv.data, (u_char*)"infinity", vv.len)) { depth = DAV_EXT_INFINITY; } else { depth = ngx_atoi(vv.data, vv.len); } } else { depth = DAV_EXT_INFINITY; } p = ngx_http_map_uri_to_path(r, &path, &root, 0); if (p == NULL || !path.len) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "dav_ext error mapping uri to path"); return NGX_ERROR; } path.len = p - path.data; *p = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http propfind path: \"%V\"", &path); NGX_HTTP_DAV_EXT_OUTL( "\n" "\n" ); ngx_http_dav_ext_flush(r, ll); ngx_http_dav_ext_send_propfind_item(r, (char*)path.data, &r->uri); if (depth) { /* treat infinite depth as 1 for performance reasons */ if ((dir = opendir((char*)path.data))) { while((de = readdir(dir))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; } len = strlen(de->d_name); ngx_http_dav_ext_make_child(r->pool, &path, (u_char*)de->d_name, len, &spath); ngx_http_dav_ext_make_child(r->pool, &r->uri, (u_char*)de->d_name, len, &suri); ngx_http_dav_ext_send_propfind_item(r, (char*)spath.data, &suri); } closedir(dir); } } NGX_HTTP_DAV_EXT_OUTL( "\n" ); if (*ll && (*ll)->buf) { (*ll)->buf->last_buf = 1; } ngx_http_dav_ext_flush(r, ll); return NGX_OK; } static void ngx_http_dav_ext_propfind_handler(ngx_http_request_t *r) { ngx_chain_t *c; ngx_buf_t *b; XML_Parser parser; ngx_uint_t status; ngx_http_dav_ext_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_dav_ext_module); if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dav_ext_ctx_t)); ngx_http_set_ctx(r, ctx, ngx_http_dav_ext_module); } c = r->request_body->bufs; status = NGX_OK; parser = XML_ParserCreate(NULL); XML_SetUserData(parser, ctx); XML_SetElementHandler(parser, ngx_http_dav_ext_start_xml_elt, ngx_http_dav_ext_end_xml_elt); for(; c != NULL && c->buf != NULL && !c->buf->last_buf; c = c->next) { b = c ->buf; if (!XML_Parse(parser, (const char*)b->pos, b->last - b->pos, b->last_buf)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "dav_ext propfind XML error"); status = NGX_ERROR; break; } } XML_ParserFree(parser); if (status == NGX_OK) { r->headers_out.status = 207; ngx_str_set(&r->headers_out.status_line, "207 Multi-Status"); ngx_http_send_header(r); ngx_http_finalize_request(r, ngx_http_dav_ext_send_propfind(r)); } else { r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR; r->header_only = 1; r->headers_out.content_length_n = 0; ngx_http_finalize_request(r, ngx_http_send_header(r)); } } static ngx_int_t ngx_http_dav_ext_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_table_elt_t *h; ngx_http_dav_ext_loc_conf_t *delcf; delcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_ext_module); if (!(r->method & delcf->methods)) { return NGX_DECLINED; } switch (r->method) { case NGX_HTTP_PROPFIND: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "dav_ext propfind"); rc = ngx_http_read_client_request_body(r, ngx_http_dav_ext_propfind_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_DONE; case NGX_HTTP_OPTIONS: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "dav_ext options"); h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_str_set(&h->key, "DAV"); ngx_str_set(&h->value, "1"); h->hash = 1; h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* FIXME: it looks so ugly because I cannot access nginx dav module */ ngx_str_set(&h->key, "Allow"); ngx_str_set(&h->value, "GET,HEAD,PUT,DELETE,MKCOL,COPY,MOVE,PROPFIND,OPTIONS"); h->hash = 1; r->headers_out.status = NGX_HTTP_OK; r->header_only = 1; r->headers_out.content_length_n = 0; ngx_http_send_header(r); return NGX_OK; } return NGX_DECLINED; } static void * ngx_http_dav_ext_create_loc_conf(ngx_conf_t *cf) { ngx_http_dav_ext_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_ext_loc_conf_t)); if (conf == NULL) { return NULL; } return conf; } static char * ngx_http_dav_ext_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_dav_ext_loc_conf_t *prev = parent; ngx_http_dav_ext_loc_conf_t *conf = child; ngx_conf_merge_bitmask_value(conf->methods, prev->methods, (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_EXT_OFF)); return NGX_CONF_OK; } static ngx_int_t ngx_http_dav_ext_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_dav_ext_handler; return NGX_OK; } debian/modules/nginx-lua/0000755000000000000000000000000012305451341012536 5ustar debian/modules/nginx-lua/valgrind.suppress0000644000000000000000000000434412305451334016161 0ustar { Memcheck:Addr1 fun:ngx_init_cycle fun:ngx_master_process_cycle fun:main } { Memcheck:Addr4 fun:ngx_init_cycle fun:ngx_master_process_cycle fun:main } { Memcheck:Cond fun:ngx_vslprintf fun:ngx_snprintf fun:ngx_sock_ntop fun:ngx_event_accept fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { Memcheck:Cond fun:ngx_vslprintf fun:ngx_snprintf fun:ngx_sock_ntop fun:ngx_event_accept fun:ngx_epoll_process_events fun:ngx_process_events_and_timers } { Memcheck:Addr1 fun:ngx_vslprintf fun:ngx_snprintf fun:ngx_sock_ntop fun:ngx_event_accept } { exp-sgcheck:SorG fun:ngx_http_lua_ndk_set_var_get } { exp-sgcheck:SorG fun:ngx_http_variables_init_vars fun:ngx_http_block } { exp-sgcheck:SorG fun:ngx_conf_parse } { exp-sgcheck:SorG fun:ngx_vslprintf fun:ngx_log_error_core } { Memcheck:Param epoll_ctl(event) fun:epoll_ctl } { Memcheck:Cond fun:ngx_conf_flush_files fun:ngx_single_process_cycle } { Memcheck:Cond fun:memcpy fun:ngx_vslprintf fun:ngx_log_error_core fun:ngx_http_charset_header_filter } { Memcheck:Param socketcall.setsockopt(optval) fun:setsockopt fun:drizzle_state_connect } { Memcheck:Cond fun:ngx_conf_flush_files fun:ngx_single_process_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init } { Memcheck:Param sendmsg(msg.msg_iov[0]) fun:__sendmsg_nocancel fun:ngx_write_channel fun:ngx_pass_open_channel fun:ngx_start_cache_manager_processes } { Memcheck:Cond fun:ngx_init_cycle fun:ngx_master_process_cycle fun:main } debian/modules/nginx-lua/README.markdown0000644000000000000000000076321412305451334015256 0ustar Name ==== ngx_lua - Embed the power of Lua into Nginx *This module is not distributed with the Nginx source.* See [the installation instructions](#installation). Table of Contents ================= * [Status](#status) * [Version](#version) * [Synopsis](#synopsis) * [Description](#description) * [Directives](#directives) * [lua_use_default_type](#lua_use_default_type) * [lua_code_cache](#lua_code_cache) * [lua_regex_cache_max_entries](#lua_regex_cache_max_entries) * [lua_regex_match_limit](#lua_regex_match_limit) * [lua_package_path](#lua_package_path) * [lua_package_cpath](#lua_package_cpath) * [init_by_lua](#init_by_lua) * [init_by_lua_file](#init_by_lua_file) * [set_by_lua](#set_by_lua) * [set_by_lua_file](#set_by_lua_file) * [content_by_lua](#content_by_lua) * [content_by_lua_file](#content_by_lua_file) * [rewrite_by_lua](#rewrite_by_lua) * [rewrite_by_lua_file](#rewrite_by_lua_file) * [access_by_lua](#access_by_lua) * [access_by_lua_file](#access_by_lua_file) * [header_filter_by_lua](#header_filter_by_lua) * [header_filter_by_lua_file](#header_filter_by_lua_file) * [body_filter_by_lua](#body_filter_by_lua) * [body_filter_by_lua_file](#body_filter_by_lua_file) * [log_by_lua](#log_by_lua) * [log_by_lua_file](#log_by_lua_file) * [lua_need_request_body](#lua_need_request_body) * [lua_shared_dict](#lua_shared_dict) * [lua_socket_connect_timeout](#lua_socket_connect_timeout) * [lua_socket_send_timeout](#lua_socket_send_timeout) * [lua_socket_send_lowat](#lua_socket_send_lowat) * [lua_socket_read_timeout](#lua_socket_read_timeout) * [lua_socket_buffer_size](#lua_socket_buffer_size) * [lua_socket_pool_size](#lua_socket_pool_size) * [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout) * [lua_socket_log_errors](#lua_socket_log_errors) * [lua_http10_buffering](#lua_http10_buffering) * [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) * [lua_transform_underscores_in_response_headers](#lua_transform_underscores_in_response_headers) * [lua_check_client_abort](#lua_check_client_abort) * [lua_max_pending_timers](#lua_max_pending_timers) * [lua_max_running_timers](#lua_max_running_timers) * [Nginx API for Lua](#nginx-api-for-lua) * [Introduction](#introduction) * [ngx.arg](#ngxarg) * [ngx.var.VARIABLE](#ngxvarvariable) * [Core constants](#core-constants) * [HTTP method constants](#http-method-constants) * [HTTP status constants](#http-status-constants) * [Nginx log level constants](#nginx-log-level-constants) * [print](#print) * [ngx.ctx](#ngxctx) * [ngx.location.capture](#ngxlocationcapture) * [ngx.location.capture_multi](#ngxlocationcapture_multi) * [ngx.status](#ngxstatus) * [ngx.header.HEADER](#ngxheaderheader) * [ngx.req.start_time](#ngxreqstart_time) * [ngx.req.http_version](#ngxreqhttp_version) * [ngx.req.raw_header](#ngxreqraw_header) * [ngx.req.get_method](#ngxreqget_method) * [ngx.req.set_method](#ngxreqset_method) * [ngx.req.set_uri](#ngxreqset_uri) * [ngx.req.set_uri_args](#ngxreqset_uri_args) * [ngx.req.get_uri_args](#ngxreqget_uri_args) * [ngx.req.get_post_args](#ngxreqget_post_args) * [ngx.req.get_headers](#ngxreqget_headers) * [ngx.req.set_header](#ngxreqset_header) * [ngx.req.clear_header](#ngxreqclear_header) * [ngx.req.read_body](#ngxreqread_body) * [ngx.req.discard_body](#ngxreqdiscard_body) * [ngx.req.get_body_data](#ngxreqget_body_data) * [ngx.req.get_body_file](#ngxreqget_body_file) * [ngx.req.set_body_data](#ngxreqset_body_data) * [ngx.req.set_body_file](#ngxreqset_body_file) * [ngx.req.init_body](#ngxreqinit_body) * [ngx.req.append_body](#ngxreqappend_body) * [ngx.req.finish_body](#ngxreqfinish_body) * [ngx.req.socket](#ngxreqsocket) * [ngx.exec](#ngxexec) * [ngx.redirect](#ngxredirect) * [ngx.send_headers](#ngxsend_headers) * [ngx.headers_sent](#ngxheaders_sent) * [ngx.print](#ngxprint) * [ngx.say](#ngxsay) * [ngx.log](#ngxlog) * [ngx.flush](#ngxflush) * [ngx.exit](#ngxexit) * [ngx.eof](#ngxeof) * [ngx.sleep](#ngxsleep) * [ngx.escape_uri](#ngxescape_uri) * [ngx.unescape_uri](#ngxunescape_uri) * [ngx.encode_args](#ngxencode_args) * [ngx.decode_args](#ngxdecode_args) * [ngx.encode_base64](#ngxencode_base64) * [ngx.decode_base64](#ngxdecode_base64) * [ngx.crc32_short](#ngxcrc32_short) * [ngx.crc32_long](#ngxcrc32_long) * [ngx.hmac_sha1](#ngxhmac_sha1) * [ngx.md5](#ngxmd5) * [ngx.md5_bin](#ngxmd5_bin) * [ngx.sha1_bin](#ngxsha1_bin) * [ngx.quote_sql_str](#ngxquote_sql_str) * [ngx.today](#ngxtoday) * [ngx.time](#ngxtime) * [ngx.now](#ngxnow) * [ngx.update_time](#ngxupdate_time) * [ngx.localtime](#ngxlocaltime) * [ngx.utctime](#ngxutctime) * [ngx.cookie_time](#ngxcookie_time) * [ngx.http_time](#ngxhttp_time) * [ngx.parse_http_time](#ngxparse_http_time) * [ngx.is_subrequest](#ngxis_subrequest) * [ngx.re.match](#ngxrematch) * [ngx.re.find](#ngxrefind) * [ngx.re.gmatch](#ngxregmatch) * [ngx.re.sub](#ngxresub) * [ngx.re.gsub](#ngxregsub) * [ngx.shared.DICT](#ngxshareddict) * [ngx.shared.DICT.get](#ngxshareddictget) * [ngx.shared.DICT.get_stale](#ngxshareddictget_stale) * [ngx.shared.DICT.set](#ngxshareddictset) * [ngx.shared.DICT.safe_set](#ngxshareddictsafe_set) * [ngx.shared.DICT.add](#ngxshareddictadd) * [ngx.shared.DICT.safe_add](#ngxshareddictsafe_add) * [ngx.shared.DICT.replace](#ngxshareddictreplace) * [ngx.shared.DICT.delete](#ngxshareddictdelete) * [ngx.shared.DICT.incr](#ngxshareddictincr) * [ngx.shared.DICT.flush_all](#ngxshareddictflush_all) * [ngx.shared.DICT.flush_expired](#ngxshareddictflush_expired) * [ngx.shared.DICT.get_keys](#ngxshareddictget_keys) * [ngx.socket.udp](#ngxsocketudp) * [udpsock:setpeername](#udpsocksetpeername) * [udpsock:send](#udpsocksend) * [udpsock:receive](#udpsockreceive) * [udpsock:close](#udpsockclose) * [udpsock:settimeout](#udpsocksettimeout) * [ngx.socket.tcp](#ngxsockettcp) * [tcpsock:connect](#tcpsockconnect) * [tcpsock:send](#tcpsocksend) * [tcpsock:receive](#tcpsockreceive) * [tcpsock:receiveuntil](#tcpsockreceiveuntil) * [tcpsock:close](#tcpsockclose) * [tcpsock:settimeout](#tcpsocksettimeout) * [tcpsock:setoption](#tcpsocksetoption) * [tcpsock:setkeepalive](#tcpsocksetkeepalive) * [tcpsock:getreusedtimes](#tcpsockgetreusedtimes) * [ngx.socket.connect](#ngxsocketconnect) * [ngx.get_phase](#ngxget_phase) * [ngx.thread.spawn](#ngxthreadspawn) * [ngx.thread.wait](#ngxthreadwait) * [ngx.on_abort](#ngxon_abort) * [ngx.timer.at](#ngxtimerat) * [ngx.config.debug](#ngxconfigdebug) * [ngx.config.prefix](#ngxconfigprefix) * [ngx.config.nginx_version](#ngxconfignginx_version) * [ngx.config.ngx_lua_version](#ngxconfigngx_lua_version) * [ngx.worker.exiting](#ngxworkerexiting) * [ndk.set_var.DIRECTIVE](#ndkset_vardirective) * [coroutine.create](#coroutinecreate) * [coroutine.resume](#coroutineresume) * [coroutine.yield](#coroutineyield) * [coroutine.wrap](#coroutinewrap) * [coroutine.running](#coroutinerunning) * [coroutine.status](#coroutinestatus) * [Lua/LuaJIT bytecode support](#lualuajit-bytecode-support) * [System Environment Variable Support](#system-environment-variable-support) * [HTTP 1.0 support](#http-10-support) * [Statically Linking Pure Lua Modules](#statically-linking-pure-lua-modules) * [Data Sharing within an Nginx Worker](#data-sharing-within-an-nginx-worker) * [Known Issues](#known-issues) * [TCP socket connect operation issues](#tcp-socket-connect-operation-issues) * [Lua Coroutine Yielding/Resuming](#lua-coroutine-yieldingresuming) * [Lua Variable Scope](#lua-variable-scope) * [Locations Configured by Subrequest Directives of Other Modules](#locations-configured-by-subrequest-directives-of-other-modules) * [Special PCRE Sequences](#special-pcre-sequences) * [Mixing with SSI Not Supported](#mixing-with-ssi-not-supported) * [SPDY Mode Not Fully Supported](#spdy-mode-not-fully-supported) * [Typical Uses](#typical-uses) * [Nginx Compatibility](#nginx-compatibility) * [Code Repository](#code-repository) * [Installation](#installation) * [Installation on Ubuntu 11.10](#installation-on-ubuntu-1110) * [Community](#community) * [English Mailing List](#english-mailing-list) * [Chinese Mailing List](#chinese-mailing-list) * [Bugs and Patches](#bugs-and-patches) * [TODO](#todo) * [Short Term](#short-term) * [Longer Term](#longer-term) * [Changes](#changes) * [Test Suite](#test-suite) * [Copyright and License](#copyright-and-license) * [See Also](#see-also) Status ====== This module is under active development and is production ready. Version ======= This document describes ngx_lua [v0.9.4](https://github.com/chaoslawful/lua-nginx-module/tags) released on 10 January 2014. Synopsis ======== ```nginx # set search paths for pure Lua external libraries (';;' is the default path): lua_package_path '/foo/bar/?.lua;/blah/?.lua;;'; # set search paths for Lua external libraries written in C (can also use ';;'): lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;'; server { location /inline_concat { # MIME type determined by default_type: default_type 'text/plain'; set $a "hello"; set $b "world"; # inline Lua script set_by_lua $res "return ngx.arg[1]..ngx.arg[2]" $a $b; echo $res; } location /rel_file_concat { set $a "foo"; set $b "bar"; # script path relative to nginx prefix # $ngx_prefix/conf/concat.lua contents: # # return ngx.arg[1]..ngx.arg[2] # set_by_lua_file $res conf/concat.lua $a $b; echo $res; } location /abs_file_concat { set $a "fee"; set $b "baz"; # absolute script path not modified set_by_lua_file $res /usr/nginx/conf/concat.lua $a $b; echo $res; } location /lua_content { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua "ngx.say('Hello,world!')"; } location /nginx_var { # MIME type determined by default_type: default_type 'text/plain'; # try access /nginx_var?a=hello,world content_by_lua "ngx.print(ngx.var['arg_a'], '\\n')"; } location /request_body { # force reading request body (default off) lua_need_request_body on; client_max_body_size 50k; client_body_buffer_size 50k; content_by_lua 'ngx.print(ngx.var.request_body)'; } # transparent non-blocking I/O in Lua via subrequests location /lua { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua ' local res = ngx.location.capture("/some_other_location") if res.status == 200 then ngx.print(res.body) end'; } # GET /recur?num=5 location /recur { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua ' local num = tonumber(ngx.var.arg_num) or 0 if num > 50 then ngx.say("num too big") return end ngx.say("num is: ", num) if num > 0 then res = ngx.location.capture("/recur?num=" .. tostring(num - 1)) ngx.print("status=", res.status, " ") ngx.print("body=", res.body) else ngx.say("end") end '; } location /foo { rewrite_by_lua ' res = ngx.location.capture("/memc", { args = { cmd = "incr", key = ngx.var.uri } } ) '; proxy_pass http://blah.blah.com; } location /blah { access_by_lua ' local res = ngx.location.capture("/auth") if res.status == ngx.HTTP_OK then return end if res.status == ngx.HTTP_FORBIDDEN then ngx.exit(res.status) end ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) '; # proxy_pass/fastcgi_pass/postgres_pass/... } location /mixed { rewrite_by_lua_file /path/to/rewrite.lua; access_by_lua_file /path/to/access.lua; content_by_lua_file /path/to/content.lua; } # use nginx var in code path # WARN: contents in nginx var must be carefully filtered, # otherwise there'll be great security risk! location ~ ^/app/(.+) { content_by_lua_file /path/to/lua/app/root/$1.lua; } location / { lua_need_request_body on; client_max_body_size 100k; client_body_buffer_size 100k; access_by_lua ' -- check the client IP address is in our black list if ngx.var.remote_addr == "132.5.72.3" then ngx.exit(ngx.HTTP_FORBIDDEN) end -- check if the request body contains bad words if ngx.var.request_body and string.match(ngx.var.request_body, "fsck") then return ngx.redirect("/terms_of_use.html") end -- tests passed '; # proxy_pass/fastcgi_pass/etc settings } } ``` [Back to TOC](#table-of-contents) Description =========== This module embeds Lua, via the standard Lua 5.1 interpreter or [LuaJIT 2.0/2.1](http://luajit.org/luajit.html), into Nginx and by leveraging Nginx's subrequests, allows the integration of the powerful Lua threads (Lua coroutines) into the Nginx event model. Unlike [Apache's mod_lua](http://httpd.apache.org/docs/2.3/mod/mod_lua.html) and [Lighttpd's mod_magnet](http://redmine.lighttpd.net/wiki/1/Docs:ModMagnet), Lua code executed using this module can be *100% non-blocking* on network traffic as long as the [Nginx API for Lua](#nginx-api-for-lua) provided by this module is used to handle requests to upstream services such as MySQL, PostgreSQL, Memcached, Redis, or upstream HTTP web services. At least the following Lua libraries and Nginx modules can be used with this ngx_lua module: * [lua-resty-memcached](https://github.com/agentzh/lua-resty-memcached) * [lua-resty-mysql](https://github.com/agentzh/lua-resty-mysql) * [lua-resty-redis](https://github.com/agentzh/lua-resty-redis) * [lua-resty-dns](https://github.com/agentzh/lua-resty-dns) * [lua-resty-upload](https://github.com/agentzh/lua-resty-upload) * [lua-resty-websocket](https://github.com/agentzh/lua-resty-websocket) * [lua-resty-lock](https://github.com/agentzh/lua-resty-lock) * [lua-resty-string](https://github.com/agentzh/lua-resty-string) * [ngx_memc](http://github.com/agentzh/memc-nginx-module) * [ngx_postgres](https://github.com/FRiCKLE/ngx_postgres) * [ngx_redis2](http://github.com/agentzh/redis2-nginx-module) * [ngx_redis](http://wiki.nginx.org/HttpRedisModule) * [ngx_proxy](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) * [ngx_fastcgi](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html) Almost all the Nginx modules can be used with this ngx_lua module by means of [ngx.location.capture](#ngxlocationcapture) or [ngx.location.capture_multi](#ngxlocationcapture_multi) but it is recommended to use those `lua-resty-*` libraries instead of creating subrequests to access the Nginx upstream modules because the former is usually much more flexible and memory-efficient. The Lua interpreter or LuaJIT instance is shared across all the requests in a single nginx worker process but request contexts are segregated using lightweight Lua coroutines. Loaded Lua modules persist in the nginx worker process level resulting in a small memory footprint in Lua even when under heavy loads. [Back to TOC](#table-of-contents) Directives ========== [Back to TOC](#table-of-contents) lua_use_default_type -------------------- **syntax:** *lua_use_default_type on | off* **default:** *lua_use_default_type on* **context:** *http, server, location, location if* Specifies whether to use the MIME type specified by the [default_type](http://nginx.org/en/docs/http/ngx_http_core_module.html#default_type) directive for the default value of the `Content-Type` response header. If you do not want a default `Content-Type` response header for your Lua request handlers, then turn this directive off. This directive is turned on by default. This directive was first introduced in the `v0.9.1` release. [Back to TOC](#table-of-contents) lua_code_cache -------------- **syntax:** *lua_code_cache on | off* **default:** *lua_code_cache on* **context:** *http, server, location, location if* Enables or disables the Lua code cache for Lua code in `*_by_lua_file` directives (like [set_by_lua_file](#set_by_lua_file) and [content_by_lua_file](#content_by_lua_file)) and Lua modules. When turning off, every request served by ngx_lua will run in a separate Lua VM instance, starting from the `0.9.3` release. So the Lua files referenced in [set_by_lua_file](#set_by_lua_file), [content_by_lua_file](#content_by_lua_file), [access_by_lua_file](#access_by_lua_file), and etc will not be cached and all Lua modules used will be loaded from scratch. With this in place, developers can adopt an edit-and-refresh approach. Please note however, that Lua code written inlined within nginx.conf such as those specified by [set_by_lua](#set_by_lua), [content_by_lua](#content_by_lua), [access_by_lua](#access_by_lua), and [rewrite_by_lua](#rewrite_by_lua) will not be updated when you edit the inlined Lua code in your `nginx.conf` file because only the Nginx config file parser can correctly parse the `nginx.conf` file and the only way is to reload the config file by sending a `HUP` signal or just to restart Nginx. Even when the code cache is enabled, Lua files which are loaded by `dofile` or `loadfile` in *_by_lua_file cannot be cached (unless you cache the results yourself). Usually you can either use the [init_by_lua](#init_by_lua) or [init_by_lua_file](#init-by_lua_file) directives to load all such files or just make these Lua files true Lua modules and load them via `require`. The ngx_lua module does not support the `stat` mode available with the Apache `mod_lua` module (yet). Disabling the Lua code cache is strongly discouraged for production use and should only be used during development as it has a significant negative impact on overall performance. For example, the performance a "hello world" Lua example can drop by an order of magnitude after disabling the Lua code cache. [Back to TOC](#table-of-contents) lua_regex_cache_max_entries --------------------------- **syntax:** *lua_regex_cache_max_entries <num>* **default:** *lua_regex_cache_max_entries 1024* **context:** *http* Specifies the maximum number of entries allowed in the worker process level compiled regex cache. The regular expressions used in [ngx.re.match](#ngxrematch), [ngx.re.gmatch](#ngxregmatch), [ngx.re.sub](#ngxresub), and [ngx.re.gsub](#ngxregsub) will be cached within this cache if the regex option `o` (i.e., compile-once flag) is specified. The default number of entries allowed is 1024 and when this limit is reached, new regular expressions will not be cached (as if the `o` option was not specified) and there will be one, and only one, warning in the `error.log` file: 2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ... Do not activate the `o` option for regular expressions (and/or `replace` string arguments for [ngx.re.sub](#ngxresub) and [ngx.re.gsub](#ngxregsub)) that are generated *on the fly* and give rise to infinite variations to avoid hitting the specified limit. [Back to TOC](#table-of-contents) lua_regex_match_limit --------------------- **syntax:** *lua_regex_match_limit <num>* **default:** *lua_regex_match_limit 0* **context:** *http* Specifies the "match limit" used by the PCRE library when executing the [ngx.re API](#ngxrematch). To quote the PCRE manpage, "the limit ... has the effect of limiting the amount of backtracking that can take place." When the limit is hit, the error string "pcre_exec() failed: -8" will be returned by the [ngx.re API](#ngxrematch) functions on the Lua land. When setting the limit to 0, the default "match limit" when compiling the PCRE library is used. And this is the default value of this directive. This directive was first introduced in the `v0.8.5` release. [Back to TOC](#table-of-contents) lua_package_path ---------------- **syntax:** *lua_package_path <lua-style-path-str>* **default:** *The content of LUA_PATH environ variable or Lua's compiled-in defaults.* **context:** *http* Sets the Lua module search path used by scripts specified by [set_by_lua](#set_by_lua), [content_by_lua](#content_by_lua) and others. The path string is in standard Lua path form, and `;;` can be used to stand for the original search paths. As from the `v0.5.0rc29` release, the special notation `$prefix` or `${prefix}` can be used in the search path string to indicate the path of the `server prefix` usually determined by the `-p PATH` command-line option while starting the Nginx server. [Back to TOC](#table-of-contents) lua_package_cpath ----------------- **syntax:** *lua_package_cpath <lua-style-cpath-str>* **default:** *The content of LUA_CPATH environment variable or Lua's compiled-in defaults.* **context:** *http* Sets the Lua C-module search path used by scripts specified by [set_by_lua](#set_by_lua), [content_by_lua](#content_by_lua) and others. The cpath string is in standard Lua cpath form, and `;;` can be used to stand for the original cpath. As from the `v0.5.0rc29` release, the special notation `$prefix` or `${prefix}` can be used in the search path string to indicate the path of the `server prefix` usually determined by the `-p PATH` command-line option while starting the Nginx server. [Back to TOC](#table-of-contents) init_by_lua ----------- **syntax:** *init_by_lua <lua-script-str>* **context:** *http* **phase:** *loading-config* Runs the Lua code specified by the argument `` on the global Lua VM level when the Nginx master process (if any) is loading the Nginx config file. When Nginx receives the `HUP` signal and starts reloading the config file, the Lua VM will also be re-created and `init_by_lua` will run again on the new Lua VM. Usually you can register (true) Lua global variables or pre-load Lua modules at server start-up by means of this hook. Here is an example for pre-loading Lua modules: ```nginx init_by_lua 'cjson = require "cjson"'; server { location = /api { content_by_lua ' ngx.say(cjson.encode({dog = 5, cat = 6})) '; } } ``` You can also initialize the [lua_shared_dict](#lua_shared_dict) shm storage at this phase. Here is an example for this: ```nginx lua_shared_dict dogs 1m; init_by_lua ' local dogs = ngx.shared.dogs; dogs:set("Tom", 56) '; server { location = /api { content_by_lua ' local dogs = ngx.shared.dogs; ngx.say(dogs:get("Tom")) '; } } ``` But note that, the [lua_shared_dict](#lua_shared_dict)'s shm storage will not be cleared through a config reload (via the `HUP` signal, for example). So if you do *not* want to re-initialize the shm storage in your `init_by_lua` code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your `init_by_lua` code. Because the Lua code in this context runs before Nginx forks its worker processes (if any), data or code loaded here will enjoy the [Copy-on-write (COW)](http://en.wikipedia.org/wiki/Copy-on-write) feature provided by many operating systems among all the worker processes, thus saving a lot of memory. Only a small set of the [Nginx API for Lua](#nginx-api-for-lua) is supported in this context: * Logging APIs: [ngx.log](#ngxlog) and [print](#print), * Shared Dictionary API: [ngx.shared.DICT](#ngxshareddict). More Nginx APIs for Lua may be supported in this context upon future user requests. Basically you can safely use Lua libraries that do blocking I/O in this very context because blocking the master process during server start-up is completely okay. Even the Nginx core does blocking I/O (at least on resolving upstream's host names) at the configure-loading phase. You should be very careful about potential security vulnerabilities in your Lua code registered in this context because the Nginx master process is often run under the `root` account. This directive was first introduced in the `v0.5.5` release. [Back to TOC](#table-of-contents) init_by_lua_file ---------------- **syntax:** *init_by_lua_file <path-to-lua-script-file>* **context:** *http* **phase:** *loading-config* Equivalent to [init_by_lua](#init_by_lua), except that the file specified by `` contains the Lua code or [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. This directive was first introduced in the `v0.5.5` release. [Back to TOC](#table-of-contents) set_by_lua ---------- **syntax:** *set_by_lua $res <lua-script-str> [$arg1 $arg2 ...]* **context:** *server, server if, location, location if* **phase:** *server-rewrite, rewrite* Executes code specified in `` with optional input arguments `$arg1 $arg2 ...`, and returns string output to `$res`. The code in `` can make [API calls](#nginx-api-for-lua) and can retrieve input arguments from the `ngx.arg` table (index starts from `1` and increases sequentially). This directive is designed to execute short, fast running code blocks as the Nginx event loop is blocked during code execution. Time consuming code sequences should therefore be avoided. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) * Control API functions (e.g., [ngx.exit](#ngxexit)) * Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) * Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). In addition, note that this directive can only write out a value to a single Nginx variable at a time. However, a workaround is possible using the [ngx.var.VARIABLE](#ngxvarvariable) interface. ```nginx location /foo { set $diff ''; # we have to predefine the $diff variable here set_by_lua $sum ' local a = 32 local b = 56 ngx.var.diff = a - b; -- write to $diff directly return a + b; -- return the $sum value normally '; echo "sum = $sum, diff = $diff"; } ``` This directive can be freely mixed with all directives of the [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html), [set-misc-nginx-module](http://github.com/agentzh/set-misc-nginx-module), and [array-var-nginx-module](http://github.com/agentzh/array-var-nginx-module) modules. All of these directives will run in the same order as they appear in the config file. ```nginx set $foo 32; set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; set $baz "bar: $bar"; # $baz == "bar: 33" ``` As from the `v0.5.0rc29` release, Nginx variable interpolation is disabled in the `` argument of this directive and therefore, the dollar sign character (`$`) can be used directly. This directive requires the [ngx_devel_kit](https://github.com/simpl/ngx_devel_kit) module. [Back to TOC](#table-of-contents) set_by_lua_file --------------- **syntax:** *set_by_lua_file $res <path-to-lua-script-file> [$arg1 $arg2 ...]* **context:** *server, server if, location, location if* **phase:** *server-rewrite, rewrite* Equivalent to [set_by_lua](#set_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. Nginx variable interpolation is supported in the `` argument string of this directive. But special care must be taken for injection attacks. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx. This directive requires the [ngx_devel_kit](https://github.com/simpl/ngx_devel_kit) module. [Back to TOC](#table-of-contents) content_by_lua -------------- **syntax:** *content_by_lua <lua-script-str>* **context:** *location, location if* **phase:** *content* Acts as a "content handler" and executes Lua code string specified in `` for every request. The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Do not use this directive and other content handler directives in the same location. For example, this directive and the [proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass) directive should not be used in the same location. [Back to TOC](#table-of-contents) content_by_lua_file ------------------- **syntax:** *content_by_lua_file <path-to-lua-script-file>* **context:** *location, location if* **phase:** *content* Equivalent to [content_by_lua](#content_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx. [Back to TOC](#table-of-contents) rewrite_by_lua -------------- **syntax:** *rewrite_by_lua <lua-script-str>* **context:** *http, server, location, location if* **phase:** *rewrite tail* Acts as a rewrite phase handler and executes Lua code string specified in `` for every request. The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs *after* the standard [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html). So the following will work as expected: ```nginx location /foo { set $a 12; # create and initialize $a set $b ""; # create and initialize $b rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; echo "res = $b"; } ``` because `set $a 12` and `set $b ""` run *before* [rewrite_by_lua](#rewrite_by_lua). On the other hand, the following will not work as expected: ```nginx ? location /foo { ? set $a 12; # create and initialize $a ? set $b ''; # create and initialize $b ? rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; ? if ($b = '13') { ? rewrite ^ /bar redirect; ? break; ? } ? ? echo "res = $b"; ? } ``` because `if` runs *before* [rewrite_by_lua](#rewrite_by_lua) even if it is placed after [rewrite_by_lua](#rewrite_by_lua) in the config. The right way of doing this is as follows: ```nginx location /foo { set $a 12; # create and initialize $a set $b ''; # create and initialize $b rewrite_by_lua ' ngx.var.b = tonumber(ngx.var.a) + 1 if tonumber(ngx.var.b) == 13 then return ngx.redirect("/bar"); end '; echo "res = $b"; } ``` Note that the [ngx_eval](http://www.grid.net.ru/nginx/eval.en.html) module can be approximated by using [rewrite_by_lua](#rewrite_by_lua). For example, ```nginx location / { eval $res { proxy_pass http://foo.com/check-spam; } if ($res = 'spam') { rewrite ^ /terms-of-use.html redirect; } fastcgi_pass ...; } ``` can be implemented in ngx_lua as: ```nginx location = /check-spam { internal; proxy_pass http://foo.com/check-spam; } location / { rewrite_by_lua ' local res = ngx.location.capture("/check-spam") if res.body == "spam" then return ngx.redirect("/terms-of-use.html") end '; fastcgi_pass ...; } ``` Just as any other rewrite phase handlers, [rewrite_by_lua](#rewrite_by_lua) also runs in subrequests. Note that when calling `ngx.exit(ngx.OK)` within a [rewrite_by_lua](#rewrite_by_lua) handler, the nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [rewrite_by_lua](#rewrite_by_lua) handler, calling [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. If the [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)'s [rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite) directive is used to change the URI and initiate location re-lookups (internal redirections), then any [rewrite_by_lua](#rewrite_by_lua) or [rewrite_by_lua_file](#rewrite_by_lua_file) code sequences within the current location will not be executed. For example, ```nginx location /foo { rewrite ^ /bar; rewrite_by_lua 'ngx.exit(503)'; } location /bar { ... } ``` Here the Lua code `ngx.exit(503)` will never run. This will be the case if `rewrite ^ /bar last` is used as this will similarly initiate an internal redirection. If the `break` modifier is used instead, there will be no internal redirection and the `rewrite_by_lua` code will be executed. The `rewrite_by_lua` code will always run at the end of the `rewrite` request-processing phase unless [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) is turned on. [Back to TOC](#table-of-contents) rewrite_by_lua_file ------------------- **syntax:** *rewrite_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *rewrite tail* Equivalent to [rewrite_by_lua](#rewrite_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid reloading Nginx. The `rewrite_by_lua_file` code will always run at the end of the `rewrite` request-processing phase unless [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) is turned on. [Back to TOC](#table-of-contents) access_by_lua ------------- **syntax:** *access_by_lua <lua-script-str>* **context:** *http, server, location, location if* **phase:** *access tail* Acts as an access phase handler and executes Lua code string specified in `` for every request. The Lua code may make [API calls](#nginx-api-for-lua) and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs *after* the standard [ngx_http_access_module](http://nginx.org/en/docs/http/ngx_http_access_module.html). So the following will work as expected: ```nginx location / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; deny all; access_by_lua ' local res = ngx.location.capture("/mysql", { ... }) ... '; # proxy_pass/fastcgi_pass/... } ``` That is, if a client IP address is in the blacklist, it will be denied before the MySQL query for more complex authentication is executed by [access_by_lua](#access_by_lua). Note that the [ngx_auth_request](http://mdounin.ru/hg/ngx_http_auth_request_module/) module can be approximated by using [access_by_lua](#access_by_lua): ```nginx location / { auth_request /auth; # proxy_pass/fastcgi_pass/postgres_pass/... } ``` can be implemented in ngx_lua as: ```nginx location / { access_by_lua ' local res = ngx.location.capture("/auth") if res.status == ngx.HTTP_OK then return end if res.status == ngx.HTTP_FORBIDDEN then ngx.exit(res.status) end ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) '; # proxy_pass/fastcgi_pass/postgres_pass/... } ``` As with other access phase handlers, [access_by_lua](#access_by_lua) will *not* run in subrequests. Note that when calling `ngx.exit(ngx.OK)` within a [access_by_lua](#access_by_lua) handler, the nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [access_by_lua](#access_by_lua) handler, calling [ngx.exit](#ngxexit) with status >= 200 (`ngx.HTTP_OK`) and status < 300 (`ngx.HTTP_SPECIAL_RESPONSE`) for successful quits and `ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)` (or its friends) for failures. [Back to TOC](#table-of-contents) access_by_lua_file ------------------ **syntax:** *access_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *access tail* Equivalent to [access_by_lua](#access_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. Nginx variables can be used in the `` string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [lua_code_cache](#lua_code_cache) `off` in `nginx.conf` to avoid repeatedly reloading Nginx. [Back to TOC](#table-of-contents) header_filter_by_lua -------------------- **syntax:** *header_filter_by_lua <lua-script-str>* **context:** *http, server, location, location if* **phase:** *output-header-filter* Uses Lua code specified in `` to define an output header filter. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) * Control API functions (e.g., [ngx.exit](#ngxexit) and [ngx.exec](#ngxexec)) * Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) * Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: ```nginx location / { proxy_pass http://mybackend; header_filter_by_lua 'ngx.header.Foo = "blah"'; } ``` This directive was first introduced in the `v0.2.1rc20` release. [Back to TOC](#table-of-contents) header_filter_by_lua_file ------------------------- **syntax:** *header_filter_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *output-header-filter* Equivalent to [header_filter_by_lua](#header_filter_by_lua), except that the file specified by `` contains the Lua code, or as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. This directive was first introduced in the `v0.2.1rc20` release. [Back to TOC](#table-of-contents) body_filter_by_lua ------------------ **syntax:** *body_filter_by_lua <lua-script-str>* **context:** *http, server, location, location if* **phase:** *output-body-filter* Uses Lua code specified in `` to define an output body filter. The input data chunk is passed via [ngx.arg](#ngxarg)[1] (as a Lua string value) and the "eof" flag indicating the end of the response body data stream is passed via [ngx.arg](#ngxarg)[2] (as a Lua boolean value). Behind the scene, the "eof" flag is just the `last_buf` (for main requests) or `last_in_chain` (for subrequests) flag of the Nginx chain link buffers. (Before the `v0.7.14` release, the "eof" flag does not work at all in subrequests.) The output data stream can be aborted immediately by running the following Lua statement: ```lua return ngx.ERROR ``` This will truncate the response body and usually result in incomplete and also invalid responses. The Lua code can pass its own modified version of the input data chunk to the downstream Nginx output body filters by overriding [ngx.arg](#ngxarg)[1] with a Lua string or a Lua table of strings. For example, to transform all the lowercase letters in the response body, we can just write: ```nginx location / { proxy_pass http://mybackend; body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])'; } ``` When setting `nil` or an empty Lua string value to `ngx.arg[1]`, no data chunk will be passed to the downstream Nginx output filters at all. Likewise, new "eof" flag can also be specified by setting a boolean value to [ngx.arg](#ngxarg)[2]. For example, ```nginx location /t { echo hello world; echo hiya globe; body_filter_by_lua ' local chunk = ngx.arg[1] if string.match(chunk, "hello") then ngx.arg[2] = true -- new eof return end -- just throw away any remaining chunk data ngx.arg[1] = nil '; } ``` Then `GET /t` will just return the output hello world That is, when the body filter sees a chunk containing the word "hello", then it will set the "eof" flag to true immediately, resulting in truncated but still valid responses. When the Lua code may change the length of the response body, then it is required to always clear out the `Content-Length` response header (if any) in a header filter to enforce streaming output, as in ```nginx location /foo { # fastcgi_pass/proxy_pass/... header_filter_by_lua 'ngx.header.content_length = nil'; body_filter_by_lua 'ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"'; } ``` Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) * Control API functions (e.g., [ngx.exit](#ngxexit) and [ngx.exec](#ngxexec)) * Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) * Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). Nginx output filters may be called multiple times for a single request because response body may be delivered in chunks. Thus, the Lua code specified by in this directive may also run multiple times in the lifetime of a single HTTP request. This directive was first introduced in the `v0.5.0rc32` release. [Back to TOC](#table-of-contents) body_filter_by_lua_file ----------------------- **syntax:** *body_filter_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *output-body-filter* Equivalent to [body_filter_by_lua](#body_filter_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. This directive was first introduced in the `v0.5.0rc32` release. [Back to TOC](#table-of-contents) log_by_lua ---------- **syntax:** *log_by_lua <lua-script-str>* **context:** *http, server, location, location if* **phase:** *log* Run the Lua source code inlined as the `` at the `log` request processing phase. This does not replace the current access logs, but runs after. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [ngx.say](#ngxsay) and [ngx.send_headers](#ngxsend_headers)) * Control API functions (e.g., [ngx.exit](#ngxexit)) * Subrequest API functions (e.g., [ngx.location.capture](#ngxlocationcapture) and [ngx.location.capture_multi](#ngxlocationcapture_multi)) * Cosocket API functions (e.g., [ngx.socket.tcp](#ngxsockettcp) and [ngx.req.socket](#ngxreqsocket)). Here is an example of gathering average data for [$upstream_response_time](http://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_response_time): ```nginx lua_shared_dict log_dict 5M; server { location / { proxy_pass http://mybackend; log_by_lua ' local log_dict = ngx.shared.log_dict local upstream_time = tonumber(ngx.var.upstream_response_time) local sum = log_dict:get("upstream_time-sum") or 0 sum = sum + upstream_time log_dict:set("upstream_time-sum", sum) local newval, err = log_dict:incr("upstream_time-nb", 1) if not newval and err == "not found" then log_dict:add("upstream_time-nb", 0) log_dict:incr("upstream_time-nb", 1) end '; } location = /status { content_by_lua ' local log_dict = ngx.shared.log_dict local sum = log_dict:get("upstream_time-sum") local nb = log_dict:get("upstream_time-nb") if nb and sum then ngx.say("average upstream response time: ", sum / nb, " (", nb, " reqs)") else ngx.say("no data yet") end '; } } ``` This directive was first introduced in the `v0.5.0rc31` release. [Back to TOC](#table-of-contents) log_by_lua_file --------------- **syntax:** *log_by_lua_file <path-to-lua-script-file>* **context:** *http, server, location, location if* **phase:** *log* Equivalent to [log_by_lua](#log_by_lua), except that the file specified by `` contains the Lua code, or, as from the `v0.5.0rc32` release, the [Lua/LuaJIT bytecode](#lualuajit-bytecode-support) to be executed. When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server. This directive was first introduced in the `v0.5.0rc31` release. [Back to TOC](#table-of-contents) lua_need_request_body --------------------- **syntax:** *lua_need_request_body <on|off>* **default:** *off* **context:** *main | server | location* **phase:** *depends on usage* Determines whether to force the request body data to be read before running rewrite/access/access_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned `on` or the [ngx.req.read_body](#ngxreqread_body) function should be called within the Lua code. To read the request body data within the [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) variable, [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) must have the same value as [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size). Because when the content length exceeds [client_body_buffer_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size) but less than [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size), Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the [$request_body](http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_body) variable. If the current location includes [rewrite_by_lua](#rewrite_by_lua) or [rewrite_by_lua_file](#rewrite_by_lua_file) directives, then the request body will be read just before the [rewrite_by_lua](#rewrite_by_lua) or [rewrite_by_lua_file](#rewrite_by_lua_file) code is run (and also at the `rewrite` phase). Similarly, if only [content_by_lua](#content_by_lua) is specified, the request body will not be read until the content handler's Lua code is about to run (i.e., the request body will be read during the content phase). It is recommended however, to use the [ngx.req.read_body](#ngxreqread_body) and [ngx.req.discard_body](#ngxreqdiscard_body) functions for finer control over the request body reading process instead. This also applies to [access_by_lua](#access_by_lua) and [access_by_lua_file](#access_by_lua_file). [Back to TOC](#table-of-contents) lua_shared_dict --------------- **syntax:** *lua_shared_dict <name> <size>* **default:** *no* **context:** *http* **phase:** *depends on usage* Declares a shared memory zone, ``, to serve as storage for the shm based Lua dictionary `ngx.shared.`. The `` argument accepts size units such as `k` and `m`: ```nginx http { lua_shared_dict dogs 10m; ... } ``` See [ngx.shared.DICT](#ngxshareddict) for details. This directive was first introduced in the `v0.3.1rc22` release. [Back to TOC](#table-of-contents) lua_socket_connect_timeout -------------------------- **syntax:** *lua_socket_connect_timeout <time>* **default:** *lua_socket_connect_timeout 60s* **context:** *http, server, location* This directive controls the default timeout value used in TCP/unix-domain socket object's [connect](#tcpsockconnect) method and can be overridden by the [settimeout](#tcpsocksettimeout) method. The `