Indicator of Compromise (IOC) matching is one of the most practical ways to turn threat intelligence into detections. In Google SecOps, you can maintain collections of malicious IP addresses, domains, URLs, file hashes, and other indicators and compare them against normalized UDM events.
For straightforward matching, lists are useful. For more sophisticated detection and enrichment, Data Tables provide a structured way to associate an IOC with additional information such as its source, confidence, severity, expiration, or threat classification.
The key difference is simple: a list answers whether a value exists, while a Data Table can also explain what that value means.
Lists versus Data Tables for IOC matching
A reference list is essentially a collection of values that you want to test against an event field.
For example, if you maintain a list called malicious_ips, a detection can conceptually ask:
$e.principal.ip in %malicious_ips
This works well when the detection question is simply:
Does this event contain a value that appears in my IOC collection?
Lists are particularly useful for straightforward allowlists, blocklists, and basic IOC filtering.
Data Tables become more useful when an indicator needs additional context.
For example:
| indicator | indicator_type | source | severity | expires |
|---|---|---|---|---|
| 203.0.113.25 | IP | Feed-A | High | 2026-10-01 |
| malicious.example | Domain | Feed-B | Medium | 2026-09-25 |
Now the detection does not merely know that an IOC matched. It can potentially use information associated with that IOC.
This creates an important separation:
Detection rule: describes how suspicious activity is identified.
Data Table: contains changing intelligence and contextual information.
Build the IOC dataset around the detection question
A common mistake is to import an intelligence feed into Google SecOps without first considering how the detection will use the data.
Instead, begin with the question you want the rule to answer.
For example:
- Should every connection to a malicious IP trigger a detection?
- Should only high-confidence indicators trigger detections?
- Should expired indicators be ignored?
- Should an IOC matter only when associated with a particular threat family?
- Does the combination of IP address and port determine whether the activity is suspicious?
The answers determine how your Data Table should be structured.
If the detection only needs to know whether an IP exists, a single indicator column may be sufficient.
If an IP, port, threat family, and confidence value all belong to the same intelligence record, keeping those values together in one row makes the relationship explicit.
The important design principle is:
If multiple attributes describe the same intelligence record, keep them together and match them as a row rather than treating them as unrelated values.
Use column-based matching for simple IOC lookups
Column-based matching is appropriate when you only need to determine whether an event value occurs in a particular column.
A conceptual pattern looks like this:
events:
$e.principal.ip in %malicious_ips.ip
condition:
$e
For hostname matching, the logic could follow the same pattern:
events:
$e.principal.hostname in %known_bad_hosts.hostname
condition:
$e
The important characteristic is that the event value is being checked for membership in a column.
This is ideal for simple questions such as:
Is this IP present in our malicious-IP dataset?
or:
Is this hostname present in our known-bad-host dataset?
It becomes less appropriate when multiple attributes need to correspond to the same intelligence record.
Use row-based matching when IOC context matters
This is one of the most important differences between basic list matching and structured Data Table matching.
Imagine a Data Table containing:
| ip | port | threat_family |
|---|---|---|
| 203.0.113.25 | 443 | ExampleThreat |
| 203.0.113.25 | 8080 | AnotherThreat |
Suppose an event contains the IP 203.0.113.25 and port 8080.
A poorly designed detection could independently find the IP and port but fail to establish that they belong to the same intelligence record.
Row-based matching solves this problem.
Conceptually:
events:
$e.target.ip = %ioc_table.ip
$e.target.port = %ioc_table.port
The objective is for both conditions to resolve against the same Data Table row.
This distinction becomes especially important when your intelligence contains combinations such as:
- IP + port
- domain + URL path
- hash + file type
- username + application
- hostname + process
- indicator + threat family
Whenever the relationship between attributes matters, think in terms of rows rather than independent columns.
Add expiration and confidence to your IOC data
One of the strongest reasons to use Data Tables is the ability to associate lifecycle information with an indicator.
For example:
| indicator | confidence | expires_at | source |
|---|---|---|---|
| suspicious.example | 90 | future timestamp | Feed-A |
| another.example | 60 | future timestamp | Feed-B |
The detection can associate an event with the appropriate indicator and then evaluate additional attributes.
This allows the intelligence dataset to answer questions such as:
- How trustworthy is this indicator?
- Where did it originate?
- Is it still active?
- What category does it belong to?
- How should an analyst interpret the match?
This is generally easier to maintain than repeatedly modifying the detection rule whenever threat intelligence changes.
A useful architecture is:
Rule = detection behavior
Data Table = indicator + context + lifecycle
When an intelligence feed changes, the Data Table can change without requiring the detection logic to be redesigned.
Choose the UDM field carefully
IOC matching is only as reliable as the event field being compared.
An IP address can appear in several semantic locations within UDM events, including fields representing the initiating system, destination system, or other entities.
For example:
$e.principal.ip
and:
$e.target.ip
do not necessarily represent the same thing.
If your Data Table contains malicious destination IP addresses, matching them against a field representing the source system could produce misleading detections.
Before writing the rule, inspect representative events and determine exactly what the selected UDM field represents.
A useful practice is to make the intelligence meaning explicit in the Data Table rather than simply copying UDM field names into the table.
For example, a column named malicious_destination_ip communicates more intent than a generic column named ip.
Normalize indicators before matching
IOC feeds frequently contain formatting inconsistencies.
Examples include:
- Different capitalization of domain names
- Leading or trailing whitespace
- Trailing dots in fully qualified domain names
- Different URL representations
- Inconsistent IPv6 formatting
- Hashes containing accidental whitespace
- Duplicate indicators from multiple sources
These issues can produce false negatives even when the underlying indicator is identical.
For that reason, normalization should ideally happen before the indicator reaches the matching layer.
Think of your Data Table as a curated detection dataset, rather than simply a storage location for raw threat-intelligence output.
For domains and other case-insensitive values, make sure your matching strategy accounts for case differences where appropriate.
The objective is to ensure that a formatting difference does not prevent a legitimate IOC from matching.
Separate broad intelligence from alerting logic
Not every IOC should necessarily generate an alert.
A Data Table can contain a relatively broad collection of intelligence while the detection rule applies additional conditions before creating a detection.
For example, a table might contain:
indicator
source
confidence
first_seen
last_seen
expires_at
classification
A hunting query could investigate all matching indicators.
A detection rule could require the indicator to meet a particular confidence threshold or classification before generating an alert.
This creates an important separation between intelligence collection and alert generation.
It also means that analysts can investigate lower-confidence indicators without necessarily flooding the detection environment with alerts.
Use Data Tables for enrichment
Data Tables do not have to be limited to malicious indicators.
They can also provide organizational context.
Consider a table containing sensitive systems:
| hostname | environment | owner | criticality |
|---|---|---|---|
| server-a | production | Finance | critical |
| server-b | development | Engineering | low |
A security event can match a hostname and use the corresponding row to provide additional context.
The same approach can be applied to:
- Privileged accounts
- Critical assets
- Approved security scanners
- Authorized administrative tools
- Temporary exceptions
- Third-party infrastructure
- Threat classifications
- Business-critical applications
This can turn a basic IOC detection into a context-aware detection.
For example, the same suspicious connection may deserve different investigation priority depending on whether it involves a critical production asset or a disposable test system.
Make the resulting detection explainable
An IOC alert should ideally provide more information than simply:
IOC matched.
Where practical, expose relevant matching information through the rule’s outcome data.
Useful context can include:
$matched_indicator
$source
$severity
The goal is to help the analyst quickly answer:
- Which indicator matched?
- Which event field matched it?
- Where did the indicator come from?
- What confidence or severity was associated with it?
- What additional context belongs to the indicator?
This reduces the amount of manual investigation required to understand why the detection fired.
It also makes rule testing more effective because you can verify not only that a detection occurred but also that the expected intelligence record was selected.
Test with deliberately ambiguous data
A powerful way to test a Data Table detection is to create rows that could expose incorrect correlation.
For example:
| ip | port | label |
|---|---|---|
| 203.0.113.25 | 443 | Row-A |
| 203.0.113.25 | 8443 | Row-B |
Then test events containing:
- The first IP and first port
- The first IP and second port
- An unknown IP and a known port
This is more useful than testing only a straightforward positive match.
The objective is to prove that the rule understands the relationship between values.
If the detection is intended to match an IP and port from the same intelligence record, an event containing values from two different rows should not accidentally qualify.
This type of negative testing can uncover logic errors that are otherwise difficult to notice.
Treat IOC data as lifecycle-managed content
Threat intelligence changes over time.
An indicator that was valuable yesterday may become stale, revoked, duplicated, or irrelevant to your environment.
Data Table management should therefore consider:
- Who owns the dataset?
- Which source supplied each indicator?
- When was the indicator last refreshed?
- When should it expire?
- How are duplicates handled?
- How are revoked indicators removed?
- What happens when an intelligence feed stops updating?
- How are temporary indicators distinguished from long-lived ones?
This changes IOC management from a simple import task into an operational detection process.
A particularly useful practice is to make timestamps part of the data model whenever indicator validity is time-dependent.
That allows the detection logic to reason about whether an indicator should still influence current events.
Keep detection logic separate from intelligence
A maintainable Google SecOps architecture can be divided into four conceptual layers.
Intelligence layer
Contains indicators, sources, confidence, classifications, and lifecycle information.
Normalization layer
Ensures indicators use consistent representations before matching.
Detection layer
Contains YARA-L logic describing suspicious activity.
Investigation layer
Exposes the matched indicator and supporting context to analysts.
The central idea is that the Data Table should change frequently while detection logic should change comparatively slowly.
When a threat-intelligence feed changes, update the intelligence data.
When the organization’s detection requirements change, update the rule.
Keeping those responsibilities separate makes detection engineering easier to maintain and review.
Common mistakes to avoid
- Using column membership when row correlation is required. A simple membership check does not establish that multiple values came from the same intelligence record.
- Embedding large IOC collections directly in rules. This makes intelligence updates unnecessarily dependent on rule changes.
- Matching the wrong UDM field. The meaning of an event field matters as much as the value stored in it.
- Ignoring normalization. Small formatting differences can prevent legitimate indicators from matching.
- Treating every IOC as equally trustworthy. Preserve confidence and source information when those attributes matter.
- Ignoring indicator expiration. Threat intelligence should be treated as time-sensitive information where appropriate.
- Testing only positive cases. Negative and cross-row tests are essential for validating correlation logic.
- Using Data Tables only as flat IOC lists. Their greater value comes from combining indicators with structured context.
The practical takeaway
For straightforward IOC matching, the mental model is:
UDM field → Data Table column → membership test
For contextual IOC matching, use:
UDM field A ─┐
UDM field B ─┼→ same Data Table row → contextual detection
UDM field C ─┘
The second pattern is where structured Data Tables become particularly valuable.
Instead of treating threat intelligence as a collection of unrelated strings, you can treat it as structured security knowledge.
That allows an IOC to carry information about its source, confidence, lifecycle, classification, and relationship with other attributes. At the same time, the YARA-L rule can remain focused on describing what constitutes suspicious activity.
The result is a cleaner separation between detection logic and changing intelligence, easier maintenance, stronger testing, and more informative alerts.
For new IOC-matching implementations, the most useful question is therefore not simply:
“How do I check whether this IOC exists?”
Instead, ask:
“What information about this IOC does my detection need, and does that information belong to the same intelligence record?”
If the answer is simply existence, a list or column lookup may be enough. If the answer involves relationships, context, confidence, expiration, or enrichment, a structured Data Table is the more appropriate design pattern.
