Hoppa till huvudinnehållet

Vidarebefordran till SIEM

fremforge skriver varje administrativ åtgärd som ändrar tillstånd till en granskningslogg per tenant med en hashkedjad integritetsgaranti (se Granskningskedjans integritet). Samma händelser kan skickas vidare till ditt SIEM så snart de skrivs, så att dina detekteringsregler ser dem inom sekunder i stället för vid nästa CSV-export.

Kommer snart: samma ström kommer också att bära en händelse per livscykeländring för ett säkerhetsfynd: när ett fynd om en sårbarhet, licens, SAST, hemlighet, skadlig kod eller paketpolicy öppnas, öppnas igen, avfärdas eller löses. Se Fyndhändelser.

Den här sidan beskriver vidarebefordrarens utformning, protokollkontraktet för verifiering och hur populära SIEM-system tar emot strömmen.

Hur leveransen fungerar

audit-emit skriver den kedjade raden och köar, i samma databastransaktion, händelsen för varje aktiverad endpoint vars åtgärdsprefix matchar. En leveransprocess skickar varje endpoints kö i ordning, en eller flera händelser per begäran beroende på endpointens format. En händelse tas bort ur kön först när din mottagare svarar 2xx.

Inställningarna finns under Organisationsadmin → Integration → SIEM-vidarebefordran (/<org>/_admin/siem), och samma inställningar finns i REST-API:t (/_app/api/v1/orgs/<org>/siem/endpoints).

Sidan för SIEM-vidarebefordran har formuläret Lägg till en endpoint och listan över endpoints: namn, URL (med inloggningsuppgifter maskerade), status, senaste leverans och antalet händelser som väntar. Varje endpoints namn öppnar dess egen sida (/<org>/_admin/siem/<endpoint id>), med ett kort per inställning:

  • Översikt: status, orsaken till senaste felet, filtret för åtgärdsprefix, Inaktivera/Aktivera och Skicka test.
  • Auth-header: ange, rotera eller ta bort den.
  • Signatur: vad HMAC:en omfattar.
  • Leverans: väntande händelser, den äldsta av dem, nästa försök, totalt levererade och kasserade, samt inställningarna för format och samling.
  • Skicka om: skicka tidigare händelser igen.
  • Ta bort endpoint: skriv endpointens namn för att bekräfta.

Konfigurera en endpoint

  1. Gå till Organisationsadmin → SIEM-vidarebefordran → Lägg till en endpoint.
  2. Ange ett Namn (fritext, unikt inom organisationen) och URL:en till din mottagare: https://…, eller syslog+tls://host:port för syslog över TLS. http://, localhost, RFC1918-adresser och metadataadresser nekas; använd en mottagare som går att nå publikt.
  3. Välj Format (se Format och samling). De återstående stegen finns under Avancerade alternativ och kan också ändras senare på endpointens sida.
  4. Begränsa eventuellt tillåtelselistan med åtgärdsprefix (till exempel forgejo.push, billing.). Tom betyder att allt vidarebefordras. Varje prefix matchas med action.startsWith(prefix).
  5. Slå eventuellt på Inkludera åtkomstloggen för registret för att också ta emot en händelse per begäran mot paket- eller container-registret (registry.access.*). Den är av som standard; se Åtkomstlogg för registry. Image-spärrarnas beslut om hämtningar, registry.pull.blocked och registry.pull.would_block, är granskningshändelser, inte åtkomsthändelser, så de vidarebefordras utan det här alternativet, en per image-digest, spärr och beslut per timme; se Spärrar för hämtning av containrar. Kommer snart: Inkludera fyndhändelser, förvalt, kommer att göra att endpointen också tar emot en händelse per livscykeländring för ett säkerhetsfynd (security.finding.*), från och med att den skapas. Tills funktionen släpps visas alternativet inte i formuläret. Se Fyndhändelser.
  6. Ange eventuellt en Auth-header: ett headernamn och ett hemligt värde som skickas med varje begäran (till exempel Authorization och Splunk <token>). Se Autentisering.
  7. Välj vad signaturen omfattar (tidsstämpel och body som standard, eller endast body).
  8. Ange samlingsstorlek, storlek per begäran och väntetid.
  9. Klicka på Skapa endpoint. Den delade HMAC-hemligheten visas en gång på nästa skärm; kopiera den till mottagarens konfiguration. fremforge visar den aldrig igen. Om du tappar bort den, ta bort endpointen och lägg till den igen.

Endpointen blir aktiv direkt; bekräftelsen länkar till dess sida. Skicka test skickar en syntetisk händelse fremforge.siem.test i endpointens format, med dess headers och signatur, och visar svarsstatusen och de första 200 byten av mottagarens svar.

Inloggningsuppgifter i URL:en. Vissa insamlare tar emot sina inloggningsuppgifter i URL:en (?token=, ?key=&secret= eller user:password@). fremforge lagrar URL:en som den angavs krypterad, och visar den överallt annars (administrationssidan, API:t, testresultatet, granskningsloggen) med varje värde i frågesträngen och användarinformationen ersatta med ***. Bara leveransen använder den fullständiga URL:en.

Autentisering

