PHP code example of kanopi / crs-engine

1. Go to this page and download the library: Download kanopi/crs-engine library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

kanopi / crs-engine example snippets


use Kanopi\Crs\CrsConfig;
use Kanopi\Crs\CrsEngine;
use Kanopi\Crs\Request\RequestData;

// Construct once per process — loading the ruleset is the expensive part.
$engine = new CrsEngine(new CrsConfig(
    paranoia: 1,
    mode: CrsConfig::MODE_BLOCK,
));

$verdict = $engine->evaluate(RequestData::fromGlobals());

if ($verdict->isBlocked()) {
    http_response_code(403);
    error_log(sprintf(
        'CRS blocked request: %s, score %d, %d rule(s) matched',
        $verdict->blockingRuleId === null
            ? 'anomaly threshold reached'
            : 'rule ' . $verdict->blockingRuleId,
        $verdict->totalScore,
        count($verdict->matchedRules),
    ));
    exit;
}

$request = new RequestData(
    method:      $r->getMethod(),
    uri:         $r->getRequestUri(),
    rawUri:      $r->server->get('REQUEST_URI', '/'),
    queryString: $r->server->get('QUERY_STRING', ''),
    protocol:    $r->server->get('SERVER_PROTOCOL', 'HTTP/1.1'),
    remoteAddr:  $r->getClientIp() ?? '0.0.0.0',
    queryArgs:   $r->query->all(),
    postArgs:    $r->request->all(),
    cookies:     $r->cookies->all(),
    headers:     array_map(static fn (array $v): string => $v[0], $r->headers->all()),
    body:        (string) $r->getContent(),
);

new CrsConfig(
    paranoia: 1,                        // 1 (default) - 4. Higher = more strict, more false positives.
    mode: CrsConfig::MODE_BLOCK,        // or MODE_MONITOR (records matches, never blocks)
    anomalyThresholds: [
        'inbound'  => 5,                // request score >= this blocks the request
        'outbound' => 4,                // response score >= this blocks the response
    ],
    disabledRules:      [920300, 942130],     // skip these rule IDs
    disabledCategories: ['session_fixation'], // skip whole categories
    rulesPath:          null,                 // override location of compiled.php
    severityScores: [
        'critical' => 5,                // what each severity *adds* to the score
        'error'    => 4,
        'warning'  => 3,
        'notice'   => 2,
    ],
    maxRequestBodyBytes:  131072,       // request body bytes handed to the ruleset
    maxResponseBodyBytes: 524288,       // response body bytes — larger, see below
    maxArgs:              255,          // argument values inspected (counting is uncapped)
    maxArgBytes:          131072,       // total argument bytes inspected per rule
    responseMode:         null,         // overrides `mode` outbound only
);

new CrsConfig(
    mode:         CrsConfig::MODE_BLOCK,    // reject attacks on the way in
    responseMode: CrsConfig::MODE_MONITOR,  // only record leakage on the way out
);

new CrsConfig(
    // Fields where users legitimately paste code, paths and regexes.
    // Prefer this over disabledRules: it keeps the rule working everywhere else.
    disabledRules: [
        932235, 932260,   // unix command injection — trips on shell examples
        932280,           // shell metacharacters — trips on regexes
    ],
);

$decoded = json_decode($rawBody, true);

new RequestData(
    // ...
    postArgs: is_array($decoded) ? $decoded : [],
    body:     $rawBody,
);

new RequestData(
    method:      'POST',
    uri:         '/api/comments',
    rawUri:      '/api/comments',
    queryString: '',
    protocol:    'HTTP/1.1',
    remoteAddr:  '203.0.113.42',
    queryArgs:   $request->query->all(),         // GET params
    postArgs:    $request->request->all(),       // POST/form params
    cookies:     $request->cookies->all(),
    headers:     $request->headers->all(),       // name => string|string[]
    body:        (string) $request->getContent(),
    files:       [],                              // [{name, filename, mime, size}]
);

$verdict->action;          // 'allow' | 'log' | 'block'
$verdict->isBlocked();     // bool
$verdict->blockingRuleId;  // ?int — the first rule that fired with deny/block/drop
$verdict->totalScore;      // accumulated anomaly score across paranoia levels
$verdict->scores;          // per-category: ['sqli' => 5, 'xss' => 0, ...]
$verdict->matchedRules;    // [id, msg, severity, score, tags, category, matched_data, logdata]
$verdict->toArray();       // serialisable shape for logging

new CrsConfig(disabledRules: [948100]);