The following variables are supported in ModSecurity 2.x:
ARGS
is a collection and can be used on its own
(means all arguments including the POST Payload), with a static
parameter (matches arguments with that name), or with a regular
expression (matches all arguments with name that matches the regular
expression). To look at only the query string or body arguments, see the
ARGS_GET
and ARGS_POST
collections.
Some variables are actually collections, which are expanded into more variables at runtime. The following example will examine all request arguments:
SecRule ARGS dirtySometimes, however, you will want to look only at parts of a collection. This can be achieved with the help of the selection operator(colon). The following example will only look at the arguments named
p
(do note that, in
general, requests can contain multiple arguments with the same name):
SecRule ARGS:p dirtyIt is also possible to specify exclusions. The following will examine all request arguments for the word dirty, except the ones named
z
(again, there can be
zero or more arguments named z
):
SecRule ARGS|!ARGS:z dirtyThere is a special operator that allows you to count how many variables there are in a collection. The following rule will trigger if there is more than zero arguments in the request (ignore the second parameter for the time being):
SecRule &ARGS !^0$And sometimes you need to look at an array of parameters, each with a slightly different name. In this case you can specify a regular expression in the selection operator itself. The following rule will look into all arguments whose names begin with
id_
: SecRule ARGS:/^id_/ dirty
Using ARGS:p
will not result in any
invocations against the operator if argument p does not exist.
In ModSecurity 1.X, the ARGS
variable stood
for QUERY_STRING
+ POST_PAYLOAD
,
whereas now it expands to individual variables.
This variable allows you to set more targeted evaluations on the total size of the Arguments as compared with normal Apache LimitRequest directives. For example, you could create a rule to ensure that the total size of the argument data is below a certain threshold (to help prevent buffer overflow issues). Example: Block request if the size of the arguments is above 25 characters.
SecRule REQUEST_FILENAME "^/cgi-bin/login\.php" \
"chain,log,deny,phase:2,t:none,t:lowercase,t:normalisePath"
SecRule ARGS_COMBINED_SIZE "@gt 25"
Is a collection of the argument names. You can search for specific argument names that you want to block. In a positive policy scenario, you can also whitelist (using an inverted rule with the ! character) only authorized argument names. Example: This example rule will only allow 2 argument names - p and a. If any other argument names are injected, it will be blocked.
SecRule REQUEST_FILENAME "/index.php" \
"chain,log,deny,status:403,phase:2,t:none,t:lowercase,t:normalisePath"
SecRule ARGS_NAMES "!^(p|a)$" "t:none,t:lowercase"
ARGS_GET_NAMES
is similar to
ARGS_NAMES
, but only contains argument names from the
query string.
ARGS_POST_NAMES
is similar to
ARGS_NAMES
, but only contains argument names from the
POST body.
This variable holds the authentication method used to validate a user. Example:
SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase
Note
This data will not be available in a proxy-mode deployment as the
authentication is not local. In a proxy-mode deployment, you would need
to inspect the REQUEST_HEADERS:Authorization
header.
Collection, requires a single parameter (after colon). The
ENV
variable is set with setenv and does not give
access to the CGI environment variables. Example:
SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious SecRule ENV:tag "suspicious"
Collection. Contains a collection of original file names (as they were called on the remote user's file system). Note: only available if files were extracted from the request body. Example:
SecRule FILES "\.conf$" log,deny,status:403,phase:2
Single value. Total size of the uploaded files. Note: only available if files were extracted from the request body. Example:
SecRule FILES_COMBINED_SIZE "@gt 1000" log,deny,status:403,phase:2
Collection w/o parameter. Contains a list of form fields that were used for file upload. Note: only available if files were extracted from the request body. Example:
SecRule FILES_NAMES "^upfile$" log,deny,status:403,phase:2
Collection. Contains a list of file sizes. Useful for implementing a size limitation on individual uploaded files. Note: only available if files were extracted from the request body. Example:
SecRule FILES_SIZES "@gt 100" log,deny,status:403,phase:2
Collection. Contains a collection of temporary files' names on the
disk. Useful when used together with @inspectFile.
Note: only available if files
were extracted from the request body. Example:
SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl"
GEO
is a collection populated by the results of
the last @geoLookup
operator. The
collection can be used to match geographical fields looked from an IP
address or hostname.
Available since ModSecurity 2.5.0.
Fields:
COUNTRY_CODE: Two character country code. EX: US, GB, etc.
COUNTRY_CODE3: Up to three character country code.
COUNTRY_NAME: The full country name.
COUNTRY_CONTINENT: The two character continent that the country is located. EX: EU
REGION: The two character region. For US, this is state. For Canada, providence, etc.
CITY: The city name if supported by the database.
POSTAL_CODE: The postal code if supported by the database.
LATITUDE: The latitude if supported by the database.
LONGITUDE: The longitude if supported by the database.
DMA_CODE: The metropolitan area code if supported by the database. (US only)
AREA_CODE: The phone system area code. (US only)
Example:
SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat
...
SecRule REMOTE_ADDR "@geoLookup" "chain,drop,msg:'Non-GB IP address'"
SecRule GEO:COUNTRY_CODE "!@streq GB"
This variable holds the highest severity of any rules that have
matched so far. Severities are numeric values and thus can be used with
comparison operators such as @lt
,
etc.
Higher severities have a lower numeric value.
A value of 255 indicates no severity has been set.
SecRule HIGHEST_SEVERITY "@le 2" "phase:2,deny,status:500,msg:'severity %{HIGHEST_SEVERITY}'"
This variable holds the value of the variable that was matched
against. It is similar to the TX:0, except it can be used for all
operators and does not require that the capture
action be specified.
SecRule ARGS pattern chain,deny
...
SecRule MATCHED_VAR "further scrutiny"
This variable holds the full name of the variable that was matched against.
SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME}
...
SecRule TX:MYMATCH "@eq ARGS:param" deny
This variable holds the ModSecurity build number. This variable is intended to be used to check the build number prior to using a feature that is available only in a certain build. Example:
SecRule MODSEC_BUILD "!@ge 02050102" skipAfter:12345
SecRule ARGS "@pm some key words" id:12345,deny,status:500
This flag variable will be set to 1
whenever a
multi-part request uses mixed line terminators. The
multipart/form-data
RFC requires
CRLF
sequence to be used to terminate lines. Since
some client implementations use only LF
to terminate
lines you might want to allow them to proceed under certain
circumstances (if you want to do this you will need to stop using
MULTIPART_STRICT_ERROR
and check each multi-part flag
variable individually, avoiding MULTIPART_LF_LINE
).
However, mixing CRLF
and LF
line
terminators is dangerous as it can allow for evasion. Therefore, in such
cases, you will have to add a check for
MULTIPART_CRLF_LF_LINES
.
MULTIPART_STRICT_ERROR
will be set to
1
when any of the following variables is also set to
1
: REQBODY_PROCESSOR_ERROR
,
MULTIPART_BOUNDARY_QUOTED
,
MULTIPART_BOUNDARY_WHITESPACE
,
MULTIPART_DATA_BEFORE
,
MULTIPART_DATA_AFTER
,
MULTIPART_HEADER_FOLDING
,
MULTIPART_LF_LINE
,
MULTIPART_SEMICOLON_MISSING
MULTIPART_INVALID_QUOTING
. Each of these variables
covers one unusual (although sometimes legal) aspect of the request body
in multipart/form-data format
. Your policies should
always contain a rule to check either this variable
(easier) or one or more individual variables (if you know exactly what
you want to accomplish). Depending on the rate of false positives and
your default policy you should decide whether to block or just warn when
the rule is triggered.
The best way to use this variable is as in the example below:
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart request body \ failed strict validation: \ PE %{REQBODY_PROCESSOR_ERROR}, \ BQ %{MULTIPART_BOUNDARY_QUOTED}, \ BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ DB %{MULTIPART_DATA_BEFORE}, \ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}, \ IQ %{MULTIPART_INVALID_QUOTING}'"
The multipart/form-data
parser was upgraded in
ModSecurity v2.1.3 to actively look for signs of evasion. Many variables
(as listed above) were added to expose various facts discovered during
the parsing process. The MULTIPART_STRICT_ERROR
variable is handy to check on all abnormalities at once. The individual
variables allow detection to be fine-tuned according to your
circumstances in order to reduce the number of false positives. Detailed
analysis of various evasion techniques covered will be released as a
separated document at a later date.
Set to 1
when, during the parsing phase of a
multipart/request-body
, ModSecurity encounters what
feels like a boundary but it is not. Such an event may occur when
evasion of ModSecurity is attempted.
The best way to use this variable is as in the example below:
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
Change the rule from blocking to logging-only if many false positives are encountered.
Besides passing query information to a script/handler, you can also pass additional data, known as extra path information, as part of the URL. Example:
SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)"
This variable holds form data passed to the script/handler by appending data after a question mark. Warning: Not URL-decoded. Example:
SecRule QUERY_STRING "attack"
This variable holds the IP address of the remote client. Example:
SecRule REMOTE_ADDR "^192\.168\.1\.101$"
If HostnameLookUps are set to On, then this variable will hold the DNS resolved remote host name. If it is set to Off, then it will hold the remote IP address. Possible uses for this variable would be to deny known bad client hosts or network blocks, or conversely, to allow in authorized hosts. Example:
SecRule REMOTE_HOST "\.evil\.network\org$"
This variable holds information on the source port that the client
used when initiating the connection to our web server. Example: in this
example, we are evaluating to see if the REMOTE_PORT
is less than 1024, which would indicate that the user is a privileged
user (root).
SecRule REMOTE_PORT "@lt 1024" phase:1,log,pass,setenv:remote_port=privileged
This variable holds the username of the authenticated user. If there are no password (basic|digest) access controls in place, then this variable will be empty. Example:
SecRule REMOTE_USER "admin"
Note
This data will not be available in a proxy-mode deployment as the authentication is not local.
Built-in processors are URLENCODED
,
MULTIPART
, and XML
.
Example:
SecRule REQBODY_PROCESSOR "^XML$ chain
SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
Possible values are 0 (no error) or 1 (error). This variable will
be set by request body processors (typically the
multipart/request-data
parser or the XML parser)
when they fail to properly parse a request payload.
Example:
SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2
Your policies must have a rule to check REQBODY_PROCESSOR_ERROR at the beginning of phase 2. Failure to do so will leave the door open for impedance mismatch attacks. It is possible, for example, that a payload that cannot be parsed by ModSecurity can be successfully parsed by more tolerant parser operating in the application. If your policy dictates blocking then you should reject the request if error is detected. When operating in detection-only mode your rule should alert with high severity when request body processing fails.
Empty, or contains the error message from the processor. Example:
SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase
This variable holds just the filename part of
REQUEST_FILENAME
(e.g. index.php).
Example:
SecRule REQUEST_BASENAME "^login\.php$" phase:2,t:none,t:lowercase
Please note that anti-evasion transformations are not applied to
this variable by default. REQUEST_BASENAME
will
recognise both /
and \
as path
separators.
This variable holds the data in the request body (including
POST_PAYLOAD
data). REQUEST_BODY
should be used if the original order of the arguments is important
(ARGS
should be used in all other cases).
Example:
SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$"
This variable is only available if the
URLENCODED
request body processor parsed a request
body. This will occur by default when an
application/x-www-form-urlencoded
is detected, or
the URLENCODED
request body parser is forced. As of
2.5.7 it is possible to force the presence of the
REQUEST_BODY
variable, but only when there is no
request body processor defined, using the
ctl:forceRequestBodyVariable
option in the
REQUEST_HEADERS
phase.
This variable is a collection of all of the cookie data. Example: the following example is using the Ampersand special operator to count how many variables are in the collection. In this rule, it would trigger if the request does not include any Cookie headers.
SecRule &REQUEST_COOKIES "@eq 0"
This variable is a collection of the cookie names in the request headers. Example: the following rule will trigger if the JSESSIONID cookie is not present.
SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0"
This variable holds the relative REQUEST_URI
minus the QUERY_STRING
part (e.g. /index.php).
Example:
SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" phase:2,t:none,t:normalisePath
Please note that anti-evasion transformations are not used on
REQUEST_FILENAME
by default.
This variable can be used as either a collection of all of the
request headers or can be used to specify individual headers (by using
REQUEST_HEADERS:Header-Name). Example: the first
example uses REQUEST_HEADERS
as a collection and is
applying the validateUrlEncoding
operator against all
headers.
SecRule REQUEST_HEADERS "@validateUrlEncoding"
Example: the second example is targeting only the
Host
header.
SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \
"deny,log,status:400,msg:'Host header is a numeric IP address'"
This variable is a collection of the names of all of the request headers. Example:
SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \
"log,deny,status:403,t:lowercase,msg:'Proxy Server Used'"
This variable holds the complete request line sent to the server (including the REQUEST_METHOD and HTTP version data). Example: this example rule will trigger if the request method is something other than GET, HEAD, POST or if the HTTP is something other than HTTP/0.9, 1.0 or 1.1.
SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" t:none,t:lowercase
This variable holds the request method used by the client.
The following example will trigger if the request method is either
CONNECT
or TRACE.
SecRule REQUEST_METHOD "^((?:connect|trace))$" t:none,t:lowercase
This variable holds the request protocol version information. Example:
SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" t:none,t:lowercase
This variable holds the full URL including the
QUERY_STRING
data (e.g. /index.php?p=X), however it
will never contain a domain name, even if it was provided on the request
line. It also does not include either the
REQUEST_METHOD
or the HTTP version info.
Example:
SecRule REQUEST_URI "attack" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath
Please note that anti-evasion transformations are not used on
REQUEST_URI
by default.
Same as REQUEST_URI
but will contain the domain
name if it was provided on the request line (e.g.
http://www.example.com/index.php?p=X).
Example:
SecRule REQUEST_URI_RAW "http:/" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath
Please note that anti-evasion transformations are not used on
REQUEST_URI_RAW
by default.
This variable holds the data for the response payload.
Example:
SecRule RESPONSE_BODY "ODBC Error Code"
Response body length in bytes. Can be available starting with
phase 3 but it does not have to be (as the length of response body is
not always known in advance.) If the size is not known this variable
will contain a zero. If RESPONSE_CONTENT_LENGTH
contains a zero in phase 5 that means the actual size of the response
body was 0.
The value of this variable can change between phases if the body
is modified. For example, in embedded mode
mod_deflate
can compress the response body between
phases 4 and 5.
This variable is similar to the REQUEST_HEADERS variable and can be used in the same manner. Example:
SecRule RESPONSE_HEADERS:X-Cache "MISS"
Note
This variable may not have access to some headers when running in embedded-mode. Headers such as Server, Date, Connection and Content-Type are added during a later Apache hook just prior to sending the data to the client. This data should be available, however, either during ModSecurity phase:5 (logging) or when running in proxy-mode.
This variable is a collection of the response header names. Example:
SecRule RESPONSE_HEADERS_NAMES "Set-Cookie"
Note
Same limitations as RESPONSE_HEADERS with regards to access to some headers in embedded-mode.
This variable holds the HTTP response protocol information. Example:
SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9"
This variable holds the HTTP response status code as generated by Apache. Example:
SecRule RESPONSE_STATUS "^[45]"
Note
This directive may not work as expected in embedded-mode as Apache handles many of the stock response codes (404, 401, etc...) earlier in Phase 2. This variable should work as expected in a proxy-mode deployment.
This variable provides access to the id
, rev
,
severity
, logdata
, and msg
fields of the rule that triggered the
action. Only available for expansion in action strings (e.g.setvar:tx.varname=%{rule.id}
). Example:
SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,setvar:tx.varname=%{rule.id}"
This variable holds just the local filename part of SCRIPT_FILENAME. Example:
SecRule SCRIPT_BASENAME "^login\.php$"
Note
This variable is not available in proxy mode.
This variable holds the full path on the server to the requested script. (e.g. SCRIPT_NAME plus the server path). Example:
SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$"
Note
This variable is not available in proxy mode.
This variable holds the group id (numerical value) of the group owner of the script. Example:
SecRule SCRIPT_GID "!^46$"
Note
This variable is not available in proxy mode.
This variable holds the group name of the group owner of the script. Example:
SecRule SCRIPT_GROUPNAME "!^apache$"
Note
This variable is not available in proxy mode.
This variable holds the script's permissions mode data (numerical - 1=execute, 2=write, 4=read and 7=read/write/execute). Example: will trigger if the script has the WRITE permissions set.
SecRule SCRIPT_MODE "^(2|3|6|7)$"
Note
This variable is not available in proxy mode.
This variable holds the user id (numerical value) of the owner of the script. Example: the example rule below will trigger if the UID is not 46 (the Apache user).
SecRule SCRIPT_UID "!^46$"
Note
This variable is not available in proxy mode.
This variable holds the username of the owner of the script. Example:
SecRule SCRIPT_USERNAME "!^apache$"
Note
This variable is not available in proxy mode.
This variable contains the IP address of the server. Example:
SecRule SERVER_ADDR "^192\.168\.1\.100$"
This variable contains the server's hostname or IP address. Example:
SecRule SERVER_NAME "hostname\.com$"
Note
This data is taken from the Host header submitted in the client request.
This variable contains the local port that the web server is listening on. Example:
SecRule SERVER_PORT "^80$"
This variable is a collection, available only after setsid
is executed. Example: the following
example shows how to initialize a SESSION collection with setsid, how to
use setvar to increase the session.score values, how to set the
session.blocked variable and finally how to deny the connection based on
the session:blocked value.
SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} SecRule REQUEST_URI "^/cgi-bin/finger$" \ "phase:2,t:none,t:lowercase,t:normalisePath,pass,log,setvar:session.score=+10" SecRule SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403"
This variable is the value set with setsid
. Example:
SecRule SESSIONID !^$ chain,nolog,pass
SecRule REQUEST_COOKIES:PHPSESSID !^$
SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
This variable holds a formatted string representing the time (hour:minute:second). Example:
SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$"
This variable holds the current date (1-31). Example: this rule would trigger anytime between the 10th and 20th days of the month.
SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$"
This variable holds the current hour (0-23). Example: this rule would trigger during "off hours".
SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$"
This variable holds the current minute (0-59). Example: this rule would trigger during the last half hour of every hour.
SecRule TIME_MIN "^(3|4|5)"
This variable holds the current month (0-11). Example: this rule would match if the month was either November (10) or December (11).
SecRule TIME_MON "^1"
This variable holds the current weekday (0-6). Example: this rule would trigger only on week-ends (Saturday and Sunday).
SecRule TIME_WDAY "^(0|6)$"
Transaction Collection. This is used to store pieces of data, create a transaction anomaly score, and so on. Transaction variables are set for 1 request/response cycle. The scoring and evaluation will not last past the current request/response process. Example: In this example, we are using setvar to increase the tx.score value by 5 points. We then have a follow-up run that will evaluate the transactional score this request and then it will decided whether or not to allow/deny the request through.
The following is a list of reserved names in the TX collection:
TX:0
- The matching value
when using the @rx
or @pm
operator with the capture
action.
TX:1-TX:9
- The captured
subexpression value when using the @rx
operator with capturing parens and the
capture
action.
SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" SecRule TX:SCORE "@gt 20" deny,log
This variable is the value set with setuid
. Example:
SecAction setuid:%{REMOTE_USER},nolog
SecRule USERID "Admin"
This variable is the value set with SecWebAppId
. Example:
SecWebAppId "WebApp1"
SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403"
SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
Contains zero or more error messages produced by the web server. Access to this variable is in phase:5 (logging). Example:
SecRule WEBSERVER_ERROR_LOG "File does not exist" "phase:5,setvar:tx.score=+5"
Can be used standalone (as a target for
validateDTD
and validateSchema
) or
with an XPath expression parameter (which makes it a valid target for
any function that accepts plain text). Example using XPath:
SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ id:12345,xmlns:xq=http://www.example.com/employees
The first XPath expression does not use namespaces. It would match against payload such as this one:
<employees> <employee> <name>Fred Jones</name> <address location="home"> <street>900 Aurora Ave.</street> <city>Seattle</city> <state>WA</state> <zip>98115</zip> </address> <address location="work"> <street>2011 152nd Avenue NE</street> <city>Redmond</city> <state>WA</state> <zip>98052</zip> </address> <phone location="work">(425)555-5665</phone> <phone location="home">(206)555-5555</phone> <phone location="mobile">(206)555-4321</phone> </employee> </employees>
The second XPath expression does use namespaces. It would match the following payload:
<xq:employees xmlns:xq="http://www.example.com/employees"> <employee> <name>Fred Jones</name> <address location="home"> <street>900 Aurora Ave.</street> <city>Seattle</city> <state>WA</state> <zip>98115</zip> </address> <address location="work"> <street>2011 152nd Avenue NE</street> <city>Redmond</city> <state>WA</state> <zip>98052</zip> </address> <phone location="work">(425)555-5665</phone> <phone location="home">(206)555-5555</phone> <phone location="mobile">(206)555-4321</phone> </employee> </xq:employees>
Note the different namespace used in the second example.
To learn more about XPath we suggest the following resources: