November 1, 2026
STIX and TAXII: Turning Threat Data into Intelligence
A security analyst sees a strange domain in an alert at 7:42 on a Tuesday morning. Is it malicious? Is it tied to an incident another…

By Maram Raboudi
8 min read
A security analyst sees a strange domain in an alert at 7:42 on a Tuesday morning. Is it malicious? Is it tied to an incident another company had last week? Should it be blocked, or is it a cloud service shared by thousands of organizations that would take payroll down with it?
A spreadsheet of indicators cannot answer those questions. You need to know where the claim came from, how sure the author was, which campaign it belongs to, and whether it is still true today. Two open standards were built to carry exactly that context: STIX and TAXII.
The short answer: STIX (Structured Threat Information Expression) is a standard JSON format for describing cyber threat intelligence. TAXII (Trusted Automated Exchange of Intelligence Information) is an HTTPS protocol for sharing that intelligence between systems. STIX is the language; TAXII is the delivery route.
What Is STIX?
It is an open standard, maintained by OASIS, that describes cyber threat information as JSON. Instead of passing around disconnected values, a team describes objects and the relationships between them: an indicator, a phishing campaign, a malware family, an attack behavior, or a sighting in its own environment.
Imagine a fictional company receives a report about a phishing campaign targeting its industry. The report mentions a suspicious domain, a malicious attachment, and a behavior seen after the attachment was opened. A flat list captures the domain and the file hash. A STIX representation preserves the rest of the picture:
What was observed: the domain or file, as an Indicator or Observed Data object.
What it may be associated with: a Campaign, Intrusion Set or Malware object.
What behavior was reported: an Attack Pattern, often pointing to a MITRE ATT&CK technique.
Where the claim came from: an Identity (the source) and external references to the original report.
How certain the analysts are: a
confidencevalue from 0 to 100.
How the objects relate: Relationship objects, such as "this indicator indicates that campaign".
-> Here is how those pieces fit in: One Identity object says who wrote the report. An Indicator holds the suspicious domain. A Campaign named "Payroll Lure" gives the activity a label, and an Attack Pattern points to MITRE ATT&CK technique T1566.001, spearphishing attachment. Two Relationship objects tie them together: the indicator "indicates" the campaign, and the campaign "uses" the technique. Later, a Sighting records that your own team saw the domain once.
That structure matters when new information arrives. If another team reports the same domain in a different context, analysts can compare the two records side by side without assuming they prove the same actor or campaign.
A STIX 2.1 Example
STIX content is JSON. Below is the fictional phishing report from the previous section, written as a STIX 2.1 bundle: one source, one indicator, one campaign, one attack pattern, two relationships and one local sighting. The domain, dates and IDs are made up, and the domain sits under example.com so nobody mistakes it for a live indicator.
{
"type": "bundle",
"id": "bundle--b92f4d1a-6c37-4e85-9a0d-7f1e5c3b8a24",
"objects": [
{
"type": "identity",
"spec_version": "2.1",
"id": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:00:00.000Z",
"modified": "2026-10-14T08:00:00.000Z",
"name": "Example Threat Intel Team",
"identity_class": "organization"
},
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Payroll lure domain",
"description": "Domain used in phishing emails that imitate an HR payroll update.",
"indicator_types": ["malicious-activity"],
"pattern": "[domain-name:value = 'payroll-update-portal.example.com']",
"pattern_type": "stix",
"valid_from": "2026-10-12T00:00:00.000Z",
"valid_until": "2027-01-12T00:00:00.000Z",
"confidence": 70,
"external_references": [
{
"source_name": "Example Industry Advisory 2026-041",
"url": "https://example.com/advisories/2026-041"
}
],
"object_marking_refs": [
"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
]
},
{
"type": "campaign",
"spec_version": "2.1",
"id": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Payroll Lure",
"first_seen": "2026-10-12T00:00:00.000Z",
"confidence": 50
},
{
"type": "attack-pattern",
"spec_version": "2.1",
"id": "attack-pattern--2b9e6f4a-1d73-4c0b-8e5a-6a7d3c9f1b22",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Spearphishing Attachment",
"external_references": [
{
"source_name": "mitre-attack",
"external_id": "T1566.001",
"url": "https://attack.mitre.org/techniques/T1566/001/"
}
]
},
{
"type": "relationship",
"spec_version": "2.1",
"id": "relationship--e1a4c8b7-3f52-4d19-b6a8-0c9d7e2f5a41",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"relationship_type": "indicates",
"source_ref": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"target_ref": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845"
},
{
"type": "relationship",
"spec_version": "2.1",
"id": "relationship--5c7f2a96-8b14-4e03-a9d5-1b6e3f8c0d72",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"relationship_type": "uses",
"source_ref": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845",
"target_ref": "attack-pattern--2b9e6f4a-1d73-4c0b-8e5a-6a7d3c9f1b22"
},
{
"type": "sighting",
"spec_version": "2.1",
"id": "sighting--a3d6e91c-7f08-4b25-8c4e-9e0b1a5d2f63",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-15T09:10:00.000Z",
"modified": "2026-10-15T09:10:00.000Z",
"sighting_of_ref": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"where_sighted_refs": ["identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10"],
"first_seen": "2026-10-15T07:42:00.000Z",
"last_seen": "2026-10-15T07:42:00.000Z",
"count": 1
}
]
}{
"type": "bundle",
"id": "bundle--b92f4d1a-6c37-4e85-9a0d-7f1e5c3b8a24",
"objects": [
{
"type": "identity",
"spec_version": "2.1",
"id": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:00:00.000Z",
"modified": "2026-10-14T08:00:00.000Z",
"name": "Example Threat Intel Team",
"identity_class": "organization"
},
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Payroll lure domain",
"description": "Domain used in phishing emails that imitate an HR payroll update.",
"indicator_types": ["malicious-activity"],
"pattern": "[domain-name:value = 'payroll-update-portal.example.com']",
"pattern_type": "stix",
"valid_from": "2026-10-12T00:00:00.000Z",
"valid_until": "2027-01-12T00:00:00.000Z",
"confidence": 70,
"external_references": [
{
"source_name": "Example Industry Advisory 2026-041",
"url": "https://example.com/advisories/2026-041"
}
],
"object_marking_refs": [
"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
]
},
{
"type": "campaign",
"spec_version": "2.1",
"id": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Payroll Lure",
"first_seen": "2026-10-12T00:00:00.000Z",
"confidence": 50
},
{
"type": "attack-pattern",
"spec_version": "2.1",
"id": "attack-pattern--2b9e6f4a-1d73-4c0b-8e5a-6a7d3c9f1b22",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"name": "Spearphishing Attachment",
"external_references": [
{
"source_name": "mitre-attack",
"external_id": "T1566.001",
"url": "https://attack.mitre.org/techniques/T1566/001/"
}
]
},
{
"type": "relationship",
"spec_version": "2.1",
"id": "relationship--e1a4c8b7-3f52-4d19-b6a8-0c9d7e2f5a41",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"relationship_type": "indicates",
"source_ref": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"target_ref": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845"
},
{
"type": "relationship",
"spec_version": "2.1",
"id": "relationship--5c7f2a96-8b14-4e03-a9d5-1b6e3f8c0d72",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-14T08:30:00.000Z",
"modified": "2026-10-14T08:30:00.000Z",
"relationship_type": "uses",
"source_ref": "campaign--c4a1d7e2-5b39-4e8f-a6d0-3f7b92e1c845",
"target_ref": "attack-pattern--2b9e6f4a-1d73-4c0b-8e5a-6a7d3c9f1b22"
},
{
"type": "sighting",
"spec_version": "2.1",
"id": "sighting--a3d6e91c-7f08-4b25-8c4e-9e0b1a5d2f63",
"created_by_ref": "identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10",
"created": "2026-10-15T09:10:00.000Z",
"modified": "2026-10-15T09:10:00.000Z",
"sighting_of_ref": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"where_sighted_refs": ["identity--8f2b8c1e-6a3d-4c58-9d7e-2b1f4a6c9e10"],
"first_seen": "2026-10-15T07:42:00.000Z",
"last_seen": "2026-10-15T07:42:00.000Z",
"count": 1
}
]
}How to Read This STIX Bundle
Every field in that bundle answers a question an analyst asks before acting. Here is what each one is doing.
The pattern field is the detection logic, written in the STIX pattern language. Here it says: match any domain name equal to the payroll lure domain. Mail gateways, proxies and SIEMs can translate that pattern into their own queries.
valid_from and valid_until give the indicator a lifespan. This one is meant to be trusted from 12 October 2026 to 12 January 2027, because domains get reassigned and an indicator with no end date turns into a false-positive factory.
confidence is a number from 0 to 100 set by the author. The indicator scores 70 and the campaign 50, which tells you the team trusts the domain more than it trusts the bigger story around it.
created_by_ref links every object to the Identity that wrote it, so you always know who is making the claim. external_references points to the original advisory and, on the attack pattern, to the ATT&CK page for the technique, so you can check the source yourself instead of taking anyone's word.
object_marking_refs attaches a handling label, here TLP:GREEN, which answers the question of who you may share this with. We come back to it later.
relationship_type is the verb that connects two objects, such as "indicates" or "uses". Without it you have a pile of facts; with it you have a story.
Finally, sighting_of_ref links a local observation back to the indicator it matched. Notice what a flat list would have lost: the campaign, the technique, the source, how sure they were, and how long the claim holds. Notice also that the Sighting is a separate object, written by the team that saw it. Local evidence sits beside external reporting instead of overwriting it.
If you want to try it, save the bundle as bundle.json and parse it with the OASIS stix2 Python library. It rejects malformed objects, which makes it a quick check on anything you generate or receive.
# pip install stix2
import stix2
with open("bundle.json") as f:
bundle = stix2.parse(f.read())
for obj in bundle.objects:
print(obj.type, obj.id)# pip install stix2
import stix2
with open("bundle.json") as f:
bundle = stix2.parse(f.read())
for obj in bundle.objects:
print(obj.type, obj.id)What Is TAXII?
TAXII stands for Trusted Automated Exchange of Intelligence Information. It is a protocol for exchanging cyber threat information, usually STIX content, between systems. Think of STIX as a carefully labeled package and TAXII as the delivery route. TAXII can automate the exchange, but it cannot fix poor-quality information inside the package.
TAXII 2.x is a plain REST API over HTTPS. A server publishes collections of STIX objects, grouped under one or more API roots. A client discovers what is available, picks a collection it is allowed to read, and asks for objects, usually filtered by type or by date.
The conversation has four steps. First comes discovery: a
GETrequest to/taxii2/returns the server's title and its list of API roots. Second, the client asks an API root, for example/api1/, for its title, the TAXII versions it supports, and the maximum request size. Third,GET /api1/collections/lists the collections, each with an ID, a title, and whether you may read it, write to it, or both. Fourth,GET /api1/collections/{id}/objects/returns an envelope holding the STIX objects themselves.
Here is step 4 with
curl. TheAcceptheader names the TAXII version,match[type]filters by STIX object type, andadded_afterreturns only what is new since your last pull. The-gflag stops curl from treating the square brackets as a range.
curl -g -s \
-H "Accept: application/taxii+json;version=2.1" \
-u "analyst:$TAXII_PASSWORD" \
"https://taxii.example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/?match[type]=indicator&added_after=2026-10-01T00:00:00Z&limit=100"curl -g -s \
-H "Accept: application/taxii+json;version=2.1" \
-u "analyst:$TAXII_PASSWORD" \
"https://taxii.example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/?match[type]=indicator&added_after=2026-10-01T00:00:00Z&limit=100"The response is an envelope. When
moreis true, thenextvalue goes back into the following request to fetch the next page.
{
"more": false,
"objects": [
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"pattern": "[domain-name:value = 'payroll-update-portal.example.com']",
"confidence": 70
}
]
}{
"more": false,
"objects": [
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--7d3e9a51-0c28-4b6f-9e14-a5c8f2b60d37",
"pattern": "[domain-name:value = 'payroll-update-portal.example.com']",
"confidence": 70
}
]
}Most teams do not hand-write these requests. The
taxii2-clientlibrary wraps the same calls:
# pip install taxii2-client stix2
from taxii2client.v21 import Server, Collection
server = Server("https://taxii.example.com/taxii2/",
user="analyst", password="...")
for api_root in server.api_roots:
for c in api_root.collections:
print(c.id, c.title, "read:", c.can_read, "write:", c.can_write)
collection = Collection(
"https://taxii.example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/",
user="analyst", password="...")
envelope = collection.get_objects(**{
"match[type]": "indicator",
"added_after": "2026-10-01T00:00:00Z",
})
for obj in envelope.get("objects", []):
print(obj["id"], obj.get("confidence"), obj["pattern"])# pip install taxii2-client stix2
from taxii2client.v21 import Server, Collection
server = Server("https://taxii.example.com/taxii2/",
user="analyst", password="...")
for api_root in server.api_roots:
for c in api_root.collections:
print(c.id, c.title, "read:", c.can_read, "write:", c.can_write)
collection = Collection(
"https://taxii.example.com/api1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/",
user="analyst", password="...")
envelope = collection.get_objects(**{
"match[type]": "indicator",
"added_after": "2026-10-01T00:00:00Z",
})
for obj in envelope.get("objects", []):
print(obj["id"], obj.get("confidence"), obj["pattern"])Publishing works the other way: a client with write access sends a POST of an envelope to the same /objects/ path and receives a status resource it can poll to see whether each object was accepted.
A match is a lead, not a verdict
Suppose an endpoint connects to a domain listed in a shared intelligence collection. The match is worth investigating, but it does not automatically mean the endpoint is compromised. The domain may have changed hands, been misclassified, or be hosted on infrastructure shared with legitimate services.
A good workflow keeps the judgment with the evidence. Each step below maps to a field you saw in the sample bundle:
- Check the dates. When was the indicator created, and is it inside its
valid_fromandvalid_untilwindow? A domain flagged eight months ago may have been resold since. - Trace it to its original source. Follow
created_by_refandexternal_references. Several reports may all repeat one original claim, which looks like corroboration but is not. - Compare it with local evidence. Which process made the connection? What happened before and after? A Sighting you record is the local half of the story.
- Consider benign explanations and the cost of blocking. Weigh the
confidencevalue against what a block would break, such as a shared hosting provider or a payroll vendor. - Choose a proportionate action: investigate, monitor, block, or set the indicator aside.
For the sample indicator, a hit from a mail gateway log is a strong lead. It becomes a finding when the user clicked, the proxy shows a download, and the endpoint shows an Office process spawning a script. It becomes a false positive when it turns out to be the HR team's own security-awareness test. The same match leads to different actions depending on what the evidence around it says.
This is why provenance, timestamps, confidence, and context are not optional decoration. They help analysts decide what a piece of intelligence is worth today.
Sharing also needs boundaries
A useful exchange respects handling restrictions and privacy. The Traffic Light Protocol (TLP) tells recipients how far a piece of information may travel. It is a handling convention, not encryption and not an access-control system. Teams still need to verify who may receive the information and apply their organization's policies.
In STIX, a TLP label is an object that other objects point to. The sample indicator carries object_marking_refs pointing at this definition:
{
"type": "marking-definition",
"spec_version": "2.1",
"id": "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
"created": "2017-01-20T00:00:00.000Z",
"definition_type": "tlp",
"name": "TLP:GREEN",
"definition": { "tlp": "green" }
}{
"type": "marking-definition",
"spec_version": "2.1",
"id": "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
"created": "2017-01-20T00:00:00.000Z",
"definition_type": "tlp",
"name": "TLP:GREEN",
"definition": { "tlp": "green" }
}The label travels with the data, so a downstream tool can read it and, if it is built to, filter or warn. TLP 2.0 has five levels. TLP:CLEAR means there is no restriction on disclosure. TLP:GREEN allows sharing within the recipient's community but not through public channels. TLP:AMBER limits sharing to the recipient's organization and the clients who need the information to act, and TLP:AMBER+STRICT narrows that to the recipient's organization only. TLP:RED is for named recipients only, with no onward sharing.
Check which TLP version your platform implements, because older tools still use TLP 1.0 labels such as TLP:WHITE.
Tools That Use STIX and TAXII: ATT&CK, MISP and OpenCTI
You will rarely use these standards on their own. They are the common format and transport that connect other tools and communities.
MITRE ATT&CK is the public knowledge base of attacker tactics and techniques, and it is where technique IDs like T1566.001 come from. STIX Attack Pattern objects reference those IDs, as in our sample. MISP and OpenCTI are open-source platforms for managing and sharing threat intelligence. MISP imports and exports STIX and can serve data to TAXII clients, while OpenCTI is built around the STIX 2 data model and has connectors for TAXII sources.
Sector communities matter too. ISACs and ISAOs, the sharing groups for industries such as finance or health, often run TAXII servers or publish STIX feeds to their members, and many government CERTs publish advisories with STIX or TAXII access next to the human-readable report.
A common pattern ties these together: a TAXII feed delivers STIX bundles into a platform such as MISP or OpenCTI, analysts enrich and review them there, and the approved indicators are pushed on to the SIEM or EDR as detections.