Två mekanismer, som kan användas tillsammans:

  • Auth-header. En header per endpoint, med namn och värde som du väljer. Värdet krypteras i vila och är endast skrivbart: ingen sida, inget API-svar, ingen loggrad och ingen granskningshändelse innehåller det. Granskningsloggen registrerar headerns namn och när den angavs. För att rotera sparar du ett nytt värde; det gamla slutar skickas direkt, så lägg till den nya nyckeln i insamlaren först. Headernamn som skulle förstöra begäran nekas: Host, Content-Length, Content-Type, Content-Encoding, Transfer-Encoding, Connection, Keep-Alive, Upgrade, TE, Trailer, Expect, Accept-Encoding, User-Agent, Cookie, Proxy-* och X-Fremforge-*. Värden är utskrivbar ASCII, upp till 4096 tecken.
  • HMAC-signatur. Varje begäran bär X-Fremforge-Signature: sha256=<hex>, med endpointens HMAC-hemlighet som nyckel. Som standard omfattar den <timestamp>.<body>, så att mottagaren kan avvisa en gammal begäran utifrån tidsstämpeln. Med Signaturen omfattar: Endast body omfattar den bara den råa bodyn, vilket är vad mottagare med inbyggd verifiering av bodyn (Elastics HTTP endpoint-input) kontrollerar. En mottagare som bara verifierar bodyn kan inte avvisa en omsänd begäran utifrån ålder; deduplicera på händelsens id i stället.

Protokollkontrakt

Varje HTTPS-leverans är en POST:

HeaderVärde
Content-Typeapplication/json (json, json_array), application/x-ndjson (ndjson), text/plain; charset=utf-8 (cef)
User-Agentfremforge-siem-forwarder/1
X-Fremforge-TimestampUnix-sekunder (sträng) när fremforge byggde begäran
X-Fremforge-Signaturesha256=<hex> över <timestamp>.<body>, eller över <body> i läget endast body
Idempotency-KeyHändelsens id för en enskild händelse; för en samling, en SHA-256 av samlingens händelse-id:n i ordning
X-Fremforge-Event-CountAntalet händelser i bodyn
din auth-headerNär den är angiven

Varje händelse har den här formen (nycklar på toppnivå i den här ordningen; nycklarna i fields.* sorterade alfabetiskt så att byten är stabila):

{
  "id": "00000000-0000-0000-0000-000000000000",
  "tenant_id": "11111111-1111-1111-1111-111111111111",
  "actor": "alice",
  "action": "policy.update.api",
  "fields": { "...": "..." },
  "created_at": "2026-05-15T13:42:11.123Z",
  "hash_chain": {
    "prev_hash": "ab12...",
    "this_hash": "cd34..."
  }
}

id är granskningshändelsens id. Det är detsamma vid varje omförsök och omsändning av den händelsen: använd det för att deduplicera.

actor är ett bart Forgejo-användarnamn för en åtgärd som en person utför (i webbkonsolen, och för många API-anrop, den användare som skapade token), oidc:idp:<sub> för en person som loggade in via din IdP och agerar med en utbytt token, token:<id> för andra API-anrop, operator:<email> för en åtgärd av fremverks personal, och andra former av <kind>:<id> för systemkomponenter.

hash_chain.this_hash är samma hash som förankras i OBS Object Lock, så att ditt SIEM kan bevisa att en händelse är äldre än en känd förankring.

En åtkomsthändelse för registret (bara på en endpoint med include_access_log) har samma form, med action registry.access.<action> och "hash_chain": null: åtkomsthändelser ingår inte i granskningens hashkedja. Dess fields är händelsen så som API:t för åtkomstloggen returnerar den.

En fyndhändelse (kommer snart; bara på en endpoint med include_finding_events) kommer också att ha den här formen, med action security.finding.<transition> och "hash_chain": null.

Format och samling

FormatBodyHändelser per begäran
json (standard)Ett händelseobjekt1
ndjsonEtt händelseobjekt per rad, varje rad avslutad med \nUpp till max händelser
json_arrayEn JSON-array med händelseobjektUpp till max händelser
cefEn CEF-rad per händelse, var och en avslutad med \nUpp till max händelser

För formaten med samling:

  • Max händelser per begäran: 1 till 1000 (100 när du byter till ett format med samling utan att välja).
  • Max storlek per begäran: 1 KiB till 4 MiB (standard 1 MiB). En samling stängs innan den skulle överskrida gränsen; en enskild händelse skickas alltid.
  • Max väntetid: 0 till 60 000 ms (standard 0). Hur länge en delvis fylld samling väntar på fler händelser innan den skickas.

Händelser levereras i den ordning de skrevs.

Mappning av CEF-fält

CEF:0|fremverk|fremforge|1|<action>|<action>|<severity>|<extension>

CEFVärde
Signature ID, Nameaction
Severity9 när hash_chain.this_hash är chain-broken; 7 när action innehåller fail, denied, deny, blocked, violation, malware, secret, breach eller tamper; 5 när den innehåller delete, revoke, disable, remove, bypass, suspend eller dismiss; annars 3
rtcreated_at, millisekunder sedan epoken
externalIdid (dedupliceringsnyckeln)
suseractor
actaction
cataction fram till första .
cs1Label=tenantId, cs1tenant_id
cs2Label=fields, cs2fields som JSON
cs3Label=hashChainThis, cs3hash_chain.this_hash
cs4Label=hashChainPrev, cs4hash_chain.prev_hash, tomt när det inte finns något
dvchostfrem.sh

