URL Plugin¶
Namespace: \Kanopi\Firewall\Plugins\Url
Evaluates requests based on URL components and request parameters.
Configuration Example¶
plugins:
- plugin: "Kanopi\\Firewall\\Plugins\\Url"
response: block
weight: 0
enable: true
config:
# Block all POST requests
- "method:POST"
# Block specific paths
- "path:/wp-admin"
- "path@starts_with:/admin"
- "path@contains:phpmyadmin"
- 'path@regex:/\.(sql|bak|old)$/i'
# Block based on host
- "host:malicious.example.com"
- "host@ends_with:.suspicious.com"
# Block based on query parameters
- "query.cmd@exists"
- "query.action:delete"
# Block based on POST data
- "post.username:admin"
- "post.action@in:drop,truncate,delete"
# Block based on headers
- "header.user-agent@contains:bot"
- "header.x-forwarded-for@exists"
# Complex URL rules
- type: AND
rules:
- "method:POST"
- "path@starts_with:/api"
- "!header.authorization@exists"
Available Variables¶
method- HTTP method (GET, POST, PUT, DELETE, etc.)host- Hostname from the requestpath- URI path (e.g., /admin/users)scheme- URL scheme (http or https)port- Port numberquery.*- Query parameters (e.g., query.page, query.id)query_count.*- How many values the client sent for one query parameter (query_count.f), as a number.query_countalone counts every parameter. See below.post.*- POST body parametersheader.*- HTTP headers (e.g., header.user-agent)cookie.*- Cookie values
Counting a parameter's values¶
query.f resolves to nothing when the client sends f as a list (f[]=a&f[]=b). That's deliberate, so contains can't match across values sent separately, and it means a list can't be compared. query_count.f is how many values of f the client sent, counted from the raw query string, so every spelling counts the same (#440):
| Query | query_count.f |
|---|---|
f[0]=a&f[1]=b&f[2]=c | 3 |
f[]=a&f[]=b | 2 |
f[0]=a&f[7]=b, f[x]=a&f[y]=b | 2 |
f=a&f=b, which PHP reduces to one | 2 |
f%5B0%5D=a&q=x&f%5B1%5D=b | 2 |
f[x][y]=a | 1 (each pair is one value of its top-level name) |
no f at all | 0 |
The name is case-sensitive, as the application reads it, and a name containing a dot is read whole (query_count.filter.type). Use the numeric operators: query_count.f@greater_than:3. See Facet crawling.