engine

package module
v1.0.0-beta.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2021 License: Apache-2.0 Imports: 26 Imported by: 2

README

Coraza Web Application Firewall

Build Status CodeQL Maintainability Rating Coverage GoDoc

Welcome to Coraza Web Application Firewall, this project is a Golang port of ModSecurity with the goal to become the first enterprise-grade Open Source Web Application Firewall, flexible and powerful enough to serve as the baseline for many projects.

Prerequisites

  • Linux distribution (Debian and Centos are recommended, Windows is not supported)
  • Golang compiler v1.16+
Optional Prerequisites

In this Coraza version, you can set CGO_ENABLED to 1 or 0, if you set it to 1, you will be required to link libinjection and libpcre to enable PCRE expressions, @detectSQLi and @detectXSS, if you set it to 0 you won't need any dynamic library but your implementation won't support @detectSQLi, @detectXSS nor PCRE expressions, which means OWASP CRS won't work.

Future versions of Coraza will fully remove CGO.

CGO Enabled CGO Disabled
@detectSQLi Yes No
@detectXSS Yes No
PCRE regex Yes No
RE2 regex Yes Yes
OWASP CRS Yes No

If you want to install Coraza with CGO support, you will need:

  • libpcre-dev (apt install libpcre++-dev for Ubuntu)
  • CGO_ENABLED environmental variable must be set to 1
  • libinjection must be installed and linked

Running the test suite

Run the go tests:

go test ./...
go test -race ./...
Run the test suite against OWASP CRS

You can run the testsuite using our OWASP CRS test docker image, it will run a Coraza instance using Caddy and go-ftw

git clone https://github.com/jptosso/coraza-ruleset
cd coraza-ruleset
docker build . -t crs
docker run crs -name crs

Your first Coraza WAF project

Make sure CGO_ENABLED=1 env is set before compiling and all dependencies are met.

package main
import(
	"fmt"
	engine"github.com/jptosso/coraza-waf"
	"github.com/jptosso/coraza-waf/seclang"
)

func main() {
	// First we initialize our waf and our seclang parser
	waf := engine.NewWaf()
	parser := seclang.NewParser(waf)

	// Now we parse our rules
	parser.FromString(`SecRule REMOTE_ADDR "@rx .*" "id:1,phase:1,drop"`)

	// Then we create a transaction and assign some variables
	tx := waf.NewTransaction()
	tx.ProcessConnection("127.0.0.1", 8080, "127.0.0.1", 12345)

	tx.ProcessRequestHeaders()

	// Finally we check the transaction status
	if tx.Interrupted() {
		fmt.Println("Transaction was interrupted")
	}
}
Integrate with any framework

Using the standard net/http library:

package main
import(
	engine"github.com/jptosso/coraza-waf"
	"github.com/jptosso/coraza-waf/seclang"
	"net/http"
)

func SomeErrorPage(w http.ResponseWriter) {
	w.WriteHeader(403)
	w.Write([]byte("WAF ERROR")
}

func someHandler(waf *engine.Waf) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    tx := waf.NewTransaction()
	tx.ProcessRequest(r)
	if tx.Interruption != nil {
		SomeErrorPage(w)
	}
  })
}

Responses are harder to handle, because we must intercept the response writers and integrate them with the Coraza BodyReader.

Handling HTTP responses with Coraza

Responses are usually long buffers, so duplicating the response or buffering it in memory is hard. In order to avoid issues while handling long buffers Coraza provides the engine.BodyReader struct, it will handle long buffers storing them to temporary files if needed.

func someHandler(waf *engine.Waf) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tx := waf.NewTransaction()
		tx.ProcessRequest(r)
		if tx.Interruption != nil {
			SomeErrorPage(w)
		}
		// We will use the Coraza response reader:
		tx.ProcessResponseHeaders()
		tx.ResponseBuffer.Write([]byte("Some of the response body"))
		tx.ProcessResponseBody()
		// We will dump the buffered response into the response writer:
		io.Copy(w, tx.ResponseBuffer)
	})
}

Compatibility status

We have currently achieved a 91% compatibility with OWASP CRS, some features are under development, like:

  • Persistent Collections
  • Some operators: fuzzyHash
  • Lua is still being tested, it may be replaced with WASM

Why Coraza WAF?

Philosophy
  • Simplicity: Anyone should be able to understand and modify Coraza WAF's source code
  • Extensibility: It should be easy to extend Coraza WAF with new functionalities
  • Innovation: Coraza WAF isn't just a ModSecurity port, it must include awesome new functions (in the meantime it's just a port 😅)
  • Community: Coraza WAF is a community project and everyone's idea will be heard
Roadmap (long term)
  • WASM scripts support, Lua was removed
  • Performance improvements
  • More tests and documentation
  • Integrated DDOS protection and directives with iptables(And others) integration
  • Integrated protocol validations (rfc2616)
  • Integrated CSRF protection
  • Integrated bot detection with captcha
  • More loggers and persistence engines
  • More integrations (traefik, gin and buffalo)
  • Open Policy Agent package (OPA)
  • Online sandbox
  • HTTP/2 and HTTP/3 support
  • Enhanced rule profiling
  • Native antivirus integration (maybe)
  • Automatic coreruleset integration (download and setup) (maybe)
  • Enhanced data masking features
  • Enhanced data signing features (cookies, forms, etc)
  • OpenAPI enforcement
  • JWT enforcement
  • JSON and YAML query

Coraza WAF implementations

Some useful tools

Troubleshooting

How to contribute

Contributions are welcome, there are so many TODOs, also functionalities, fixes, bug reports and any help you can provide. Just send your PR.

cd /path/to/coraza
egrep -Rin "TODO|FIXME" -R --exclude-dir=vendor *

Special thanks

  • Modsecurity team for creating SecLang
  • OWASP Coreruleset team for the CRS and their feedback

About

The name Coraza is trademarked, Coraza is a registered trademark of Juan Pablo Tosso.

Documentation

Overview

Copyright 2021 Juan Pablo Tosso

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