Headerfält escapar \ och |; extensionvärden escapar \, = och radbrytningar (\n, \r). Ett värde från en granskningshändelse kan därför inte starta ett nytt fält eller en ny rad.

Fyndhändelser

Kommer snart. Fyndhändelser är inte tillgängliga än. Det här avsnittet beskriver dem inför lanseringen; tills dess erbjuder varken konsolen eller API:t include_finding_events eller ögonblicksbilden, och ingen händelse security.finding.* skickas. Ändringsloggen meddelar lanseringen.

En endpoint med Inkludera fyndhändelser påslaget (include_finding_events i API:t) tar emot en händelse varje gång ett säkerhetsfynd byter tillstånd. Alla typer av fynd som fremforge rapporterar ingår:

alert_categoryFyndKälla
dependency_vulnerabilityEtt sårbart beroende på standardbranchenBeroendeskanning
image_vulnerabilityEn sårbarhet eller felkonfiguration i en container-image i ditt registry (inklusive proxade images) eller i en skanning av ett repoImage-skanning
licenceEtt beroende under en licens som är forbidden eller restrictedLicensskanning
sastEtt fynd från statisk analys på standardbranchenSAST
secretEn incheckad hemlighet (aldrig själva hemligheten)Hemlighetsskanning
malwareEtt skadligt paket (OSV MAL-*), eller en uppladdning som antivirusskanningen (ClamAV) nekadeBeroende- och registry-skanningar, skanning av uppladdningar
package_policyEn fil i paketregistret som paketpolicyn blockerar eller varnar förPaketpolicy

Fynd från skanningar av pull requests ingår inte: de kommer och går med varje pull request. Det är på standardbranchen som ett fynds livscykel följs. Händelser för paketpolicy skickas bara för organisationer där resultaten av paketpolicyn är synliga.

Händelsekatalog

actionNär
security.finding.openedFyndet upptäcks för första gången.
security.finding.reopenedFyndet upptäcks igen efter att ha lösts (transition_reason reappeared), eller dess avfärdande drogs tillbaka innan det löpte ut (undismissed).
security.finding.dismissedNågon avfärdade fyndet (risk accepterad, falskt positivt), en tillåtelselista täcker det, eller ett antivirusfynd åsidosattes. actor är den användare som avfärdade det, eller system:<kind>.
security.finding.dismissal_expiredEtt tidsbegränsat avfärdande löpte ut och fyndet är öppet igen. Det omfattar ett undantag som nådde sitt slutdatum.
security.finding.exception_revokedEtt godkänt undantag som avfärdade fyndet återkallades, och fyndet är öppet igen. transition_reason är utlösaren: manual (någon återkallade det), severity_raised, new_vulnerability eller approver_left. actor är den person som återkallade det, eller system:exception-revocation.
security.finding.dismissal_revokedfremforge återkallade ett avfärdande som byggde på bedömningen ej nåbar från nåbarhet, eftersom en senare skanning motsäger den, och fyndet är öppet igen. transition_reason är reachability_changed, finding.revocation anger varför och actor är system:exception-revocation. (Före 2026-10-12 skickades detta som dismissal_expired.) Ett återkallat undantag är exception_revoked med samma orsak.
security.finding.resolvedFyndet upptäcks inte längre: åtgärdat, borttaget, ersatt av en nyare skanning som inte längre rapporterar det, eller paketfilen raderad.
security.finding.known_exploitedEn sårbarhet i ett beroende eller en image, öppen eller avfärdad, listades som utnyttjad i praktiken av ENISA:s EU Vulnerability Database eller CISA:s KEV-katalog. Skickas en gång per listning. transition_reason är listed (listan har lagt till den sedan dess), eller new (fyndet var redan listat när det först sågs; händelsen skickas tillsammans med dess opened, i valfri ordning). known_exploited anger CVE:erna och listorna, och actor är system:known-exploited-feed. Ett avfärdat fynd rapporteras också: risken som det accepterades under har ändrats.
security.finding.snapshotEn per öppet fynd, skickas en gång när du begär en ögonblicksbild.

Ändringar fångas upp inom ungefär en minut. Ett fynd behåller en identitet mellan omskanningar, så en omskanning som rapporterar samma fynd igen skickar ingenting; fields.original_alert_id är detsamma på varje händelse om ett och samma fynd.

Vad som skickas, och från när

  • Bara ändringar från och med att du valde det. En endpoint tar emot händelser för ändringar som sker efter att den började ta emot dem (finding_events_since i API:t). Att slå på fyndhändelser skickar inte dina befintliga fynd.
  • Ögonblicksbild av öppna fynd. För att läsa in det som är öppet nu, använd Skicka ögonblicksbild av öppna fynd på endpointens sida, eller POST /_app/api/v1/orgs/<org>/siem/endpoints/<id>/finding-snapshot. Det köar en händelse security.finding.snapshot per öppet fynd, bara till den endpointen. Den kan tas en gång per endpoint; en andra begäran besvaras med 409.
  • Standard. En endpoint som skapas efter lanseringen inkluderar fyndhändelser om du inte avmarkerar rutan (eller skickar "include_finding_events": false). Endpoints som finns före lanseringen lämnas som de var, utan fyndhändelser, så att en mottagare som byggts för granskningshändelser inte börjar ta emot en ny händelsefamilj utan förvarning; slå på dem per endpoint på dess sida eller med PATCH … {"include_finding_events": true}.
  • Åtgärdsprefix gäller som för granskningshändelser. En endpoint med prefix tar bara emot fyndhändelser om något av dem matchar, till exempel security.finding..
  • Leverans, samling, omförsök, omsändning, HMAC, auth-headern och hur länge olevererade händelser sparas är desamma som för granskningshändelser. Händelsens id är detsamma vid varje omförsök och omsändning: deduplicera på det.

