diff --git a/internal/actions/allow.go b/internal/actions/allow.go index 28e602952..139c2c827 100644 --- a/internal/actions/allow.go +++ b/internal/actions/allow.go @@ -11,7 +11,31 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) -// 0 nothing, 1 phase, 2 request +// Action Group: Disruptive +// +// Description: +// Stops rule processing on a successful match and allows a transaction to be proceed. +// +// - Using solely: allow will affect the entire transaction. +// stopping processing of the current phase but also skipping over all other phases apart from the logging phase. +// (The logging phase is special; it is designed to be always execute.) +// - Using with parameter `phase`: the engine will stop processing the current phase, and the other phases will continue. +// - Using with parameter `request`: engine will stop processing the current phase, and the next phase to be processed will be phase `types.PhaseResponseHeaders`. +// +// Example: +// ``` +// # Allow unrestricted access from 192.168.1.100 +// SecRule REMOTE_ADDR "^192\.168\.1\.100$" phase:1,id:95,nolog,allow +// +// # Do not process request but process response +// SecAction phase:1,allow:request,id:96 +// +// # Do not process transaction (request and response). +// SecAction phase:1,allow,id:97 +// +// # If you want to allow a response through, put a rule in phase RESPONSE_HEADERS and use allow +// SecAction phase:3,allow,id:98 +// ``` type allowFn struct { allow corazatypes.AllowType } @@ -30,17 +54,6 @@ func (a *allowFn) Init(_ plugintypes.RuleMetadata, data string) error { return nil } -// Evaluate Allow has the following rules: -// -// Example: `SecRule REMOTE_ADDR "^192\.168\.1\.100$" "phase:1,id:95,nolog,allow"` -// -// - If used one its own, like in the example above, allow will affect the entire transaction, -// stopping processing of the current phase but also skipping over all other phases apart from the logging phase. -// (The logging phase is special; it is designed to always execute.) -// - If used with parameter "phase", allow will cause the engine to stop processing the current phase. -// Other phases will continue as normal. -// - If used with parameter "request", allow will cause the engine to stop processing the current phase. -// The next phase to be processed will be phase types.PhaseResponseHeaders. func (a *allowFn) Evaluate(r plugintypes.RuleMetadata, txS plugintypes.TransactionState) { tx := txS.(*corazawaf.Transaction) tx.AllowType = a.allow diff --git a/internal/actions/auditlog.go b/internal/actions/auditlog.go index 00bd1a3bd..d575dd4a5 100644 --- a/internal/actions/auditlog.go +++ b/internal/actions/auditlog.go @@ -8,6 +8,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// Marks the transaction for logging in the audit log. +// +// Example: +// ``` +// # The action is explicit if the log is specified. +// SecRule REMOTE_ADDR "^192\.168\.1\.100$" "auditlog,phase:1,id:100,allow" +// ``` type auditlogFn struct{} func (a *auditlogFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/block.go b/internal/actions/block.go index 9495352fa..cc85438b3 100644 --- a/internal/actions/block.go +++ b/internal/actions/block.go @@ -7,6 +7,45 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +// Action Group: Disruptive +// +// Description: +// Performs the disruptive action defined by the previous `SecDefaultAction`. +// This action is a placeholder to be used by rule writers to request a blocking action, +// but without specifying how the blocking is to be done. +// The idea is that such decisions are best left to rule users, as well as to allow users, to override blocking for their demands. +// In future versions of Coraza, more control and functionality will be added to define "how" to block. +// +// Example: +// ``` +// # Specify how blocking is to be done +// SecDefaultAction "phase:2,deny,id:101,status:403,log,auditlog" +// +// # Detect attacks where we want to block +// SecRule ARGS "@rx attack1" "phase:2,block,id:102" +// +// # Detect attacks where we want only to warn +// SecRule ARGS "@rx attack2" "phase:2,pass,id:103" +// +// # It is possible to use the `SecRuleUpdateActionById` directive to override how a rule handles blocking. +// # This is useful in three cases: +// +// # 1. If a rule has blocking hard-coded, and you want it to use the policy you determine. +// # 2. If a rule was written to `block`, but you want it to warn only. +// # 3. If a rule was written to only `warn`, but you want it to block. +// +// # The following example demonstrates the first case, +// # in which the hard-coded block is removed in favor of the user-controllable block: +// +// # Specify how blocking is to be done +// SecDefaultAction "phase:2,deny,status:403,log,auditlog,id:104" +// +// # Detect attacks and block +// SecRule ARGS "@rx attack1" "phase:2,id:1,deny" +// +// # Change how rule ID 1 blocks +// SecRuleUpdateActionById 1 "block" +// ``` type blockFn struct{} func (a *blockFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/capture.go b/internal/actions/capture.go index ebbd8d2dd..bab58bd00 100644 --- a/internal/actions/capture.go +++ b/internal/actions/capture.go @@ -8,6 +8,22 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// > This action is being forced by now, it might be reused in the future. +// +// When used together with the regular expression operator `@rx`, +// `capture` creates a copy of the regular expression and places them into the transaction variable collection. +// Up to 10 captures will be copied on a successful pattern match, each with a name consisting of a digit from 0 to 9. +// The `TX.0` variable always contains the entire area that the regular expression matched. +// All the other variables contain the captured values, in the order in which the capturing parentheses appear in the regular expression. +// +// Example: +// ``` +// SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain,id:105 +// SecRule TX:1 "(?:(?:a(dmin|nonymous)))" +// ``` type captureFn struct{} func (a *captureFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/chain.go b/internal/actions/chain.go index 3d3adea62..3c254a3d7 100644 --- a/internal/actions/chain.go +++ b/internal/actions/chain.go @@ -8,6 +8,41 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Flow +// +// Description: +// Creating a rule chain - chains the current rule with the rule that immediately follows it. +// +// Noted that rule chains simulate **AND condition**. +// The disruptive actions specified in the first portion of the chained rule will be triggered only if all of the variable checks return positive hits. +// If one of the chained rule is negative, the entire rule chain will fail to match. +// +// These action can be specified only by the chain starter rule: +// - disruptive actions +// - execution phases +// - metadata actions (id, rev, msg, tag, severity, logdata) +// - skip +// - skipAfter +// +// The following directives can be used in rule chains: +// - `SecAction` +// - `SecRule` +// - `SecRuleScript` +// +// Special rules control the usage of actions in a chained rule: +// - An action which affects the rule flow (i.e., the disruptive actions, `skip` and `skipAfter`) can be used only in the chain starter. They will be executed only if the entire chain matches. +// - Non-disruptive rules can be used in any rule; they will be executed if the rule that contains them matches and not only when the entire chain matches. +// - The metadata actions (e.g., `id`, `rev`, `msg`) can be used only in the chain starter. +// +// Example: +// ``` +// # Refuse to accept POST requests that do not contain a Content-Length header. +// # Noted that the rule should be preceded by a rule that verifies only valid request methods are used. +// +// SecRule REQUEST_METHOD "^POST$" "phase:1,chain,t:none,id:105" +// SecRule &REQUEST_HEADERS:Content-Length "@eq 0" "t:none" +// +// ``` type chainFn struct{} func (a *chainFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/ctl.go b/internal/actions/ctl.go index c062d2066..9bb0da00a 100644 --- a/internal/actions/ctl.go +++ b/internal/actions/ctl.go @@ -44,6 +44,62 @@ const ( ctlDebugLogLevel ctlFunctionType = iota ) +// Action Group: Non-disruptive +// +// Description: +// Change Coraza configuration on transient, per-transaction basis. +// Any changes made using this action will affect only the transaction in which the action is executed. +// The default configuration, as well as the other transactions running in parallel, will be unaffected. +// +// The following configuration options are supported: +// - `auditEngine` +// - `auditLogParts` +// - `debugLogLevel` +// - `forceRequestBodyVariable` +// - `requestBodyAccess` +// - `requestBodyLimit` +// - `requestBodyProcessor` +// - `responseBodyAccess` +// - `responseBodyLimit` +// - `ruleEngine` +// - `ruleRemoveById` +// - `ruleRemoveByMsg` +// - `ruleRemoveByTag` +// - `ruleRemoveTargetById` +// - `ruleRemoveTargetByMsg` +// - `ruleRemoveTargetByTag` +// - `hashEngine` (**Not Supported in Coraza (TBI)**) +// - `hashEnforcement` (**Not supported in Coraza (TBI)**) +// +// Here are some notes about the options: +// +// 1. Option `ruleRemoveTargetById`, `ruleRemoveTargetByMsg`, and `ruleRemoveTargetByTag`, users don't need to use the char ! before the target list. +// +// 2. Option `ruleRemoveById` is triggered at run time and should be specified before the rule in which it is disabling. +// +// 3. Option `requestBodyProcessor` allows you to configure the request body processor. +// By default, Coraza will use the `URLENCODED` and `MULTIPART` processors to process an `application/x-www-form-urlencoded` and a `multipart/form-data` body respectively. +// Other processors also supported: `JSON` and `XML`, but they are never used implicitly. +// Instead, you must tell Coraza to use it by placing a few rules in the `REQUEST_HEADERS` processing phase. +// After the request body is processed as XML, you will be able to use the XML-related features to inspect it. +// Request body processors will not interrupt a transaction if an error occurs during parsing. +// Instead, they will set the variables `REQBODY_PROCESSOR_ERROR` and `REQBODY_PROCESSOR_ERROR_MSG`. +// These variables should be inspected in the `REQUEST_BODY` phase and an appropriate action taken. +// +// 4. Option `forceRequestBodyVariable“ allows you to configure the `REQUEST_BODY` variable to be set when there is no request body processor configured. +// This allows for inspection of request bodies of unknown types. +// +// Example: +// ``` +// # Parse requests with Content-Type "text/xml" as XML +// SecRule REQUEST_CONTENT_TYPE ^text/xml "nolog,pass,id:106,ctl:requestBodyProcessor=XML" +// +// # white-list the user parameter for rule #981260 when the REQUEST_URI is /index.php +// +// SecRule REQUEST_URI "@beginsWith /index.php" "phase:1,t:none,pass,\ +// nolog,ctl:ruleRemoveTargetById=981260;ARGS:user" +// +// ``` type ctlFn struct { action ctlFunctionType value string diff --git a/internal/actions/deny.go b/internal/actions/deny.go index f717a7f78..994f35c7d 100644 --- a/internal/actions/deny.go +++ b/internal/actions/deny.go @@ -8,6 +8,15 @@ import ( "github.com/corazawaf/coraza/v3/types" ) +// Action Group: Disruptive +// +// Description: +// Stops rule processing and intercepts transaction. +// +// Example: +// ``` +// SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,id:107,msg:'Nikto Scanners Identified'" +// ``` type denyFn struct{} func (a *denyFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/drop.go b/internal/actions/drop.go index 0184eb26e..828574635 100644 --- a/internal/actions/drop.go +++ b/internal/actions/drop.go @@ -8,6 +8,24 @@ import ( "github.com/corazawaf/coraza/v3/types" ) +// Action Group: Disruptive +// +// Description: +// > This action depends on each implementation, the server is instructed to drop the connection. +// +// Initiates an immediate close of the TCP connection by sending a FIN packet. +// This action is extremely useful when responding to both Brute Force and Denial of Service attacks, +// which you may want to minimize the network bandwidth and the data returned to the client. +// This action causes error message to appear in the log `(9)Bad file descriptor: core_output_filter: writing data to the network` +// +// Example: +// ``` +// # The following example initiates an IP collection for tracking Basic Authentication attempts. +// # If the client exceed the threshold of more than 25 attempts in 2 minutes, it will `DROP` the subsequent connections. +// SecAction phase:1,id:109,initcol:ip=%{REMOTE_ADDR},nolog +// SecRule ARGS:login "!^$" "nolog,phase:1,id:110,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=25/120" +// SecRule IP:AUTH_ATTEMPT "@gt 25" "log,drop,phase:1,id:111,msg:'Possible Brute Force Attack'" +// ``` type dropFn struct{} func (a *dropFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/exec.go b/internal/actions/exec.go index 9464bff29..487b36b74 100644 --- a/internal/actions/exec.go +++ b/internal/actions/exec.go @@ -7,6 +7,28 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +// Action Group: Non-disruptive +// +// Description: +// Executes an external script/binary supplied as parameter. +// The `exec` action is executed independently from any disruptive actions specified. +// External scripts will always be called with no parameters. +// Some transaction information will be placed in environment variables. +// All the usual CGI environment variables will be there. +// You should be aware that forking a threaded process results in all threads being replicated in the new process. +// Forking can therefore incur larger overhead in a multithreaded deployment. +// +// > The script you execute must write something (anything) to stdout, +// > if it doesn’t, Coraza will assume that the script failed, and will record the failure. +// +// Example: +// ``` +// # Run external program on rule match +// SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:112,t:none,t:lowercase,t:normalizePath,block,\ exec:/usr/local/apache/bin/test.sh" + +// # Run Lua script on rule match +// SecRule ARGS:p attack "phase:2,id:113,block,exec:/usr/local/apache/conf/exec.lua" +// ``` type execFn struct{} func (a *execFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/expirevar.go b/internal/actions/expirevar.go index a05d76623..e09462f62 100644 --- a/internal/actions/expirevar.go +++ b/internal/actions/expirevar.go @@ -7,6 +7,22 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) +// Action Group: Non-disruptive +// +// Description: +// Configures a collection variable to expire after the given time period (in seconds). +// You should use the `expirevar` with `setvar` action to keep the intended expiration time. +// The expire time will be reset if they are used on their own (perhaps in a SecAction directive). +// +// Example: +// ``` +// +// SecRule REQUEST_COOKIES:JSESSIONID "!^$" "nolog,phase:1,id:114,pass,setsid:%{REQUEST_COOKIES:JSESSIONID}" +// +// SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:115,t:none,t:lowercase,t:normalizePath,log,allow,\ +// setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" +// +// ``` type expirevarFn struct{} func (a *expirevarFn) Init(_ plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/id.go b/internal/actions/id.go index 77f5ac650..4dcd80266 100644 --- a/internal/actions/id.go +++ b/internal/actions/id.go @@ -11,6 +11,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Metadata +// +// Description: +// > This action is mandatory for all `SecRule` and `SecAction`, and it must be numeric. +// Assigns a unique ID to the rule or chain in which it appears. +// +// Example: +// ``` +// SecRule &REQUEST_HEADERS:Host "@eq 0" "log,id:60008,severity:2,msg:'Request Missing a Host Header'" +// ``` type idFn struct{} func (a *idFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/initcol.go b/internal/actions/initcol.go index 12163f2d2..4899d7213 100644 --- a/internal/actions/initcol.go +++ b/internal/actions/initcol.go @@ -9,7 +9,19 @@ import ( "github.com/corazawaf/coraza/v3/experimental/plugins/plugintypes" ) -// Initializes a persistent collection and add the data to the standard collections coraza. +// Action Group: Non-disruptive +// +// Description: +// Initializes a named persistent collection, either by loading data from storage or by creating a new collection in memory. +// Collections are loaded into memory on-demand, when the initcol action is executed. +// A collection will be persisted only if a change was made to it in the course of transaction processing. +// See the `Persistent Storage` section for further details. +// +// Example: +// ``` +// # Initiates IP address tracking, which is best done in phase 1 +// SecAction "phase:1,id:116,nolog,pass,initcol:ip=%{REMOTE_ADDR}" +// ``` type initcolFn struct { collection string variable byte diff --git a/internal/actions/log.go b/internal/actions/log.go index 6b41edd40..ba3297a11 100644 --- a/internal/actions/log.go +++ b/internal/actions/log.go @@ -8,6 +8,16 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// Indicates that a successful match of the rule needs to be logged. +// +// Example: +// ``` +// # log matches from the error log file to the Coraza audit log. +// SecAction "phase:1,id:117,pass,initcol:ip=%{REMOTE_ADDR},log" +// ``` type logFn struct{} func (a *logFn) Init(r plugintypes.RuleMetadata, data string) error { diff --git a/internal/actions/logdata.go b/internal/actions/logdata.go index 1928d9bf2..03a602d74 100644 --- a/internal/actions/logdata.go +++ b/internal/actions/logdata.go @@ -9,6 +9,18 @@ import ( "github.com/corazawaf/coraza/v3/internal/corazawaf" ) +// Action Group: Non-disruptive +// +// Description: +// Logs a data fragment as part of the alert message. +// The logdata information appears in the error and/or audit log files. Macro expansion is performed, +// so you may use variable names such as %{TX.0} or %{MATCHED_VAR}. +// The information is properly escaped for use with logging of binary data. +// +// Example: +// ``` +// SecRule ARGS:p "@rx