The same country that invented the telephone is on its second draft of digital identity, and the first draft is taking a long time to retire.
Antonio Meucci demonstrated a working teletrofono on Staten Island in 1860, sixteen years before Bell's patent, and died too poor to pay the renewal fee. I've written about that theft before. Italy has a recurring talent for inventing the future and then handing the deed to whoever shows up with paperwork. SPID β Sistema Pubblico di IdentitΓ Digitale β is the 2026 edition of that pattern, with the citizen as the asset and a private contractor holding the deed.
I hold the passport. I have the codice fiscale. I could move back tomorrow β the hill town, the sea, the family table, the wine. The architecture is what's keeping me on the New York side of this for now, so I'll do the thing a dual-citizen engineer with a laptop can actually do: read it carefully and write it down. The title is provocation. The work is observation. No exploit. No payload. Just the blueprint, read out loud β and a long look at CIE, because Italy already has the better draft and I'm getting one.
SAML 2.0 Is a 2005 Fax Machine With a Certificate
At the protocol layer, SPID is built on SAML 2.0 β a spec formalized in 2005, the same year YouTube launched and nobody had an iPhone. The government has since bolted OpenID Connect SDKs on the side for Node, Django, and Spring, but the structural skeleton underneath is still XML federation.
The loop: a citizen hits a government Service Provider, the SP mints a SAML AuthnRequest, the user gets bounced to their chosen Identity Provider, the IdP authenticates them and bounces back a signed XML assertion. That assertion is the keys to the kingdom, and it is a parser's nightmare. XML signature validation is one of the most reliably broken things in applied cryptography, because the signature and the thing it signs live in the same mutable tree.
THE XSW PLAYBOOK β XML Signature Wrapping
-------------------------------------------------------------
Original assertion: After the wrap:
<Response> <Response>
<Assertion ID="real"> <Assertion ID="evil"> <-- attacker payload
<Subject>citizen</Subject> <Subject>admin</Subject>
<Signature>VALID</Signature> </Assertion>
</Assertion> <Assertion ID="real"> <-- moved, still signed
</Response> <Signature>VALID</Signature>
</Assertion>
</Response>
-------------------------------------------------------------
Signature still verifies (it points at "real").
Application reads the FIRST assertion it finds ("evil").
The cryptography is fine. The plumbing decides who you are.
-------------------------------------------------------------
XXE injection, signature wrapping, DOM confusion are textbook failure modes of exactly the protocol family Italy chose to put 40 million legal identities on. SAML in 2026 is a procurement choice. The RFP was written when SAML was new, nobody got fired for specifying an enterprise standard, and the engineers inside the IdPs already know.
The Tax Code Was Never a Secret, and They Built the Whole Fortress Anyway
Two designs, opposite assumptions about secrecy.
In the US, the Social Security Number is the holy grail because the credit system is private, decentralized, and matches on name + DOB + SSN with no human in the loop. Steal the number, spin up a phantom, drain it before the victim notices. The number is treated as a secret, so the entire fraud economy is built on the fact that it leaks.
The codice fiscale is the opposite. It is not issued. It is computed β a deterministic function of your surname, name, birth date, sex, and town of birth. Anyone with your name and birthday can regenerate it in microseconds, offline, no network call, no database, no permission:
MONTH = {1:'A',2:'B',3:'C',4:'D',5:'E',6:'H',
7:'L',8:'M',9:'P',10:'R',11:'S',12:'T'}
def _consonants(s): return [c for c in s if c not in 'AEIOU']
def _vowels(s): return [c for c in s if c in 'AEIOU']
def _name_code(s, is_first_name=False):
s = ''.join(ch for ch in s.upper() if ch.isalpha())
cons, vow = _consonants(s), _vowels(s)
if is_first_name and len(cons) >= 4: # the one special case
cons = [cons[0], cons[2], cons[3]]
code = (cons + vow + ['X', 'X', 'X'])[:3]
return ''.join(code)
# Odd/even position tables β also public, also not a secret.
ODD = {**{str(i): v for i, v in enumerate(
[1,0,5,7,9,13,15,17,19,21])},
**dict(zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
[1,0,5,7,9,13,15,17,19,21,2,4,18,20,11,3,6,8,12,14,16,10,22,25,24,23]))}
EVEN = {**{str(i): i for i in range(10)},
**{c: i for i, c in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}
def codice_fiscale(surname, name, year, month, day, sex, belfiore):
"""`belfiore` is the 4-char town code. It comes from a *public*
Agenzia delle Entrate table. There is no secret anywhere in here."""
cf = _name_code(surname)
cf += _name_code(name, is_first_name=True)
cf += f"{year % 100:02d}"
cf += MONTH[month]
cf += f"{day + (40 if sex.upper() == 'F' else 0):02d}" # women: day + 40
cf += belfiore.upper()
total = sum((ODD if i % 2 == 0 else EVEN)[ch] for i, ch in enumerate(cf))
return cf + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[total % 26]
# Every input is a fact about you that the state already published.
print(codice_fiscale("Subrizi", "Michael", 1978, 8, 14, "M", "Z404"))
Five seconds of Python. No requests import. Nothing to phish. The Italian financial system knows this β it explicitly treats the codice fiscale as a lookup tag, never a credential. Which detonates the official story. SPID was never built to stop someone from opening a fake credit card in your name. That attack was never possible. The fortress is real, but it isn't pointed outward at thieves. It's pointed inward, at you.
What SPID actually hardens against:
- The cash hustle. Lavoro in nero β the informal economy that kept the country fed for generations β leaves no audit trail. Make SPID the mandatory bottleneck for signing a lease, activating a router, registering a car, drawing a paycheck, and every action logs straight to the Agenzia delle Entrate. Not a wall to keep hackers out. A turnstile to keep the citizen inside a hyper-audited box.
- Structural fraud you can't do with a public tag. Forged commercial leases, intercepted agricultural grants, phantom-company invoices. The MFA push exists to bind the flesh to the tag β to make sure a human approved the privilege escalation, because the identifier itself proves nothing.
- State-disruption. The portals are classed as critical infrastructure, so they're shielded against DDoS, credential stuffing, and XML-parsing exploits β because whoever can revoke the logins of millions at once can freeze the country.
Read that last one again. The system's own threat model concedes that a single node can disconnect a population from public life. They wrote that down as a feature to defend, not a design to abolish.
Distributed Trust, Zero Uniform Standard
There is no government bunker. SPID is a federation, and the servers holding your tax code, your phone number, your biometrics, and your access history sit inside private companies β Poste Italiane, Aruba, InfoCert, TIM β each running its own data centers, its own cloud tenancy, its own DevOps maturity, its own worst engineer having his worst day.
AgID, the technical wing of the Prime Minister's office, issues the guidelines and the audits. TLS, PKI, mandatory MFA for Level 2. The controls on paper are fine. The problem is not the controls. The problem is the topology:
threat_surface(SPID) = β surface(IdP_i) for every private provider i
# A union, not an intersection. The system is exactly as
# secure as the LEAST secure contractor in the federation.
# One misconfigured bucket. One unpatched dependency.
# One front-end XSS at the lowest bidder.
# 40,000,000 centralized identities, compromised through
# the weakest vendor's worst sprint.
Then there's Developers Italia β the GitHub org launched under modernization pressure to look open-source. Read the repos. A lot of it is bloated wrappers around legacy enterprise software, long-lived security issues, thin maintenance. The gap between the policy PDF and the code that actually runs at 3 a.m. when an IdP is under load is the whole story. It was specced by AgID, built by state-owned tech (Sogei) and consulting firms via public tender, and "opened" as an afterthought. Procurement is not an engineering culture. It never produces one.
The Defenders Are an Org Chart, Not a Perimeter
When something goes wrong, four entities own the response, which is another way of saying nobody does:
ROLE ENTITY FUNCTION
----------------------------------------------------------------
threat-intel CERT-AgID circulars, IoC feeds, PA monitoring
national IR ACN Italy's CISA-equivalent, big incidents
corporate SOC Poste/Aruba/ each IdP defends its own silo,
InfoCert on its own standard, on its own clock
financial core Sogei Anagrafe Tributaria β the tax brain
----------------------------------------------------------------
incident_response_latency β (number_of_orgs_who_must_agree)Β²
Enterprise WAFs and CDNs absorb the loud stuff β DDoS, known exploits, botnet credential stuffing, the automated XML attacks. Fine. But the WAF is in front of the strong wall. The weak link is behind it: the private IdP with outsourced code and a SOC standard that drifts every time a contract is renewed. A signed SAML blob with a parser that trusts the first <Assertion> it sees. Defense-in-depth only works if the depth is uniform. Here it's a moat in front of a screen door.
Three Foreign Services Already Did the Threat Model For You
Italy sits in a geopolitically loud seat β NATO core, complicated ties across the Mediterranean, a 2026 Winter Olympics to defend. Consolidate the entire population's legal existence into a fragmented outsourced pipeline and you haven't built an administrative tool. You've built a target with a brochure.
ACTOR CLASS GROUPS OPERATING MODE
----------------------------------------------------------------------
Russia (loud) NoName057(16), Sandworm, DDoS + mass credential
Fancy Bear / APT28 stuffing. Map the auth
gateways. Prove the
digital state can be
blinded on command.
----------------------------------------------------------------------
Iran (quiet) MuddyWater, Spearphish PA
Charming Kitten / APT35 contractors. Don't
crash it β sit in it.
CF-keyed, timestamped
logs = movement
profiles on dissidents.
----------------------------------------------------------------------
Israel (grey) Paragon, NSO, + the Offensive spyware on
2024 Milan database journalists/activists;
ring (PIs, IT, alleged backdoored state/tax/
mafia ties) bank DBs; illegal
movement dossiers on
politicians & elites.
----------------------------------------------------------------------
Russia wants the lights off. Iran wants to read by them without anyone noticing. The third column is the one that should end the debate: a documented ring inside Milan was already pulling unauthorized profiles out of confidential state, tax, and banking systems, and Paragon's spyware was found on Italian journalists' phones until the fallout forced the vendor to cut Italian access. The honeypot doesn't theoretically attract intelligence operations. It demonstrably already has. The architecture's defenders are arguing about XML schema validation while the actual incident reports are filed.
Authoritarianism by Accident, Then on Purpose
The system works because it's broken. That's the part nobody in the procurement chain says out loud, because saying it requires admitting it.
Nobody sat in a volcano lair designing a panopticon. Phase one was boring: the state wanted EU modernization money, so it mandated a digital identity framework and outsourced the build to the lowest bidders and legacy state tech. The engineers weren't chasing a beautiful UX. They were closing a compliance ticket against a bloated SAML requirements doc. The output was exactly what you'd predict: a glitchy, crashing, counterintuitive wall.
Phase two is where bad tech becomes structural power, and it was never designed β it was discovered:
class StrategicFriction:
"""Nobody specced this. It emerged from incompetence,
and then the administrators noticed it was load-bearing."""
def on_citizen_login(self, user):
user.attempt_basic_task() # pay a bill, see a doctor
raise GlitchyMFA() # token desyncs, portal 500s
def downstream(self, user):
user.cognitive_load += HOURS_OF_SWEARING_AT_A_SCREEN
user.energy_to_resist -= user.cognitive_load
user.creative_hustle -= user.energy_to_resist
return user.state == "compliant" # not convinced. exhausted.
A flawless system is invisible. A broken mandatory system forces you to think about the state every single day, and spends the exact cognitive budget you'd otherwise use to organize, build off-grid, or push back. The MFA push that desyncs, the portal that 500s during a deadline, the lockout from one administrative glitch β the friction is the control surface. The trap grew out of institutional incompetence, and the administrators noticed user frustration was load-bearing. The cage of this century is the broken one you're legally required to use.