Fält

fields i en fyndhändelse:

FältVärdeCortex XDM
alert_nameKort rubrik, till exempel CVE-2024-3094 in xz-utils (frem.sh/acme/api@sha256:…)xdm.alert.name
alert_categoryFyndtypen (tabellen ovan)xdm.alert.category
alert_severitycritical, high, medium, low eller informationalxdm.alert.severity
alert_descriptionSammanfattning av säkerhetsmeddelandet eller regelns meddelande, högst 500 tecken, eller nullxdm.alert.description
original_alert_idStabilt fynd-id (32 hexadecimala tecken), detsamma på varje händelse om ett och samma fyndxdm.alert.original_alert_id
orgOrganisationens slug
finding.type, finding.sourceFyndtyp, och skannern som rapporterade fyndet
finding.state, finding.previous_stateopen, dismissed eller resolved; tillståndet före (null när fyndet ses första gången)
finding.transition, finding.transition_reasonÖvergången (som i action) och varför: new, reappeared, undismissed, dismissal_expired, manual, severity_raised, new_vulnerability, approver_left, reachability_changed, dismissed, new_dismissed, not_detected, superseded, no_longer_present, allowed, package_deleted, listed, snapshot
finding.first_seen_at, finding.state_changed_atNär fyndet sågs första gången; när det gick in i sitt nuvarande tillstånd
finding.keyDen identitet fyndet behåller mellan omskanningar, till exempel dep:acme/api:GHSA-…:npm:lodash@4.17.20
finding.row_idFyndets nuvarande id i Findings API; det ändras när en omskanning ersätter raden
finding.console_urlVar fyndet visas i konsolen
finding.dismissal{by, reason, expires_at, approved_by, exception_id} på en händelse dismissed, annars null. approved_by och exception_id anges när ett godkänt undantag avfärdade fyndet
finding.exception{id, status, approved_by, revoked_by, revoked_reason, revoked_detail} för undantaget som upphörde, på exception_revoked och på en dismissal_expired som avslutade ett undantag; annars null
finding.revocation{reason, detail, revoked_verdict, revoked_at} på dismissal_revoked: utlösaren (reachability_changed), vad som ändrades, den senare bedömning som motsade ej nåbar, och när; annars null
cvss{score, vector} när säkerhetsmeddelandet har en, annars null
identifiersListor över id:n för säkerhetsmeddelanden: cve, ghsa, mal och other; rule_id (regler för SAST, hemligheter och felkonfiguration i images, regler för paketpolicy); licence; signature (ClamAV)
componentDen berörda komponenten: purl, name, version, ecosystem, fixed_version, sha256. Null för SAST och hemligheter. För en sårbarhet i en image har paketet i imagen ett namn men ingen purl; imagens purl finns i location.image.purl.
locationkind (repository, image, package eller upload), repository, path, line, commit_sha; image {reference, purl, proxied, tags}, med reference i den publika formen frem.sh/<org>/<image>@sha256:<digest>; package {type, owner, name, version, file, sha256}; upload_kindxdm.target.resource.name (reference, repo eller package)
policyBara händelser för paketpolicy: {verdict, reasons}
known_exploitedPå varje händelse: {cve_ids, sources} när fyndet är en sårbarhet i ett beroende eller en image som är listad som utnyttjad i praktiken (sources: euvd för ENISA EUVD, cisa_kev för CISA KEV), annars null. CVE-id:na är fyndets egna eller alias för dess säkerhetsmeddelande.

Exempel (formatet json):