View Source
const (
	ACTION_TYPE_METADATA      = 1
	ACTION_TYPE_DISRUPTIVE    = 2
	ACTION_TYPE_DATA          = 3
	ACTION_TYPE_NONDISRUPTIVE = 4
	ACTION_TYPE_FLOW          = 5

	ACTION_DISRUPTIVE_PASS     = 0
	ACTION_DISRUPTIVE_DROP     = 1
	ACTION_DISRUPTIVE_BLOCK    = 2
	ACTION_DISRUPTIVE_DENY     = 3
	ACTION_DISRUPTIVE_ALLOW    = 4
	ACTION_DISRUPTIVE_PROXY    = 5
	ACTION_DISRUPTIVE_REDIRECT = 6
)
View Source
const (
	// Single valua variables
	VARIABLE_URLENCODED_ERROR                 = 0x00
	VARIABLE_RESPONSE_CONTENT_TYPE            = 0x01
	VARIABLE_UNIQUE_ID                        = 0x02
	VARIABLE_ARGS_COMBINED_SIZE               = 0x03
	VARIABLE_AUTH_TYPE                        = 0x04
	VARIABLE_FILES_COMBINED_SIZE              = 0x05
	VARIABLE_FULL_REQUEST                     = 0x06
	VARIABLE_FULL_REQUEST_LENGTH              = 0x07
	VARIABLE_INBOUND_DATA_ERROR               = 0x08
	VARIABLE_MATCHED_VAR                      = 0x09
	VARIABLE_MATCHED_VAR_NAME                 = 0x0A
	VARIABLE_MULTIPART_BOUNDARY_QUOTED        = 0x0B
	VARIABLE_MULTIPART_BOUNDARY_WHITESPACE    = 0x0C
	VARIABLE_MULTIPART_CRLF_LF_LINES          = 0x0D
	VARIABLE_MULTIPART_DATA_AFTER             = 0x0E
	VARIABLE_MULTIPART_DATA_BEFORE            = 0x0F
	VARIABLE_MULTIPART_FILE_LIMIT_EXCEEDED    = 0x10
	VARIABLE_MULTIPART_HEADER_FOLDING         = 0x11
	VARIABLE_MULTIPART_INVALID_HEADER_FOLDING = 0x12
	VARIABLE_MULTIPART_INVALID_PART           = 0x13
	VARIABLE_MULTIPART_INVALID_QUOTING        = 0x14
	VARIABLE_MULTIPART_LF_LINE                = 0x15
	VARIABLE_MULTIPART_MISSING_SEMICOLON      = 0x16
	VARIABLE_MULTIPART_STRICT_ERROR           = 0x17
	VARIABLE_MULTIPART_UNMATCHED_BOUNDARY     = 0x18
	VARIABLE_OUTBOUND_DATA_ERROR              = 0x19
	VARIABLE_PATH_INFO                        = 0x1A
	VARIABLE_QUERY_STRING                     = 0x1B
	VARIABLE_REMOTE_ADDR                      = 0x1C
	VARIABLE_REMOTE_HOST                      = 0x1D
	VARIABLE_REMOTE_PORT                      = 0x1E
	VARIABLE_REQBODY_ERROR                    = 0x1F
	VARIABLE_REQBODY_ERROR_MSG                = 0x20
	VARIABLE_REQBODY_PROCESSOR_ERROR          = 0x21
	VARIABLE_REQBODY_PROCESSOR_ERROR_MSG      = 0x22
	VARIABLE_REQBODY_PROCESSOR                = 0x23
	VARIABLE_REQUEST_BASENAME                 = 0x24
	VARIABLE_REQUEST_BODY                     = 0x25
	VARIABLE_REQUEST_BODY_LENGTH              = 0x26
	VARIABLE_REQUEST_FILENAME                 = 0x27
	VARIABLE_REQUEST_LINE                     = 0x28
	VARIABLE_REQUEST_METHOD                   = 0x29
	VARIABLE_REQUEST_PROTOCOL                 = 0x2A
	VARIABLE_REQUEST_URI                      = 0x2B
	VARIABLE_REQUEST_URI_RAW                  = 0x2C
	VARIABLE_RESPONSE_BODY                    = 0x2D
	VARIABLE_RESPONSE_CONTENT_LENGTH          = 0x2E
	VARIABLE_RESPONSE_PROTOCOL                = 0x2F
	VARIABLE_RESPONSE_STATUS                  = 0x30
	VARIABLE_SERVER_ADDR                      = 0x31
	VARIABLE_SERVER_NAME                      = 0x32
	VARIABLE_SERVER_PORT                      = 0x33
	VARIABLE_SESSIONID                        = 0x34

	// Set Variables
	VARIABLE_RESPONSE_HEADERS_NAMES = 0x35
	VARIABLE_REQUEST_HEADERS_NAMES  = 0x36
	VARIABLE_USERID                 = 0x37
	VARIABLE_ARGS                   = 0x38
	VARIABLE_ARGS_GET               = 0x39
	VARIABLE_ARGS_POST              = 0x3A
	VARIABLE_FILES_SIZES            = 0x3B
	VARIABLE_FILES_NAMES            = 0x3C
	VARIABLE_FILES_TMP_CONTENT      = 0x3D
	VARIABLE_MULTIPART_FILENAME     = 0x3E
	VARIABLE_MULTIPART_NAME         = 0x3F
	VARIABLE_MATCHED_VARS_NAMES     = 0x40
	VARIABLE_MATCHED_VARS           = 0x41
	VARIABLE_FILES                  = 0x42
	VARIABLE_REQUEST_COOKIES        = 0x43
	VARIABLE_REQUEST_HEADERS        = 0x44
	VARIABLE_RESPONSE_HEADERS       = 0x45
	VARIABLE_GEO                    = 0x46
	VARIABLE_REQUEST_COOKIES_NAMES  = 0x47
	VARIABLE_FILES_TMPNAMES         = 0x48
	VARIABLE_ARGS_NAMES             = 0x49
	VARIABLE_ARGS_GET_NAMES         = 0x4A
	VARIABLE_ARGS_POST_NAMES        = 0x4B
	VARIABLE_TX                     = 0x4C

	// Persistent collections
	VARIABLE_GLOBAL   = 0x4D
	VARIABLE_IP       = 0x4E
	VARIABLE_SESSION  = 0x4F
	VARIABLE_USER     = 0x50
	VARIABLE_RESOURCE = 0x51

	VARIABLE_RULE               = 0x52 //TODO FIX
	VARIABLE_XML                = 0x53 //TODO FIX
	VARIABLE_JSON               = 0x54 //TODO FIX
	VARIABLE_INBOUND_ERROR_DATA = 0x55 //TODO FIX
	VARIABLE_DURATION           = 0x56 //TODO FIX
	VARIABLE_URI_PARSE_ERROR    = 0x57
)
View Source
const (
	CONN_ENGINE_OFF        = 0
	CONN_ENGINE_ON         = 1
	CONN_ENGINE_DETECTONLY = 2

	AUDIT_LOG_ENABLED  = 0
	AUDIT_LOG_DISABLED = 1
	AUDIT_LOG_RELEVANT = 2

	REQUEST_BODY_PROCESSOR_DEFAULT    = 0
	REQUEST_BODY_PROCESSOR_URLENCODED = 1
	REQUEST_BODY_PROCESSOR_XML        = 2
	REQUEST_BODY_PROCESSOR_JSON       = 3
	REQUEST_BODY_PROCESSOR_MULTIPART  = 4

	REQUEST_BODY_LIMIT_ACTION_PROCESS_PARTIAL = 0
	REQUEST_BODY_LIMIT_ACTION_REJECT          = 1

	RULE_ENGINE_ON         = 0
	RULE_ENGINE_DETECTONLY = 1
	RULE_ENGINE_OFF        = 2
)
View Source
const VARIABLES_COUNT = 89

