

Context: Following the discovery of critical flaws in Belgian eID software affecting 2 million users, developers must transition from "trusting" client-side identity assertions to a Zero Trust Identity Verification model.
This guide provides the architectural implementation for a secure backend verification system designed to mitigate identity spoofing and session hijacking vulnerabilities common in flawed eID implementations.
Before implementing secure identity verification, ensure your development environment meets these requirements:
cryptography (Python) or jose/jsonwebtoken (Node.js).python-dotenv for Python or dotenv for Node.js.Install the necessary dependencies for both environments.
# Create a virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install core security and environment libraries
pip install cryptography python-dotenv fastapi uvicorn
# Initialize project
npm init -y
# Install security and server dependencies
npm install express dotenv jose crypto-js
npm install --save-dev typescript @types/node @types/express ts-node
In the wake of eID flaws, you cannot rely solely on the presence of a "Verified" flag from the client. You must implement Cryptographic Signature Verification to ensure the identity data hasn't been tampered with by a compromised driver.
This example demonstrates how to verify a signed identity payload using public key cryptography.
import os
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from dotenv import load_dotenv
load_dotenv()
app = FastAPI()
# Mocking a Public Key that would normally be retrieved from a secure
# Certificate Authority (CA) or the Government's official endpoint.
PUBLIC_KEY_PEM = os.getenv("ID_PUBLIC_KEY")
class IdentityPayload(BaseModel):
user_id: str
email: str
signature: str # Base64 encoded signature from the eID driver
data_payload: str # The raw data that was signed
def verify_signature(payload_data: str, signature_b64: str) -> bool:
"""
Verifies that the identity data was signed by a legitimate eID driver
using the official public key.
"""
try:
# Convert PEM string to key object
public_key = serialization.load_pem_public_key(PUBLIC_KEY_PEM.encode())
# Convert signature from Base64
import base64
signature = base64.b64decode(signature_b64)
# Perform cryptographic verification
public_key.verify(
signature,
payload_data.encode(),
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
return True
except Exception as e:
print(f"Security Alert: Signature Verification Failed: {e}")
return False
@app.post("/verify-identity")
async def verify_identity(identity: IdentityPayload):
# CRITICAL: We do not trust 'user_id' from the request body alone.
# We verify the signature against the data provided.
is_valid = verify_signature(identity.data_payload, identity.signature)
if not is_valid:
# Log this as a potential spoofing attempt
raise HTTPException(status_code=401, detail="Invalid Identity Signature")
return {"status": "success", "message": "Identity verified via cryptographic proof"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
Using jose for modern, high-performance JWT/JWS verification.
import express, { Request, Response } from 'express';
import { jwtVerify, importSPKI } from 'jose';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());
// In production, this key is fetched from a secure Secret Manager
const PUBLIC_KEY_PEM = process.env.ID_PUBLIC_KEY || '';
interface IdentityRequest {
claims: object;
signature: string;
}
/**
* Securely verifies the identity token provided by the eID client.
* This prevents "Man-in-the-Middle" or "Driver Spoofing" attacks.
*/
async function verifyIdentityToken(claims: object, signature: string): Promise<boolean> {
try {
const publicKey = await importSPKI(PUBLIC_KEY_PEM, 'RS256');
// We verify the signature against the claims
// Note: In a real eID implementation, the payload is often a JWS
await jwtVerify(claims, publicKey);
return true;
} catch (error) {
console.error('[SECURITY ALERT] Identity verification failed:', error.message);
return false;
}
}
app.post('/api/v1/auth/eID-verify', async (req: Request, res: Response) => {
const { claims, signature } = req.body;
if (!claims ||!signature) {
return res.status(400).json({ error: "Missing identity credentials" });
}
const isValid = await verifyIdentityToken(claims, signature);
if (!isValid) {
// Mitigate against automated attacks by implementing rate limiting
return res.status(401).json({ error: "Cryptographic verification failed" });
}
// Proceed with session creation
res.status(200).json({ status: "Verified", user: claims });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Secure Identity Service running on port ${PORT}`));
Never hardcode cryptographic keys. Use environment variables and secure vaults.
.env Template:
# The public key used to verify eID signatures
# Should be the official key provided by the government authority
ID_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----"
# Server Configuration
PORT=8000
NODE_ENV=production
Never rely on a single boolean isVerified: true from a client-side SDK.
if (client.isVerified) { grantAccess(); }if (verifySignature(client.signedData, client.signature)) { grantAccess(); }Always include a nonce (number used once) or a high-resolution timestamp in the signed payload. When verifying, ensure the timestamp is within a valid window (e.g., last 30 seconds) to prevent attackers from re-sending a previously captured valid signature.
| Error | Likely Cause | Resolution |
|---|---|---|
Invalid Signature | Key Mismatch | Ensure the PUBLIC_KEY matches the one used by the eID driver. |
Malformed PEM | Newline issues in .env | Use literal newlines \n in your environment variable string. |
Algorithm Mismatch | Wrong padding/alg | Ensure you are using RS256 or PSS as specified in the eID documentation. |
Timeout/Connection | Key Fetch Failure | Ensure your server has network access to the Key Management Service (KMS). |
Before deploying identity verification logic to production, verify the following:
PUBLIC_KEY if the government rotates their certificates?npm audit or pip-audit to ensure your crypto libraries don't have known vulnerabilities?access_denied if an error occurs during the verification process?Source: Security Week AI
Follow ICARAX for more AI insights and tutorials.