{
  "id": "6f0b1f7e-2c55-4c55-9a0e-6b8f8f7e1a10",
  "tenant_id": "11111111-1111-1111-1111-111111111111",
  "actor": "system:finding-lifecycle",
  "action": "security.finding.opened",
  "fields": {
    "alert_category": "dependency_vulnerability",
    "alert_description": "Command injection in lodash",
    "alert_name": "GHSA-35jh-r3h4-6jhm in lodash 4.17.20",
    "alert_severity": "high",
    "component": { "purl": "pkg:npm/lodash@4.17.20", "name": "lodash", "version": "4.17.20", "ecosystem": "npm", "fixed_version": "4.17.21", "sha256": null },
    "cvss": { "score": 7.2, "vector": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H" },
    "finding": {
      "id": "3c1d2a8e5b0f4e6a9d7c1b2a3e4f5a6b",
      "key": "dep:acme/api:GHSA-35jh-r3h4-6jhm:npm:lodash@4.17.20",
      "type": "dependency_vulnerability",
      "source": "dep-scan",
      "state": "open",
      "previous_state": null,
      "transition": "opened",
      "transition_reason": "new",
      "first_seen_at": "2026-10-08T09:12:40.120Z",
      "state_changed_at": "2026-10-08T09:13:02.457Z",
      "row_id": "0d6c7c43-2b1f-4f5e-8a3e-0b2f9f1c2d3e",
      "console_url": "https://frem.sh/acme/_admin/code-security/deps",
      "dismissal": null
    },
    "identifiers": { "cve": [], "ghsa": ["GHSA-35jh-r3h4-6jhm"], "mal": [], "other": [], "rule_id": null, "licence": null, "signature": null },
    "known_exploited": null,
    "location": { "kind": "repository", "repository": "acme/api", "path": "package-lock.json", "line": null, "commit_sha": null, "image": null, "package": null, "upload_kind": null },
    "org": "acme",
    "original_alert_id": "3c1d2a8e5b0f4e6a9d7c1b2a3e4f5a6b"
  },
  "created_at": "2026-10-08T09:13:02.457Z",
  "hash_chain": null
}

Fyndhändelser ingår inte i granskningens hashkedja. En ändring av en endpoints inställning för fyndhändelser, och en begäran om ögonblicksbild, är granskningshändelser (siem.endpoint.finding_events.update, siem.endpoint.finding_snapshot).

Fyndhändelser i CEF

En fyndhändelse använder CEF-mappningen med de här skillnaderna:

CEFVärde
Namealert_name (Signature ID förblir action)
SeverityMedan fyndet är öppet (opened, reopened, dismissal_expired, exception_revoked, dismissal_revoked, snapshot): 10 critical, 8 high, 5 medium, 3 low, 1 informational. 1 för dismissed och resolved, så att goda nyheter inte utlöser ett larm som fyndet gjorde. known_exploited är minst 8, oavsett fyndets allvarlighetsgrad
catalert_category (fyndtypen)
msgalert_name
outcomefinding.state
reasonfinding.transition_reason
startfinding.first_seen_at, millisekunder sedan epoken
cs5Label=findingId, cs5original_alert_id
cs6Label=purl, cs6component.purl, när det finns en
flexString1Label=identifiers, flexString1CVE-, GHSA-, MAL- och andra id:n, regel-id, licens och signatur, kommaseparerade
flexString2Label=location, flexString2Image-referensen, repot eller paketet
cfp1Label=cvssScore, cfp1cvss.score, när det finns ett
filePathlocation.path
fileHashcomponent.sha256 (antivirusfynd, paketfiler)

cs3 och cs4 är tomma, som för åtkomsthändelser för registret. Över syslog följer allvarlighetsgraden enligt RFC 5424 CEF-allvarlighetsgraden som för granskningshändelser: ett kritiskt fynd skickas med 2 (critical), ett löst med 6 (informational).

Syslog över TLS

En endpoint-URL på formen syslog+tls://host[:port] (standardport 6514) levererar över syslog i stället för HTTPS:

  • Transport: TCP med TLS 1.2 eller senare, med ramning genom oktetträkning (RFC 5425). fremforge verifierar mottagarens certifikat mot den publika uppsättningen CA:er, för värdnamnet i URL:en, så mottagaren behöver ett publikt betrott certifikat. Använd port 6514 eller 443; andra portar går inte att nå från fremforge.
  • Meddelande: ett meddelande enligt RFC 5424 per händelse, <PRI>1 TIMESTAMP frem.sh fremforge - MSGID - MSG. PRI är facility 13 (log audit) × 8 + allvarlighetsgrad: 2 för CEF-allvarlighetsgrad 9, 4 för 7, 5 för 5, annars 6. MSGID är action (upp till 32 tecken). MSG är en UTF-8-byteordningsmarkering följd av CEF-raden (formatet cef) eller JSON-händelsen (alla andra format).
  • Samling: upp till max händelser meddelanden inom max storlek per begäran delar en anslutning.
  • Ingen bekräftelse. Syslog har inget svar på applikationsnivå. En samling räknas som levererad när varje meddelande har skrivits och mottagaren har stängt anslutningen korrekt. En nekad anslutning, ett TLS-fel eller en mottagare som stänger innan samlingen är skriven gör att samlingen misslyckas, och den skickas om i sin helhet. En mottagare som accepterar anslutningen och sedan kastar det den läser ser levererad ut. Deduplicera på externalId (CEF) eller id (JSON).
  • Auth-headern och HMAC-signaturen gäller bara HTTPS. Begränsa lyssnaren till fremforge med TLS och, om du behöver, en tillåtelselista för källadresser.

Verifiera signaturen

HMAC:en beräknas över exakt de byte som bodyn består av, i den ordning de kommer. Tolka och serialisera inte om innan du verifierar.

Mottagare i Node.js (tidsstämpel och body):

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(req, body) {
  const sig = req.headers['x-fremforge-signature'];
  const ts = req.headers['x-fremforge-timestamp'];
  if (!sig?.startsWith('sha256=') || !ts) throw new Error('missing headers');

  // 5-minute replay window. Adjust to your tolerance.
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) throw new Error('stale');

  const expected = createHmac('sha256', SHARED_SECRET)
    .update(`${ts}.${body}`)
    .digest('hex');
  const got = sig.slice(7); // strip "sha256="
  if (expected.length !== got.length) throw new Error('bad sig');
  if (!timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(got, 'hex'))) {
    throw new Error('bad sig');
  }
}