Variables

This section is empty.

Functions

func NameToVariable

func NameToVariable(name string) (byte, error)

NameToVariable returns the byte interpretation of a variable from a string Returns error if there is no representation

func VariableToName

func VariableToName(v byte) string

VariableToName transforms a VARIABLE representation into a string, it's used for audit and logging

Types

type Action

type Action interface {
	// Initializes an action, will be done during compilation
	Init(*Rule, string) error
	// Evaluate will be done during rule evaluation
	Evaluate(*Rule, *Transaction)
	// Type will return the rule type, it's used by Evaluate
	// to choose when to evaluate each action
	Type() int
}

This interface is used by this rule's actions

type BodyBuffer

type BodyBuffer struct {
	io.Writer //OK?
	// contains filtered or unexported fields
}

BoddyReader is used to read RequestBody and ResponseBody objects It will handle memory usage for buffering and processing

func NewBodyReader

func NewBodyReader(tmpDir string, memLimit int64) *BodyBuffer

NewBodyReader Initializes a body reader After writing memLimit bytes to the memory buffer, data will be written to a temporary file Temporary files will be written to tmpDir

func (*BodyBuffer) Close

func (br *BodyBuffer) Close()

Close will close all readers and delete temporary files

func (*BodyBuffer) Reader

func (br *BodyBuffer) Reader() io.Reader

Reader Returns a working reader for the body buffer in memory or file

func (*BodyBuffer) Size

func (br *BodyBuffer) Size() int64

Size returns the current size of the body buffer

func (*BodyBuffer) String

func (br *BodyBuffer) String() string

String returns a string with the whole body buffer In some cases it will be needed for body processing

func (*BodyBuffer) Write

func (br *BodyBuffer) Write(data []byte) (n int, err error)

Write appends data to the body buffer by chunks You may dump io.Readers using io.Copy(br, reader)

type Collection

type Collection struct {

	// The key used to store the collection if it must persist
	PersistenceKey string
	// contains filtered or unexported fields
}

Collections are used to store VARIABLE data for transactions, this data structured is designed to store slices of data for keys Important: Collections ARE NOT concurrent safe

func NewCollection

func NewCollection(name string) *Collection

Creates a new collection

func (*Collection) Add

func (c *Collection) Add(key string, value string)

Add a value to some key

func (*Collection) AddUnique

func (c *Collection) AddUnique(key string, value string)

AddUnique will add a value to a key if it is not already there

func (*Collection) Data

func (c *Collection) Data() map[string][]string

Data returns the stored data

func (*Collection) Find

func (c *Collection) Find(key string, re *regex.Regexp, exceptions []string) []*MatchData

Find is returns a slice of MatchData for the regex or key, exceptions are used to skip some keys

func (*Collection) Get

func (c *Collection) Get(key string) []string

Get returns a slice of strings for a key

func (*Collection) GetFirstInt

func (c *Collection) GetFirstInt(key string) int

GetFirstInt returns the first int ocurrence of a key

func (*Collection) GetFirstInt64

func (c *Collection) GetFirstInt64(key string) int64

GetFirstInt64 returns the first int64 ocurrence of a key

func (*Collection) GetFirstString

func (c *Collection) GetFirstString(key string) string

GetFirstString returns the first string ocurrence of a key

func (*Collection) Name

func (c *Collection) Name() string

Name returns the name for the current collection

func (*Collection) Remove

func (c *Collection) Remove(key string)

Remove deletes the key from the collection

func (*Collection) Reset

func (c *Collection) Reset()

Reset the current collection

func (*Collection) Set

func (c *Collection) Set(key string, value []string)

Set will replace the key's value with this slice

func (*Collection) SetData

func (c *Collection) SetData(data map[string][]string)

SetData replaces the data map with something else Useful for persistent collections

type Interruption

type Interruption struct {
	// Rule that caused the interruption
	RuleId int

	// drop, deny, redirect
	Action string

	// Force this status code
	Status int

	// Parameters used by proxy and redirect
	Data string
}

type MatchData

type MatchData struct {
	// Variable name as a string
	Collection string
	// Key of the variable, blank if no key is required
	Key string
	// Value of the current VARIABLE:KEY
	Value string
}

MatchData works like VariableKey but is used for logging so it contains the collection as a string and it's value

type MatchedRule

type MatchedRule struct {
	// A single rule may contain multiple messages from chains
	Messages []string
	// A slice of matched variables
	MatchedData []*MatchData
	// A pointer to the triggered rule
	Rule *Rule
}

