PHPackages                             inqaba-security/gatekeeper - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. inqaba-security/gatekeeper

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

inqaba-security/gatekeeper
==========================

Bridge Laravel applications to your SOC: normalized security event logging (ECS) to Microsoft Sentinel, Wazuh and syslog, with honeypots, endpoint-abuse detection and auth monitoring out of the box.

v0.1.0(1mo ago)04↓75%MITPHPPHP ^8.2

Since Jul 14Pushed 1mo agoCompare

[ Source](https://github.com/inqaba-security/gatekeeper)[ Packagist](https://packagist.org/packages/inqaba-security/gatekeeper)[ RSS](/packages/inqaba-security-gatekeeper/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (3)Used By (0)

Gatekeeper
==========

[](#gatekeeper)

Bridge your Laravel application to your SOC. **Gatekeeper** (`inqaba-security/gatekeeper`) turns application-level security signals — logins, registrations, honeypot hits, scanner probes, endpoint abuse — into normalized [Elastic Common Schema (ECS)](https://www.elastic.co/guide/en/ecs/current/index.html) events and ships them to **Microsoft Sentinel**, **Wazuh**, or any syslog/CEF pipeline, without slowing your requests down.

```
Laravel app ──> SecurityEvent (ECS + MITRE ATT&CK) ──> queue ──> Sentinel / Wazuh / syslog / log
     ▲
     ├── auth events (login, register, lockout, ...)
     ├── honeypot routes (incl. parameterized decoys) & form honeypots
     ├── endpoint abuse detection (brute force, scanning, 429 hammering)
     ├── resource enumeration / IDOR probing detection (photos/1, 2, 3 ...)
     ├── request threat scanner (SQLi / XSS / traversal / JNDI probes)
     └── Siem::log(...) custom events
                          + GeoIP enrichment ──> Slack / Teams alerting

```

Features
--------

[](#features)

- **Normalized events** — every event is an ECS 8.x document (`source.ip`, `user.id`, `event.action`, `event.outcome`, ...) with **MITRE ATT&amp;CK** tactic/technique tags, so analysts get consistent fields regardless of destination.
- **Microsoft Sentinel sink** — Azure Monitor **Logs Ingestion API** (DCE/DCR, Entra ID auth) or the legacy HTTP Data Collector API.
- **Wazuh sink** — JSON-lines file tailed by the Wazuh agent (Wazuh's recommended pattern for custom app logs).
- **Syslog sink** — RFC 5424 over UDP/TCP with **JSON or CEF** payloads (works with Wazuh's syslog listener, rsyslog, or Sentinel's AMA/CEF connector).
- **Auth monitoring** — automatic listeners for Laravel's `Login`, `Failed`, `Logout`, `Registered`, `PasswordReset`, `Lockout`, `Verified` events; each individually toggleable.
- **Slack &amp; Teams alerting** — webhook sinks (Slack Block Kit / Teams Adaptive Card via Workflows) with a per-sink severity floor, so only high/critical events page the channel while the full stream flows to the SIEM.
- **GeoIP enrichment** — ECS `source.geo.*` (country, city, coordinates, AS org) added at delivery time; local MaxMind database or HTTP driver, cached per IP, always off the request path.
- **Route honeypots** — decoy endpoints (`/wp-login.php`, `/.env`, ...) that log high-confidence attack traffic and can auto-block the source IP. Parameterized decoys (`internal/photos/{id}`) capture exactly which IDs were probed, with optional method and regex constraints.
- **Resource enumeration / IDOR detection** — flags per-IP behaviour real users don't exhibit on parameterized routes: constant-stride ID scans (`photos/1, 2, 3...`), abnormal distinct-ID breadth, and high 404/403 miss ratios from ID guessing.
- **Form honeypot** — invisible field + minimum-fill-time check via a Blade component; reject or *silently accept* bot submissions.
- **Endpoint abuse detection** — per-IP thresholds for auth failures (brute force), 404 bursts (forced browsing / scanning), 429 hammering, and raw volume.
- **Request threat scanner** — passive heuristics for SQLi, XSS, path traversal, command/template injection, JNDI (log4shell-style) and PHP wrapper probes. Telemetry-first; optional blocking.
- **IP blocklist** — cache-backed, TTL'd, CIDR-aware exemptions, enforced by middleware, fed by honeypots/abuse detection or `Siem::blockIp()`.
- **Queue shipping** — events ship on a queue with retries + backoff; failures fall back to the local log so nothing is lost silently.
- **Privacy controls** — key-based redaction (`password`, `token`, ...) and optional sha256 pseudonymization of usernames.

Installation
------------

[](#installation)

```
composer require inqaba-security/gatekeeper
php artisan vendor:publish --tag=siem-config
```

Choose your destinations in `.env`:

```
SIEM_SINKS=sentinel,wazuh   # any of: sentinel, wazuh, syslog, log, null
SIEM_QUEUE=true             # ship on the "siem" queue (recommended)
```

Verify connectivity end-to-end:

```
php artisan siem:test            # all active sinks
php artisan siem:test --sink=sentinel
```

Sinks
-----

[](#sinks)

### Microsoft Sentinel (recommended: Logs Ingestion API)

[](#microsoft-sentinel-recommended-logs-ingestion-api)

1. Create a **Data Collection Endpoint** (DCE) and a **custom table** (e.g. `Gatekeeper_CL`) with a **Data Collection Rule** (DCR) in your Log Analytics workspace.
2. Create an **Entra ID app registration** and grant it the **Monitoring Metrics Publisher** role on the DCR.
3. Configure:

```
SIEM_SENTINEL_MODE=dcr
SIEM_SENTINEL_TENANT_ID=...
SIEM_SENTINEL_CLIENT_ID=...
SIEM_SENTINEL_CLIENT_SECRET=...
SIEM_SENTINEL_DCE=https://..ingest.monitor.azure.com
SIEM_SENTINEL_DCR_ID=dcr-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
SIEM_SENTINEL_STREAM=Custom-Gatekeeper_CL
```

Events include a `TimeGenerated` field ready for the DCR transform. Example hunting query:

```
Gatekeeper_CL
| where event_action == "honeypot_route_hit" or event_severity >= 7
| summarize hits = count() by source_ip = tostring(source_ip), event_action
| order by hits desc
```

For legacy workspaces, set `SIEM_SENTINEL_MODE=legacy` with `SIEM_SENTINEL_WORKSPACE_ID` and `SIEM_SENTINEL_SHARED_KEY` (HTTP Data Collector API — deprecated by Microsoft but still common).

### Wazuh

[](#wazuh)

Events are appended as JSON lines to a file the Wazuh agent tails:

```
SIEM_WAZUH_PATH=/var/log/gatekeeper/events.json
```

Agent `ossec.conf`:

```

  json
  /var/log/gatekeeper/events.json

```

Example manager rule (all ECS fields are available to the JSON decoder):

```

    json
    gatekeeper
    honeypot_route_hit
    Laravel honeypot hit from $(source.ip)
    T1595

    json
    gatekeeper
    brute_force_detected
    Brute force against Laravel app from $(source.ip)
    T1110

```

Rotate the file with `logrotate` (`copytruncate`) so the agent keeps its file handle.

### Syslog / CEF

[](#syslog--cef)

```
SIEM_SINKS=syslog
SIEM_SYSLOG_HOST=siem.internal
SIEM_SYSLOG_PORT=514
SIEM_SYSLOG_PROTOCOL=udp     # or tcp (RFC 6587 octet framing)
SIEM_SYSLOG_FORMAT=json      # or cef
```

Use `cef` for ArcSight-style pipelines, including Sentinel's AMA/CEF connector and Wazuh's `` syslog listener.

### Slack &amp; Microsoft Teams (alerting)

[](#slack--microsoft-teams-alerting)

```
SIEM_SINKS=sentinel,slack,teams
SIEM_SLACK_WEBHOOK=https://hooks.slack.com/services/T000/B000/xxx
SIEM_SLACK_MIN_SEVERITY=high          # only high/critical page the channel
SIEM_TEAMS_WEBHOOK=https://...logic.azure.com/workflows/...   # Teams Workflows webhook
SIEM_TEAMS_MIN_SEVERITY=high
```

Slack receives a Block Kit card (severity color bar, source IP + geo, user, request, MITRE technique, context). Teams receives an Adaptive Card via a Workflows/Power Automate *"post to a channel when a webhook request is received"* flow — the replacement for the retired O365 connectors. The `minimum_severity` floor is what makes these alerting channels rather than a firehose.

GeoIP enrichment
----------------

[](#geoip-enrichment)

```
SIEM_GEOIP=true
SIEM_GEOIP_DRIVER=maxmind   # or: ipapi
SIEM_GEOIP_MMDB=/var/lib/geoip/GeoLite2-City.mmdb
```

- `maxmind` — offline lookups against a local GeoLite2/GeoIP2 City database. Requires `composer require geoip2/geoip2`.
- `ipapi` — HTTP lookups (ip-api.com format by default, URL configurable). Zero dependencies; check your provider's terms.

Enrichment happens at **delivery time on the queue worker**, results (including misses) are cached per IP for 24h, and any resolver failure degrades to an un-enriched event — GeoIP can never delay a request or lose an event. Fields land under ECS `source.geo.*` / `source.as.*` and show up in Slack/Teams alerts as `203.0.113.9 (Oslo, NO)`.

Custom resolver: bind your own `Inqaba\Gatekeeper\Geo\GeoIpResolver` implementation in the container.

Middleware
----------

[](#middleware)

Register where appropriate (typically the global stack or the `web`/`api` groups):

AliasPurpose`siem.block`Enforce the IP blocklist (put it **first**)`siem.abuse`Per-IP brute force / scanning / rate-abuse detection`siem.scan`Request payload threat scanner (detection-only by default)`siem.enum`Resource enumeration / IDOR probing detection on parameterized routes`siem.honeypot`Form honeypot validation on POST/PUT/PATCH/DELETE routes```
// bootstrap/app.php (Laravel 11+)
->withMiddleware(function (Middleware $middleware) {
    $middleware->prepend(\Inqaba\Gatekeeper\Http\Middleware\BlockDeniedIps::class);
    $middleware->web(append: [
        \Inqaba\Gatekeeper\Http\Middleware\DetectEndpointAbuse::class,
        \Inqaba\Gatekeeper\Http\Middleware\ScanRequestThreats::class,
    ]);
})
```

Honeypots
---------

[](#honeypots)

**Route honeypots** are registered automatically from `config('siem.honeypots.routes.paths')` — decoy paths like `wp-login.php`, `.env`, `phpmyadmin`. Any hit emits a high-severity `honeypot_route_hit` event and (by default) blocks the source IP for 24h. Add paths that make sense for your stack; remove any that collide with real routes.

Decoys can take **route parameters** — useful for trapping ID-guessing scripts on endpoints that look like real resources:

```
'paths' => [
    'wp-login.php',
    'internal/photos/{id}',   // params are captured in the event context
    ['path' => 'legacy/export/{file}', 'methods' => ['GET'], 'where' => ['file' => '.*\.sql']],
],
```

The probed parameter values (`route_params`) and the matched pattern land in the event, so the SOC sees exactly what the attacker tried to pull. Make sure a decoy never shadows a real route.

**Form honeypot** — drop the component into any form and protect the route:

```

    @csrf

    ...

```

```
Route::post('/register', ...)->middleware('siem.honeypot');
```

Bots that fill the invisible field or submit faster than `min_seconds` are logged and rejected (`respond => 'reject'`) or fed a fake success (`respond => 'silent'`).

Resource enumeration / IDOR detection
-------------------------------------

[](#resource-enumeration--idor-detection)

Attach `siem.enum` to routes with parameters:

```
Route::get('/photos/{photo}', [PhotoController::class, 'show'])
    ->middleware('siem.enum');
```

Per IP and per route *pattern* (`photos/{photo}`), within a 5-minute window, it raises a high-severity `resource_enumeration_detected` event when it sees behaviour no human browsing session produces:

PatternTrigger (defaults)What it catches`sequential_scan`≥ 5 numeric IDs advancing with a constant stride`photos/1, 2, 3...` and `photos/10, 20, 30...` scrapers`high_distinct_ids`≥ 15 distinct IDs touchedbreadth-first scraping, incl. non-numeric IDs/slugs`high_miss_ratio`≥ 50% 404/403/410 across ≥ 10 requestsIDOR guessing — probing IDs that don't exist or aren't theirsEvents include the resource pattern, distinct-ID count, detected stride, miss counts and a sample of probed IDs. Each pattern fires once per IP per window, and `SIEM_ENUM_AUTOBLOCK=true` blocks the scanner outright. Tune thresholds in `config('siem.enumeration')`.

Custom events
-------------

[](#custom-events)

```
use Inqaba\Gatekeeper\Facades\Siem;
use Inqaba\Gatekeeper\Events\{EventType, Severity};

// One-liner
Siem::log('export_download', 'Customer data export downloaded', Severity::High, [
    'rows' => 12000,
], $request->user());

// Fluent builder with a catalog type
Siem::event(EventType::AccessDenied)
    ->user($request->user())
    ->fromRequest($request)
    ->with(['ability' => 'orders.export'])
    ->report();
```

Custom sink drivers:

```
Siem::extend('kafka', fn ($app) => new KafkaSink(...));
// config/siem.php: 'active' => ['kafka'],
```

Blocklist API
-------------

[](#blocklist-api)

```
Siem::blockIp('203.0.113.7', minutes: 120, reason: 'analyst-decision');
Siem::unblockIp('203.0.113.7');
Siem::blocklist()->isBlocked('203.0.113.7');
```

IPs/CIDRs in `siem.blocklist.never_block` (health checks, offices, load balancer probes) can never be auto-blocked.

Event catalog
-------------

[](#event-catalog)

`event.action`SeverityMITREEmitted by`login_succeeded` / `login_failed` / `logout`info / low / infoT1078 / T1110 / —auth listener`user_registered`, `password_reset*`, `email_verified`info–low—auth listener`auth_lockout`highT1110auth listener`honeypot_route_hit`highT1595.003honeypot routes`honeypot_form_triggered`medium—`siem.honeypot``brute_force_detected`criticalT1110`siem.abuse``scanning_detected`highT1595.003`siem.abuse``rate_limit_abuse` / `endpoint_abuse`medium / highT1595`siem.abuse``suspicious_input`highT1190`siem.scan``resource_enumeration_detected`highT1119`siem.enum``blocked_ip_attempt`low—`siem.block`anything via `Siem::log()`your choice—your codeOperational notes
-----------------

[](#operational-notes)

- **Run a queue worker** for the `siem` queue (`php artisan queue:work --queue=siem`). With `SIEM_QUEUE=false` events ship inline — fine for the file/log sinks, not for network sinks.
- **Failure handling**: sink failures are retried (3 tries, backoff 5/30/120s) and then written to the local Laravel log with the full event, so security telemetry is never dropped silently.
- **Noise control**: abuse detections fire once per IP per window; blocked-IP events once per IP per 5 minutes; `SIEM_MIN_SEVERITY` filters globally.
- **Privacy**: context keys like `password`/`token` are always redacted; set `SIEM_HASH_USERNAMES=true` to pseudonymize identifiers before they leave the app.
- The scanner and abuse detectors are **telemetry, not a WAF** — keep your WAF/CDN protections in front; this package gives your SOC the application-layer signal those layers can't see.

Testing
-------

[](#testing)

```
composer install
composer test
```

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance90

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/113a901c937c45bd61bd5cb46f0739de609023dabea0623f7037a49b63c45bcb?d=identicon)[Inqaba](/maintainers/Inqaba)

---

Top Contributors

[![inqaba-security](https://avatars.githubusercontent.com/u/13131835?v=4)](https://github.com/inqaba-security "inqaba-security (2 commits)")

---

Tags

laravelloggingsecurityHoneypotECSSIEMsentinelwazuhsoc

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/inqaba-security-gatekeeper/health.svg)

```
[![Health](https://phpackages.com/badges/inqaba-security-gatekeeper/health.svg)](https://phpackages.com/packages/inqaba-security-gatekeeper)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k118.2M1.0k](/packages/laravel-socialite)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k59.5M714](/packages/laravel-scout)[illuminate/auth

The Illuminate Auth package.

10528.8M1.4k](/packages/illuminate-auth)[illuminate/routing

The Illuminate Routing package.

1419.6M3.8k](/packages/illuminate-routing)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