Mottagare i Python (tidsstämpel och body):

import hmac, hashlib, time

def verify(headers, raw_body: bytes) -> None:
    sig = headers.get('X-Fremforge-Signature', '')
    ts = headers.get('X-Fremforge-Timestamp', '')
    if not sig.startswith('sha256=') or not ts:
        raise ValueError('missing headers')
    if abs(time.time() - int(ts)) > 300:
        raise ValueError('stale')
    expected = hmac.new(
        SHARED_SECRET.encode(),
        f'{ts}.'.encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    if not hmac.compare_digest(expected, sig[7:]):
        raise ValueError('bad sig')

I läget endast body utelämnar du tidsstämpeln: HMAC-SHA256 över raw_body.

Felhantering, omförsök och omsändning

  • Varje begäran har en tidsgräns på 10 sekunder.
  • Ett svar som inte är 2xx, en tidsgräns som löper ut eller ett anslutningsfel lämnar händelserna i kön. Endpointen backar: 5 sekunder, fördubblat för varje fel i följd, högst 10 minuter, med ±20 % jitter. När mottagaren svarar igen töms kön i ordning.
  • Efter 5 fel i följd visar endpointen failing med orsaken till det senaste felet. Leveransen fortsätter att försöka; ingenting behöver slås av och på. Att inaktivera en endpoint gör att nya händelser inte längre köas för den; att aktivera den igen återupptar leveransen direkt.
  • En 413 halverar samlingen. En enskild händelse som mottagaren ändå nekar som för stor kasseras och räknas.
  • Olevererade händelser sparas under organisationens lagringstid för granskning (Organisationsadmin → Auth-policy; 90 dagar som standard), och högst 200 000 per endpoint. Äldre kasseras och räknas.
  • Kortet Leverans på endpointens sida (och API:ts block delivery, dropped_total) visar antalet väntande händelser, den äldsta av dem, nästa försök, och totalt levererade och kasserade. En händelse kasseras, och levereras aldrig till den endpointen om du inte skickar om den, bara i dessa fall: mottagaren svarade 413 på just den händelsen ensam; den väntade fortfarande när den blev äldre än din lagringstid för granskning; endpointen hade redan 200 000 väntande händelser, och då går de äldsta först; eller själva granskningshändelsen finns inte längre, vilket inte händer inom lagringstiden. Inget annat kasserar en händelse: en mottagare som felar eller är inaktiverad gör bara att händelser väntar.
  • Skicka om köar dina granskningshändelser igen från en tidpunkt (och, när fyndhändelser är tillgängliga, på en endpoint som inkluderar dem, dess fyndhändelser i samma fönster, aldrig äldre än när endpointen började ta emot dem): Skicka om från (UTC) på endpointens sida, eller POST /_app/api/v1/orgs/<org>/siem/endpoints/<id>/replay med {"since": "<ISO 8601>", "until": "<ISO 8601, optional>"}. since får ligga högst din lagringstid för granskning bakåt i tiden. En begäran köar högst 100 000 händelser och returnerar next_since när fler återstår. Händelser som fortfarande väntar köas inte två gånger; omsända händelser behåller sitt id.

Leveransen sker minst en gång: ett omförsök efter ett förlorat svar, eller en omsändning, skickar en händelse igen. Deduplicera på id.

Populära SIEM-system

SIEMTar emot över HTTPS viaInställningar i fremforgeDirekt från fremforge?
Cortex XSIAMHTTP log collectorAuth-header Authorization: <API key>, format jsonJa
SplunkHTTP Event Collector, /services/collector/rawAuth-header Authorization: Splunk <token>, format ndjsonJa
Microsoft SentinelAzure Monitor Logs Ingestion APIKräver en kortlivad Entra ID-tokenNej, relä
ElasticHTTP endpoint-input i Elastic Agent / FilebeatSignaturen omfattar endast body, eventuellt en hemlig headerJa
IBM QRadarLoggkälla av typen HTTP Receiver, eller en loggkälla av typen TLS SyslogFormat cefJa, om lyssnaren går att nå publikt
Google SecOpsWebhook-feedURL med ?key=&secret=, format ndjsonJa

Cortex XSIAM

Tar emot JSON från tredje part via en HTTP log collector (Settings → Data Sources & Integrations → + Add New → HTTP). Välj Log Format: JSON; den Vendor och Product du anger namnger datasetet <vendor>_<product>_raw. Insamlarens URL har formen https://api-<tenant external URL>/logs/v1/event och tar sin API-nyckel i Authorization (Palo Altos dokumentation).

Konfiguration: URL = insamlarens URL; auth-header Authorization = insamlarens API-nyckel; format json.

Exempel på regler för parsning och datamodell för fremforge_audit_raw

Parsningsregel (Settings → Configurations → Data Management → Parsing Rules), så att _time är fremforges händelsetid i stället för ankomsttiden. created_at har alltid tre decimaler och ett Z:

[INGEST:vendor="fremforge", product="audit", target_dataset="fremforge_audit_raw", no_hit=keep]
alter _time = parse_timestamp("%Y-%m-%dT%H:%M:%E3SZ", created_at);

Datamodellregel (Settings → Configurations → Data Management → Data Model Rules):

[MODEL: dataset="fremforge_audit_raw"]
filter action != "fremforge.siem.test"
| alter
    xdm.event.id = id,
    xdm.event.type = "fremforge_audit",
    xdm.event.original_event_type = action,
    xdm.event.operation_sub_type = action,
    xdm.event.description = to_json_string(fields),
    xdm.observer.vendor = "fremforge",
    xdm.observer.product = "fremforge",
    xdm.observer.unique_identifier = tenant_id,
    xdm.source.user.identifier = actor;

Båda förutsätter att XSIAM lagrar JSON-nycklarna på toppnivå som kolumner. Om ditt dataset bara visar _raw_log, läs värdena med json_extract_scalar(_raw_log, "$.created_at") och så vidare.

Datamodellregel för fyndhändelser

Fyndhändelser (kommer snart) hamnar i samma dataset. Deras larmfält är namngivna efter XDM:s, så de mappas ett till ett. När du använder regeln tillsammans med granskningsregeln ovan, lägg till | filter not (action ~= "^security\.finding\.") i granskningsregeln så att varje händelse modelleras en gång:

[MODEL: dataset="fremforge_audit_raw"]
filter action ~= "^security\.finding\."
| alter
    xdm.event.id = id,
    xdm.event.type = "fremforge_finding",
    xdm.event.original_event_type = action,
    xdm.event.outcome = json_extract_scalar(fields, "$.finding.state"),
    xdm.event.outcome_reason = json_extract_scalar(fields, "$.finding.transition_reason"),
    xdm.alert.name = json_extract_scalar(fields, "$.alert_name"),
    xdm.alert.category = json_extract_scalar(fields, "$.alert_category"),
    xdm.alert.severity = json_extract_scalar(fields, "$.alert_severity"),
    xdm.alert.description = json_extract_scalar(fields, "$.alert_description"),
    xdm.alert.original_alert_id = json_extract_scalar(fields, "$.original_alert_id"),
    xdm.target.resource.name = coalesce(json_extract_scalar(fields, "$.location.image.reference"), json_extract_scalar(fields, "$.location.repository"), json_extract_scalar(fields, "$.location.package.name")),
    xdm.observer.vendor = "fremforge",
    xdm.observer.product = "fremforge",
    xdm.observer.unique_identifier = tenant_id,
    xdm.source.user.identifier = actor;

HTTP log collector tar emot de här händelserna som loggposter. För att skapa ett larm i XSIAM från dem, lägg till en korrelationsregel på datasetet, till exempel på action = "security.finding.opened" och alert_severity critical eller high, och gruppera på original_alert_id så att de senare händelserna om samma fynd (dismissed, resolved) knyts till det.

Splunk

Tar emot via HTTP Event Collector. /services/collector/raw tar bodyn som den är; ange sourcetype=_json i URL:en så att varje rad blir en händelse. Token skickas i Authorization: Splunk <token>. Med indexer acknowledgement påslaget behöver raw-endpointen också ett kanal-GUID; lägg det i URL:en som channel=<GUID> (Splunks dokumentation).

Konfiguration: URL https://<hec-host>:8088/services/collector/raw?sourcetype=_json; auth-header Authorization = Splunk <token>; format ndjson.

Microsoft Sentinel

Tar emot egen JSON via Azure Monitors Logs Ingestion API, till en egen tabell _CL som definieras av en datainsamlingsregel (DCR). Begäran går till {endpoint}/dataCollectionRules/{dcrImmutableId}/streams/{streamName}?api-version=2023-01-01 med en bearer-token från Entra ID via flödet client credentials (scope https://monitor.azure.com/.default), och bodyn måste vara en JSON-array (Microsofts dokumentation). Det äldre HTTP Data Collector API nådde slutet av sin support den 14 september 2026; bygg inte på det.

Konfiguration: token löper ut efter ungefär en timme, så en statisk auth-header kan inte bära den. Placera reläet emellan: det hämtar och cachar token och vidarebefordrar bodyn. Använd formatet json_array, så att bodyn redan är den array som API:t vill ha. Mappa created_at till TimeGenerated i DCR-transformationen.

Elastic

Tar emot via HTTP endpoint-inputen i Elastic Agent (integrationen Custom HTTP Endpoint Logs) eller Filebeat. Den lyssnar på en egen port (standard 8000), kan terminera TLS och kan kontrollera en HMAC över bodyn (Elastics dokumentation).

Konfiguration: signaturen omfattar endast body; ange i inputen hmac.header: X-Fremforge-Signature, hmac.key: <the endpoint's HMAC secret>, hmac.type: sha256 och hmac.prefix: "sha256=". För att även använda inputens alternativ för en hemlig header, ange samma headernamn och värde som endpointens auth-header. Format json, eller ndjson för samlingar. Indexera actor, action och tenant_id som keyword.

IBM QRadar

Tar emot bodyn i HTTP POST via en loggkälla som använder protokollet HTTP Receiver. QRadar kör en HTTP- eller HTTPS-server på en Event Collector (standardport 12469); HTTPS kräver ett certifikat utfärdat av en CA, och ömsesidig TLS är valfri (IBM:s dokumentation). CEF-rader tolkas av QRadars Universal CEF DSM.

Konfiguration: URL = mottagarens publika HTTPS-URL, eller syslog+tls://<collector>:6514 för en loggkälla av typen TLS Syslog; format cef. fremforge kan inte visa upp ett klientcertifikat, så lämna ömsesidig TLS avstängd. När Event Collector inte går att nå från internet vidarebefordrar en omvänd proxy eller ett syslog-relä i din kant till den.

Google SecOps (Chronicle)

Tar emot via en webhook-feed (Settings → Feeds → Add new, källtypen Webhook, välj sedan en loggtyp). Begäranden går till feedens endpoint ...:importPushLogs med en API-nyckel och feedens hemlighet, som headers X-goog-api-key och X-Webhook-Access-Key, eller som ?key=<API key>&secret=<secret>. Flera radseparerade JSON-objekt kan dela en begäran, upp till 4 MB (Googles dokumentation). Det finns ingen färdig parser för fremforge; du behöver en loggtyp och en parser för de här händelserna.

Konfiguration: URL = feedens endpoint med ?key=<API key>&secret=<secret> (lagras krypterad och visas med ***); format ndjson; max storlek per begäran högst 4096 KiB.

Ett relä som verifierar signaturen

Sentinel, och varje mottagare som behöver mer än en header eller en kortlivad token, använder en liten tjänst: verifiera fremforges signatur och vidarebefordra sedan bodyn oförändrad med SIEM-systemets inloggningsuppgift. Den här versionen använder bara Pythons standardbibliotek; ange TARGET_URL och TARGET_AUTH för ditt SIEM. För Sentinel lägger du till hämtningen av token; med formatet json_array är bodyn redan den array den vill ha. MAX_BODY motsvarar standardstorleken 1 MiB per begäran. Reläet vidarebefordrar en korrekt signerad händelse med inloggningsuppgiften, svarar 401 på en felaktig signatur eller en tidsstämpel som är mer än fem minuter gammal, och 502 när målet inte går att nå.

"""fremforge SIEM forwarder -> SIEM relay: verify the signature, forward with the SIEM's credential."""
import hashlib
import hmac
import os
import time
import urllib.error
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

SECRET = os.environ["FREMFORGE_SIEM_SECRET"].encode()
TARGET_URL = os.environ["TARGET_URL"]    # e.g. https://api-<tenant>/logs/v1/event
TARGET_AUTH = os.environ["TARGET_AUTH"]  # e.g. "<XSIAM API key>" or "Splunk <HEC token>"
MAX_SKEW_S = 300
MAX_BODY = 1024 * 1024


class Relay(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get("Content-Length") or 0)
        if length <= 0 or length > MAX_BODY:
            return self.reply(413)
        body = self.rfile.read(length)
        sig = self.headers.get("X-Fremforge-Signature", "")
        ts = self.headers.get("X-Fremforge-Timestamp", "")
        if not sig.startswith("sha256=") or not ts.isdigit():
            return self.reply(401)
        if abs(time.time() - int(ts)) > MAX_SKEW_S:
            return self.reply(401)
        expected = hmac.new(SECRET, ts.encode() + b"." + body, hashlib.sha256).hexdigest()
        if not hmac.compare_digest(expected, sig[7:]):
            return self.reply(401)
        req = urllib.request.Request(
            TARGET_URL,
            data=body,
            method="POST",
            headers={"Authorization": TARGET_AUTH, "Content-Type": "application/json"},
        )
        try:
            # fremforge gives up after 10 s; answer before that.
            with urllib.request.urlopen(req, timeout=4) as res:
                status = res.status
        except urllib.error.HTTPError as e:
            status = e.code
        except OSError:
            status = 0
        # Pass failure back, so fremforge keeps the event and retries it.
        self.reply(200 if 200 <= status < 300 else 502)

    def reply(self, code):
        self.send_response(code)
        self.send_header("Content-Length", "0")
        self.end_headers()

    def log_message(self, fmt, *args):
        pass


if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", int(os.environ.get("PORT", "8080"))), Relay).serve_forever()

Kör det bakom din vanliga TLS-terminering: fremforge levererar bara till publika https://-URL:er. Håll reläets svarstid under 10 sekunder, och returnera något annat än 2xx när SIEM-systemet nekade händelsen. Ett relä som alltid svarar 200 tappar händelser: fremforge betraktar ett 2xx som levererat och tar bort händelsen ur kön.

Generisk webhook

Vilken HTTPS-endpoint som helst som returnerar 2xx när den har accepterat begäran. Verifiera med kodexemplen ovan. Det passar SIEM-system som du driftar själv (Wazuh, Graylog, …) och HTTP-intag som Logflare eller Datadog.

Hälsa på operatörssidan

fremverks operatörer ser varje endpoints tillstånd på hälsosidan för SIEM i operatörsportalen. Ett larm utlöses när en endpoints äldsta olevererade händelse är mer än 24 timmar gammal, och vi kontaktar organisationens ägare. Larma också i ditt SIEM när händelser från fremforge uteblir.

Vad som inte går över protokollet

  • Syslog över UDP eller okrypterad TCP. Syslog stöds bara med TLS.
  • Klientcertifikat (ömsesidig TLS) mot din mottagare.
  • Mer än en egen header per endpoint.
  • Utskick mellan tenants. Varje endpoint är knuten till en tenant. Det finns ingen spegel på operatörssidan; händelser från din organisation lämnar aldrig kedjan för din tenant.

Relaterat