MatchedRule contains a list of macro expanded messages, matched variables and a pointer to the rule

type Operator

type Operator interface {
	// Init is used during compilation to setup and cache
	// the operator
	Init(string) error
	// Evaluate is used during the rule evaluation,
	// it returns true if the operator succeeded against
	// the input data for the transaction
	Evaluate(*Transaction, string) bool
}

Operator interface is used to define rule @operators

type Rule

type Rule struct {
	// Contains a list of variables that will be compiled
	// by a transaction
	Variables []RuleVariable

	// Contains a pointer to the Operator struct used
	// SecActions and SecMark can have nil Operators
	Operator *RuleOperator

	// List of transformations to be evaluated
	// In the future, transformations might be run by the
	// action itself
	Transformations []transformations.Transformation

	// Contains the Id of the parent rule if you are inside
	// a chain. Otherwise it will be 0
	ParentId int

	// Slice of initialized actions to be evaluated during
	// the rule evaluation process
	Actions []Action

	// Used to mark a rule as a secmarker and alter flows
	SecMark string

	// Contains the raw rule code
	Raw string

	// Contains the child rule to chain, nil if there are no chains
	Chain *Rule

	// Contains the disruptive action, it does nothing and might be
	// removed in future versions
	DisruptiveAction int

	// Used by the chain action to indicate if the next rule is chained
	// to this one, it's only used for compilation
	HasChain bool

	// If true, this rule will always match and won't run it's operator
	AlwaysMatch bool

	// Where is this rule stored
	File string

	// Line of the file where this rule was found
	Line int

	//METADATA
	// Rule unique sorted identifier
	Id int

	// Rule tag list
	Tags []string

	// Rule execution phase 1-5
	Phase int

	// Message text to be macro expanded and logged
	Msg string

	// Rule revision value
	Rev string

	// Rule maturity index
	Maturity int

	// RuleSet Version
	Version string

	// Rule accuracy
	Accuracy int

	// Rule severity
	Severity int

	// Rule logdata
	LogData string

	// If true and this rule is matched, this rule will be
	// written to the audit log
	// If no auditlog, this rule won't be logged
	Log bool

	// If true, the transformations will be multimatched
	MultiMatch bool
}

Rule is used to test a Transaction against certain operators and execute actions

func NewRule

func NewRule() *Rule

NewRule returns a new initialized rule

func (*Rule) AddVariable

func (r *Rule) AddVariable(count bool, negation bool, collection byte, key string, regexkey bool) error

AddsVariable appends a new variable to the rule, it will precompile regular expressions and transforma the variable name to it's byte form

func (*Rule) Evaluate

func (r *Rule) Evaluate(tx *Transaction) []*MatchData

Evaluate will evaluate the current rule for the indicated transaction If the operator matches, actions will be evaluated and it will return the matched variables, keys and values (MatchData)

type RuleGroup

type RuleGroup struct {
	// contains filtered or unexported fields
}

func NewRuleGroup

func NewRuleGroup() *RuleGroup

func (*RuleGroup) Add

func (rg *RuleGroup) Add(rule *Rule) error

Adds a rule to the collection Will return an error if the ID is already used

func (*RuleGroup) Clear

func (rg *RuleGroup) Clear()

Clear will remove each and every rule stored

func (*RuleGroup) Count

func (rg *RuleGroup) Count() int

Count returns the count of rules

func (*RuleGroup) DeleteById

func (rg *RuleGroup) DeleteById(id int)

DeleteById removes a rule by it's Id

func (*RuleGroup) Evaluate

func (rg *RuleGroup) Evaluate(phase int, tx *Transaction) bool

Evaluate rules for the specified phase, between 1 and 5 Returns true if transaction is disrupted

func (*RuleGroup) FindById

func (rg *RuleGroup) FindById(id int) *Rule

FindById return a Rule with the requested Id

func (*RuleGroup) FindByMsg

func (rg *RuleGroup) FindByMsg(msg string) []*Rule

FindByMsg returns a slice of rules that matches the msg

func (*RuleGroup) FindByTag

func (rg *RuleGroup) FindByTag(tag string) []*Rule

FindByTag returns a slice of rules that matches the tag

func (*RuleGroup) GetRules

func (rg *RuleGroup) GetRules() []*Rule

GetRules returns the slice of rules, it's concurrent safe.

type RuleOperator

type RuleOperator struct {
	// Operator to be used
	Operator Operator
	// Data to initialize the operator
	Data string
	// If true, rule will match if op.Evaluate returns false
	Negation bool
}

