AI in Cybersecurity

Triaging alerts at scale

A security team gets far more alerts than they can review one by one, so a model's real job is often ranking which alert to look at first, not deciding anything alone.

On this page 5
  1. Why it exists
  2. How it works
  3. A real example you have seen
  4. Remember this
  5. What to learn next

One lesson, three depths. Pick the one that fits you today — you can switch any time.

Beginner — No maths. Plain English.

Alert triage means deciding which security alert to look at first, when there are far more alerts than reviewers.

Think about a hospital emergency room. Patients do not get seen in the order they arrive. A nurse quickly assesses everyone at the door. Someone with chest pain gets seen before someone with a mild headache, even if the headache walked in first. That quick assessment is triage — sorting by urgency, not by arrival time.

A security team is called a SOC (Security Operations Center). It faces the identical problem. Hundreds or thousands of alerts arrive daily, and only a few analysts can review them deeply.

Why it exists

Security tools generate an alert for almost anything unusual. A failed login, an odd process, a new connection. Most of these are completely harmless. A person mistyped a password. A new but legitimate tool was installed. A small number are the start of a real breach.

Reviewing every alert in arrival order means a genuinely dangerous one can sit behind fifty harmless ones for hours. It is exactly like the headache being seen before the chest pain, purely because it got to the door first. Alert fatigue is analysts becoming numb to alerts, because almost all of them turn out to be nothing. It is one of the best-documented problems in security operations. It is a direct consequence of arrival-order review.

A triage model does not usually decide "block" or "ignore" on its own. Its job is to combine everything known about an alert. How it looks, what it touched, how sensitive the system is. It produces a single risk score. A human then reviews the most dangerous alerts first, rather than the ones that happened to arrive first.

How it works

100 alerts arrive today, in random order
        |
        v
  [ model scores every alert: 0.00 to 1.00 ]
        |
        v
  Sorted by risk score, highest first
        |
        v
  Analyst reviews the top 10 -- the ones the model
  thinks are most likely to be real
        |
        v
  Everything below a cutoff waits, or auto-closes
  with low confidence, for a human to check later

The value here is entirely in the ordering. A model that ranks alerts well lets a small team catch the dangerous ones first, even without reviewing everything.

A real example you have seen

Fraud alerts on your bank account work the same way behind the scenes. Not every unusual transaction gets an urgent phone call. The bank's system ranks them. The ones that look most dangerous get a human's attention fastest — the same triage idea, one domain over.

Remember this

  • Security teams face far more alerts than they can individually review. Triage means deciding what to look at first, not deciding everything alone.
  • Reviewing alerts strictly by arrival order lets a genuinely dangerous alert wait behind harmless ones purely by bad luck.
  • A triage model's output is usually a risk score for ranking, not a final block/allow decision.

What to learn next

Developer — Code and libraries.

Setup

bash
pip install scikit-learn numpy

Minimal runnable code

A small synthetic set of past alerts, labelled by whether they turned out to be real incidents, used to rank a fresh batch of today's alerts.

alert_triage.py
import numpy as np
from sklearn.linear_model import LogisticRegression

# Each row: [failed_logins, off_hours(0/1), asset_criticality(1-3), known_bad_ip(0/1)]
alerts = np.array([
    [1, 0, 1, 0], [2, 0, 1, 0], [1, 1, 1, 0], [8, 1, 3, 1],
    [1, 0, 2, 0], [3, 0, 1, 0], [6, 1, 2, 1], [1, 0, 1, 0],
    [2, 1, 1, 0], [9, 1, 3, 1], [1, 0, 1, 0], [2, 0, 2, 0],
])
# was this alert, historically, a confirmed real incident?
was_real_incident = np.array([0,0,0,1,0,0,1,0,0,1,0,0])

model = LogisticRegression()
model.fit(alerts, was_real_incident)

# Today's queue: 6 fresh alerts, in the order they happened to arrive
alert_ids = ["A101", "A102", "A103", "A104", "A105", "A106"]
todays_alerts = np.array([
    [1, 0, 1, 0], [7, 1, 3, 1], [2, 0, 1, 0],
    [1, 1, 2, 0], [5, 1, 2, 1], [1, 0, 1, 0],
])

risk_scores = model.predict_proba(todays_alerts)[:, 1]

print("arrival order (what an analyst sees by default):")
for aid, score in zip(alert_ids, risk_scores):
    print(f"  {aid}  risk={score:.2f}")

print()
print("risk-ranked order (review budget = top 2 today):")
order = np.argsort(-risk_scores)
for rank, i in enumerate(order[:2], start=1):
    print(f"  #{rank}: {alert_ids[i]}  risk={risk_scores[i]:.2f}")
Output
arrival order (what an analyst sees by default):
  A101  risk=0.01
  A102  risk=0.93
  A103  risk=0.03
  A104  risk=0.02
  A105  risk=0.57
  A106  risk=0.01