RuleOperator is a container for an operator,

type RuleVariable

type RuleVariable struct {
	// If true, the count of results will be returned
	Count bool

	// The VARIABLE that will be requested
	Collection byte

	// The key for the variable that is going to be requested
	Key string

	// If not nil, a regex will be used instead of a key
	Regex *regex.Regexp //for performance

	// A slice of key exceptions
	Exceptions []string
}

RuleVariable is compiled during runtime by transactions to get values from the transaction's variables It supports xml, regex, exceptions and many more features

type Transaction

type Transaction struct {
	// If true the transaction is going to be logged, it won't log if IsRelevantStatus() fails
	Log bool

	//Transaction Id
	Id string

	// Contains the list of matched rules and associated match information
	MatchedRules []*MatchedRule

	//True if the transaction has been disrupted by any rule
	Interruption *Interruption

	//Response data to be sent
	Status int `json:"status"`

	// This is used to store log messages
	// TODO check how we are going to reuse it for logging
	Logdata []string `json:"logdata"`

	// Rules will be skipped after a rule with this SecMarker is found
	SkipAfter string

	// Copies from the WafInstance that may be overwritten by the ctl action
	AuditEngine              int
	AuditLogParts            []rune
	ForceRequestBodyVariable bool
	RequestBodyAccess        bool
	RequestBodyLimit         int64
	RequestBodyProcessor     int
	ResponseBodyAccess       bool
	ResponseBodyLimit        int64
	RuleEngine               int
	HashEngine               bool
	HashEnforcement          bool

	// Stores the last phase that was evaluated
	// Used by allow to skip phases
	LastPhase int

	// Handles request body buffers
	RequestBodyBuffer *BodyBuffer

	// Handles response body buffers
	ResponseBodyBuffer *BodyBuffer

	// Rules with this id are going to be skipped while processing a phase
	RuleRemoveById []int

	// Used by ctl to remove rule targets by id during the transaction
	// All other "target removers" like "ByTag" are an abstraction of "ById"
	// For example, if you want to remove REQUEST_HEADERS:User-Agent from rule 85:
	// {85: {VARIABLE_REQUEST_HEADERS, "user-agent"}}
	RuleRemoveTargetById map[int][]*VariableKey

	// Will skip this number of rules, this value will be decreased on each skip
	Skip int

	// Actions with capture features will read the capture state from this field
	// We have currently removed this feature as Capture will always run
	// We must reuse it in the future
	Capture bool

	// Contains duration in useconds per phase
	StopWatches map[int]int

	// Contains de *engine.Waf instance for the current transaction
	Waf *Waf

	// In case of an XML request body we will cache the XML object here
	XmlDoc *xmlquery.Node

	// Timestamp of the request
	Timestamp int64
	// contains filtered or unexported fields
}

func (*Transaction) AddArgument

func (tx *Transaction) AddArgument(orig string, key string, value string)

AddArgument Add arguments GET or POST This will set ARGS_(GET|POST), ARGS, ARGS_NAMES, ARGS_COMBINED_SIZE and ARGS_(GET|POST)_NAMES

func (*Transaction) AddRequestHeader

func (tx *Transaction) AddRequestHeader(key string, value string)

AddRequestHeader Adds a request header

With this method it is possible to feed Coraza with a request header. Note: Golang's *http.Request object will not contain a "Host" header and you might have to force it

func (*Transaction) AddResponseHeader

func (tx *Transaction) AddResponseHeader(key string, value string)

AddResponseHeader Adds a response header variable

With this method it is possible to feed Coraza with a response header.

func (*Transaction) AuditLog

func (tx *Transaction) AuditLog() *loggers.AuditLog

AuditLog returns an AuditLog struct, used to write audit logs

func (*Transaction) CaptureField

func (tx *Transaction) CaptureField(index int, value string)

CaptureField is used to set the TX:[index] variables by operators that supports capture, like @rx

func (*Transaction) ExtractArguments

func (tx *Transaction) ExtractArguments(orig string, uri string)

ExtractArguments transforms an url encoded string to a map and creates ARGS_POST|GET

func (*Transaction) GetCollection

func (tx *Transaction) GetCollection(variable byte) *Collection

GetCollection transforms a VARIABLE_ constant into a *Collection used to get VARIABLES data

func (*Transaction) GetCollections

func (tx *Transaction) GetCollections() map[string]*Collection

GetCollections is used to debug collections, it maps the Collection slice into a map of variable names and collections

func (*Transaction) GetField

func (tx *Transaction) GetField(rv RuleVariable, exceptions []string) []*MatchData

GetField Retrieve data from collections applying exceptions This function will apply xpath if the variable is XML In future releases we may remove de exceptions slice and make it easier to use

func (*Transaction) GetStopWatch

func (tx *Transaction) GetStopWatch() string

GetStopWatch is used to debug phase durations Normally it should be named StopWatch() but it would be confusing

func (*Transaction) Interrupted

func (tx *Transaction) Interrupted() bool

Interrupted will return true if the transaction was interrupted

func (*Transaction) IsProcessableResponseBody

func (tx *Transaction) IsProcessableResponseBody() bool

IsProcessableRequestBody returns true if the response body meets the criteria to be processed, response headers must be set before this. The content-type response header must be in the SecRequestBodyMime This is used by webservers to choose whether tostream response buffers directly to the client or write them to Coraza

func (*Transaction) MacroExpansion

func (tx *Transaction) MacroExpansion(data string) string

MacroExpansion expands a string that contains %{somevalue.some-key} into it's first value, for example:

	v1 := tx.MacroExpansion("%{request_headers.user-agent")
 v2 := tx.GetCollection(VARIABLE_REQUEST_HEADERS).GetFirstString("user-agent")
 v1 == v2 // returns true

Important: this function is case insensitive

func (*Transaction) MatchRule

func (tx *Transaction) MatchRule(rule *Rule, msgs []string, match []*MatchData)

MatchRule Matches a rule to be logged

func (*Transaction) MatchVars

func (tx *Transaction) MatchVars(match []*MatchData)

MatchVars Creates the MATCHED_ variables required by chains and macro expansion MATCHED_VARS, MATCHED_VAR, MATCHED_VAR_NAME, MATCHED_VARS_NAMES

func (*Transaction) ParseRequestReader

func (tx *Transaction) ParseRequestReader(data io.Reader) (*Interruption, error)

ParseRequestReader Parses binary request including body, it does only supports http/1.1 and http/1.0 This function does not run ProcessConnection This function will store in memory the whole reader, DON't USE IT FOR PRODUCTION yet

func (*Transaction) ProcessConnection

func (tx *Transaction) ProcessConnection(client string, cPort int, server string, sPort int)

ProcessConnection should be called at very beginning of a request process, it is expected to be executed prior to the virtual host resolution, when the connection arrives on the server. Important: Remember to check for a possible intervention.

func (*Transaction) ProcessLogging

func (tx *Transaction) ProcessLogging()

ProcessLogging Logging all information relative to this transaction.

At this point there is not need to hold the connection, the response can be delivered prior to the execution of this method.

func (*Transaction) ProcessRequest

func (tx *Transaction) ProcessRequest(req *http.Request) (*Interruption, error)

ProcessRequest Fill all transaction variables from an http.Request object Most implementations of Coraza will probably use http.Request objects so this will implement all phase 0, 1 and 2 variables Note: This function will stop after an interruption Note: Do not manually fill any request variables

func (*Transaction) ProcessRequestBody

func (tx *Transaction) ProcessRequestBody() (*Interruption, error)

ProcessRequestBody Performs the request body (if any)

This method perform the analysis on the request body. It is optional to call that function. If this API consumer already know that there isn't a body for inspect it is recommended to skip this step.

Remember to check for a possible intervention.

func (*Transaction) ProcessRequestHeaders

func (tx *Transaction) ProcessRequestHeaders() *Interruption

ProcessRequestHeaders Performs the analysis on the request readers.

This method perform the analysis on the request headers, notice however that the headers should be added prior to the execution of this function.

note: Remember to check for a possible intervention.

func (*Transaction) ProcessResponseBody

func (tx *Transaction) ProcessResponseBody() (*Interruption, error)

ProcessResponseBody Perform the request body (if any)

This method perform the analysis on the request body. It is optional to call that method. If this API consumer already know that there isn't a body for inspect it is recommended to skip this step.

note Remember to check for a possible intervention.

func (*Transaction) ProcessResponseHeaders

func (tx *Transaction) ProcessResponseHeaders(code int, proto string) *Interruption

ProcessResponseHeaders Perform the analysis on the response readers.

This method perform the analysis on the response headers, notice however that the headers should be added prior to the execution of this function.

note: Remember to check for a possible intervention.

func (*Transaction) ProcessUri

func (tx *Transaction) ProcessUri(uri string, method string, httpVersion string)

ProcessUri Performs the analysis on the URI and all the query string variables. This method should be called at very beginning of a request process, it is expected to be executed prior to the virtual host resolution, when the connection arrives on the server. note: There is no direct connection between this function and any phase of