risk-ranked order (review budget = top 2 today):
  #1: A102  risk=0.93
  #2: A105  risk=0.57

What actually happened

In arrival order, an analyst working strictly top-to-bottom with limited time would review A101, A102, maybe A103 before running out of time — catching the dangerous A102 mostly by luck, since it happened to arrive second.

Sorted by risk score, A102 (0.93) and A105 (0.57) rise straight to the top, and both are the alerts sharing features with confirmed past incidents: several failed logins, off-hours activity, and a known-bad source IP. The genuinely low-risk alerts — A101, A103, A104, A106 — correctly sink to the bottom, regardless of when they arrived.

  • predict_proba(...)[:, 1] again returns the probability of the positive class — "was a real incident" — exactly as in the credit-scoring lesson. Ranking alerts and ranking loan applicants by risk are the same underlying operation.
  • np.argsort(-risk_scores) sorts ascending by default; negating the scores first is the standard trick to get a descending sort using the same function.

Common mistakes

Training on too few confirmed incidents. Real incidents are rare, exactly like fraud. A model trained on a handful of historical incidents, as this toy example is, needs far more real, confirmed examples before it is trustworthy — see class weights and imbalanced data.

Auto-closing low-scored alerts with no human ever checking a sample. Even a good model misses things. Mature triage pipelines route a random sample of "low risk" alerts to a human anyway, specifically to catch what the model is missing and to keep measuring its real-world accuracy.

Optimising only for catching more incidents. A model that flags everything as high risk catches every real incident and is worthless, because it gives the analyst no ordering information at all. The goal is a ranking that concentrates real incidents near the top, not a model that screams about everything.

No feedback loop. If confirmed incident outcomes are never fed back into retraining, the model's ranking quality slowly drifts away from what analysts are actually finding, and nobody notices until performance has already degraded.

Try it yourself

Add a seventh alert, A107 = [4, 0, 3, 0] — moderate failed logins, business hours, but on a highly critical asset (criticality 3), with no known-bad IP. Score it and see where it lands in the ranking. It is a genuinely ambiguous case, and a good exercise in reading what the model actually learned from asset_criticality.

What to learn next

Researcher — Mathematics and papers.

Triage as a ranking, not classification, problem

Framing alert triage as binary classification (real incident vs not) optimises the wrong objective when the true constraint is a fixed daily review budget k. The operationally relevant quantity is closer to:

Precision@k  =  ( true incidents in the top k scored alerts )  /  k

This is the same metric family used in ranking metrics for recommender systems — alert triage and product ranking share the mathematical structure of "limited attention, rank to spend it well," despite looking like unrelated domains.

Combining signals: rules, anomaly scores, and supervised risk

Production triage pipelines rarely rely on one model. A common architecture combines three distinct signal types into one score:

  • Deterministic rule matches (e.g. a MITRE ATT&CK technique signature) — high precision, interpretable, but only catches known patterns.
  • Unsupervised anomaly scores (e.g. isolation forest or autoencoder reconstruction error on behavioural features) — catches novel patterns at the cost of a higher false-positive rate.
  • Supervised risk score, as demonstrated above, trained on confirmed historical incident labels where they exist.

These are typically combined via a final ranking model (often a gradient-boosted tree) that treats each of the above as an input feature rather than a competing final answer, since each captures a different, only partially overlapping kind of signal.

The label scarcity and label lag problem

As in AML, confirmed "this alert was a real incident" labels are rare, arrive with a substantial delay (an investigation can take days to weeks to close), and confirmed negatives are almost never explicitly labelled — most alerts are never escalated at all, which is evidence of low risk but not proof. This pushes mature SOC ML systems toward the same weakly-supervised and semi-supervised techniques discussed for AML, using analyst dispositions as a noisy label source rather than ground truth.

Cost

Analyst time is the binding constraint the entire triage system exists to protect, and it is directly measurable: Mean Time to Triage (MTTT) and alert-to-analyst ratio are standard SOC operational metrics. A triage model's value is properly measured against these operational metrics, not against an offline classification accuracy number, because the deployed goal is analyst time saved and incidents caught within a fixed budget, not classification accuracy in isolation.

Key references

  • MITRE ATT&CK. Enterprise Matrix — attack.mitre.org — the standard taxonomy of attacker techniques used to build rule-based signals feeding triage models.
  • Sundaramurthy, S. C., et al. (2016). Turning Contextual Analysis into Automated Decision-Making for Security Operations. USENIX — one of the more widely cited empirical studies of SOC analyst workflow and the alert-fatigue problem.
  • Vielberth, M., et al. (2020). Security Operations Center: A Systematic Study and Open Challenges. IEEE Access — a broad survey of SOC architecture including alert triage automation.

Current state

Large language models are increasingly used as a triage aid — summarising an alert's context and suggesting an investigation path for a human analyst — but current practice keeps a human in the loop for any containment action; fully autonomous incident response remains rare and is treated with active caution given the cost of a wrong automated action on production systems.

What to learn next