the SecLanguages phases. It is something that may occur between the
SecLanguage phase 1 and 2.

note: This function won't add GET arguments, they must be added with AddArgument

func (*Transaction) RemoveRuleTargetById

func (tx *Transaction) RemoveRuleTargetById(id int, col byte, key string)

RemoveRuleTargetById Removes the VARIABLE:KEY from the rule ID It's mostly used by CTL to dinamically remove targets from rules

func (*Transaction) ResetCapture

func (tx *Transaction) ResetCapture()

ResetCapture Resets the captured variables for further uses Captures variables must be always reset before capturing again

func (*Transaction) SetFullRequest

func (tx *Transaction) SetFullRequest()

SetFullRequest Creates the FULL_REQUEST variable based on every input It's a heavy operation and it's not used by OWASP CRS so it's optional

type VariableKey

type VariableKey struct {
	// Contains the variable
	Collection byte
	// Contains the key of the variable
	Key string
}

VariableKey is used to store Variables with it's key, for example: ARGS:id would be the same as {VARIABLE_ARGS, "id"}

type Waf

type Waf struct {
	// RuleGroup object, contains all rules and helpers
	Rules *RuleGroup

	// Audit mode status
	AuditEngine int

	// Array of logging parts to be used
	AuditLogParts []rune

	// If true, transactions will have access to the request body
	RequestBodyAccess bool

	// Request body page file limit
	RequestBodyLimit int64

	// Request body in memory limit
	RequestBodyInMemoryLimit int64

	// If true, transactions will have access to the response body
	ResponseBodyAccess bool

	// Response body memory limit
	ResponseBodyLimit int64

	// Defines if rules are going to be evaluated
	RuleEngine int

	// If true, transaction will fail if response size is bigger than the page limit
	RejectOnResponseBodyLimit bool

	// If true, transaction will fail if request size is bigger than the page limit
	RejectOnRequestBodyLimit bool

	// Responses will only be loaded if mime is listed here
	ResponseBodyMimeTypes []string

	// Web Application id, apps sharing the same id will share persistent collections
	WebAppId string

	// This signature is going to be reported in audit logs
	ComponentSignature string

	// Contains the regular expression for relevant status audit logging
	AuditLogRelevantStatus regex.Regexp

	// Contains the GeoIP2 database reader object
	GeoDb *geoip2.Reader

	// If true WAF engine will fail when remote rules cannot be loaded
	AbortOnRemoteRulesFail bool

	// Instructs the waf to change the Server response header
	ServerSignature string

	// This directory will be used to store page files
	TmpDir string

	// Persistence engine
	Persistence persistence.Persistence

	// Sensor ID tu, must be unique per cluster nodes
	SensorId string

	// Path to store data files (ex. cache)
	DataDir string

	UploadKeepFiles         bool
	UploadFileMode          fs.FileMode
	UploadFileLimit         int
	UploadDir               string
	RequestBodyNoFilesLimit int64
	CollectionTimeout       int

	// Used to perform unicode mapping, required by t:utf8ToUnicode
	Unicode *utils.Unicode

	RequestBodyLimitAction int

	ArgumentSeparator string

	// Used for the debug logger
	Logger *zap.Logger

	// Used to allow switching the debug level during runtime
	// ctl cannot switch use it as it will update de lvl
	// for the whole Waf instance
	LoggerAtomicLevel zap.AtomicLevel
	// contains filtered or unexported fields
}

Waf instances are used to store configurations and rules Every web application should have a different Waf instance but you can share an instance if you are okwith sharing configurations, rules and logging. Transactions and SecLang parser requires a Waf instance You can use as many Waf instances as you want and they are concurrent safe

func NewWaf

func NewWaf() *Waf

NewWaf creates a new WAF instance with default variables

func (*Waf) AddAuditLogger

func (w *Waf) AddAuditLogger(engine string, args []string) error

AddAuditLogger creates a new logger for the current WAF instance You may add as many loggers as you want Keep in mind loggers may lock go routines

func (*Waf) AuditLoggers

func (w *Waf) AuditLoggers() []loggers.Logger

Logger returns the initiated loggers Coraza supports unlimited loggers, so you can write for example to syslog and a local drive at the same time

func (*Waf) NewTransaction

func (w *Waf) NewTransaction() *Transaction

NewTransaction Creates a new initialized transaction for this WAF instance

func (*Waf) SetGeoip

func (w *Waf) SetGeoip(path string) error

SetGeoip Initializes Geoip2 database

func (*Waf) SetLogLevel

func (w *Waf) SetLogLevel(lvl int) error

SetLogLevel changes the debug level of the Waf instance

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL