Crypto Agent - Stage 2: Improved Implementation¶
🎯 Goal: Understand incremental security and why partial fixes aren't enough
⏱️ Time: 2-3 hours
📍 You are here: Stage 2 of 3
⚠️ Security Rating: 4/10 - PARTIALLY SECURE
Navigation¶
← Previous: Stage 1 - Vulnerable | Next: Stage 3 - Secure →
↑ Up: Crypto Example Overview
👋 Welcome to Stage 2!¶
Great job making it here! You've seen what's wrong in Stage 1. Now let's see what happens when we try to fix things... partially.
Here's the twist: Stage 2 is better than Stage 1, but it's NOT good enough for production. This stage exists to teach you a critical lesson:
⚠️ Critical Lesson: "Better" ≠ "Secure"
Partial security improvements can give false confidence while leaving critical vulnerabilities.
What Makes Stage 2 Different?¶
Stage 1: No security at all (0/10)
Stage 2: Some security improvements (4/10) ← You are here
Stage 3: Production-ready security (9/10)
Think of it like locking your front door but leaving all windows open. You've improved security, but an attacker still has easy access!
🎯 What You'll Learn¶
By the end of this stage, you will understand:
- ✅ How agent registries enable service discovery
- ✅ What HMAC authentication is and how it works
- ✅ Why shared secrets are problematic
- ✅ How partial validation helps (but isn't enough)
- ✅ Why Stage 2 still fails against determined attackers
- ✅ What "defense in depth" really means
- ✅ How to critically evaluate security improvements
The Key Insight¶
Stage 2 teaches you to ask: - "What did we fix?" ✅ - "What did we NOT fix?" ⚠️ (This is more important!) - "Can an attacker still succeed?" 🔴
📊 What Changed from Stage 1?¶
New Components¶
1. Agent Registry (NEW!) - Central service discovery - Agent registration and heartbeats - Capability matching - Health monitoring
2. Authentication (NEW!) - HMAC-SHA256 message signing - Shared secret between agents - Basic signature verification - Timestamp validation
3. Input Validation (IMPROVED!) - Query length limits (500 chars max) - Basic sanitization - Coin symbol whitelist - Request format checking
Comparison Table¶
| Feature | Stage 1 | Stage 2 | Change |
|---|---|---|---|
| Authentication | ❌ None | ⚠️ HMAC (weak) | 🟡 Better |
| Input Validation | ❌ None | ⚠️ Basic | 🟡 Better |
| Rate Limiting | ❌ None | ❌ Still none | 🔴 No change |
| Encryption | ❌ HTTP | ❌ Still HTTP | 🔴 No change |
| Replay Protection | ❌ None | ❌ Still none | 🔴 No change |
| Audit Logging | ❌ None | ⚠️ Basic | 🟡 Better |
| Error Handling | ❌ Unsafe | ⚠️ Better | 🟡 Better |
| Agent Discovery | ❌ None | ✅ Registry | 🟢 Fixed |
| Request Size Limits | ❌ None | ❌ Still none | 🔴 No change |
| Security Rating | 0/10 | 4/10 | +4 points |
Key Observation: We fixed ~27% of the issues, but major vulnerabilities remain!
🚀 Quick Start (10 Minutes)¶
Stage 2 has more components than Stage 1. Let's get everything running.
Prerequisites Check¶
# Check Python version
python3 --version # Need 3.8+
# Install new dependencies
pip install requests # For HTTP client
# That's it! Registry uses pure Python
Architecture Overview¶
┌─────────────┐
│ Registry │ ← New! Service discovery
│ Server │ Port 8000
└──────┬──────┘
│
│ Agents register here
│
┌───┴────────────┐
│ │
┌──▼─────┐ ┌────▼──┐
│ Crypto │ │ Other │
│ Agent │ │Agents │
│ :8080 │ │ ... │
└────────┘ └───────┘
Step 1: Start the Registry¶
Terminal 1:
Expected Output:
==================================================
A2A Agent Registry Server
==================================================
🚀 Starting Agent Registry Server...
💓 Health monitor initialized (check every 30s, stale after 90s)
📦 Initialized in-memory agent storage
💚 Health monitor started
✅ Registry is ready
INFO: Uvicorn running on http://0.0.0.0:8000
💡 What just happened? The registry is now running and waiting for agents to register!
Step 2: Start the Crypto Agent¶
Terminal 2:
Expected Output:
🚀 Starting Cryptocurrency Price Agent (Stage 2 - Improved)
================================================================================
🔧 Configuration:
- Port: 8080
- Registry: http://localhost:8000
- Auth: HMAC-SHA256 (shared secret)
- Validation: Basic input sanitization
⚠️ Security Status: PARTIALLY SECURE (4/10)
✅ Agent registry
✅ HMAC authentication
✅ Basic input validation
❌ No replay protection
❌ No rate limiting
⚠️ Shared secret (not ideal)
🔄 Registering with agent registry...
✅ Registered! Agent ID: crypto-price-agent-a3d2c9f1
💓 Sending heartbeats every 20 seconds
📡 Server ready on http://localhost:8080
Waiting for authenticated requests...
🎯 Notice: The agent tells you what's secure and what's not!
Step 3: Run the Client¶
Terminal 3:
Expected Output:
================================================================================
Cryptocurrency Query Client (Stage 2 - With Authentication)
================================================================================
🔐 Authentication: HMAC-SHA256
📋 Discovering agents from registry...
Found agents:
• crypto-price-agent-a3d2c9f1
Capabilities: price_query, streaming
Status: healthy
Last heartbeat: 2s ago
Connected to: crypto-price-agent-a3d2c9f1
Commands:
- Query: "What's the price of Bitcoin?"
- Discover: "show agents"
- Quit: "quit" or "exit"
Enter your query:
Step 4: Try an Authenticated Query¶
Response:
🔐 Signing request with HMAC...
✅ Signature verified!
🤖 Agent Response:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The current price of Ethereum (ETH) is $2,245.50
📊 Additional Information:
• 24h Change: +3.2%
• 24h Volume: $15.2B
• Market Cap: $270B
🔐 Response authenticated: ✓
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 Success! You just made an authenticated A2A request with service discovery!
🔍 Understanding the Improvements¶
Let's examine what changed and how it works.
Improvement #1: Agent Registry (Service Discovery)¶
What It Solves: Finding agents dynamically without hardcoding URLs
How It Works:
# Agent registers itself
registration = {
"agent_id": "crypto-price-agent-a3d2c9f1",
"name": "Cryptocurrency Price Agent",
"capabilities": ["price_query", "streaming"],
"endpoint": "http://localhost:8080",
"authentication": {
"type": "hmac-sha256",
"required": True
}
}
# POST to registry
response = requests.post("http://localhost:8000/register", json=registration)
The Registry Stores:
{
"crypto-price-agent-a3d2c9f1": {
"name": "Cryptocurrency Price Agent",
"capabilities": ["price_query", "streaming"],
"endpoint": "http://localhost:8080",
"last_heartbeat": "2024-12-19T10:30:00Z",
"status": "healthy"
}
}
Clients Can Discover:
# Client queries registry
agents = requests.get("http://localhost:8000/agents").json()
# Find agents with price_query capability
price_agents = [
agent for agent in agents
if "price_query" in agent["capabilities"]
]
# Connect to the first available one
agent_endpoint = price_agents[0]["endpoint"]
Benefits: - ✅ Dynamic service discovery - ✅ No hardcoded URLs - ✅ Automatic failover (pick another agent if one is down) - ✅ Health monitoring via heartbeats - ✅ Capability-based routing
Limitations: - ⚠️ Registry is single point of failure - ⚠️ No registry authentication - ⚠️ Anyone can register malicious agents - ⚠️ No verification of claimed capabilities
💡 Lesson: Service discovery is great, but the registry itself needs security!
Improvement #2: HMAC Authentication¶
What It Solves: Verifying that requests come from trusted clients
How It Works:
Step 1: Shared Secret (configured in both client & server)
⚠️ Problem Alert: Shared secrets are risky! More on this later.
Step 2: Client Signs Request
import hmac
import hashlib
import json
import time
# Build the request
request = {
"agent_id": "client-123",
"query": "What's the price of BTC?",
"timestamp": int(time.time())
}
# Create signature
message = json.dumps(request, sort_keys=True)
signature = hmac.new(
SHARED_SECRET.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
# Send request with signature
headers = {"X-Signature": signature}
requests.post(endpoint, json=request, headers=headers)
Step 3: Server Verifies Signature
def verify_signature(request, provided_signature):
# Recreate the signature
message = json.dumps(request, sort_keys=True)
expected_signature = hmac.new(
SHARED_SECRET.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
# Compare signatures (timing-safe comparison)
return hmac.compare_digest(expected_signature, provided_signature)
# In request handler
if not verify_signature(request_data, signature):
return {"error": "Invalid signature"}, 401
Benefits: - ✅ Only clients with the secret can make requests - ✅ Messages can't be tampered with (integrity) - ✅ Cryptographically secure (HMAC-SHA256) - ✅ Better than no authentication
Limitations: - ⚠️ Shared secret must be distributed securely - ⚠️ If secret is leaked, ALL clients are compromised - ⚠️ No replay attack protection (more on this soon!) - ⚠️ Difficult to rotate keys - ⚠️ Can't identify individual clients
💡 Lesson: HMAC is good, but shared secrets scale poorly. Production systems use asymmetric crypto.
Improvement #3: Basic Input Validation¶
What It Solves: Preventing some injection and DoS attacks
The Code:
def validate_query(query):
"""Basic input validation"""
# Check type
if not isinstance(query, str):
return False, "Query must be a string"
# Check length
if len(query) > 500:
return False, "Query too long (max 500 chars)"
# Check for empty
if not query.strip():
return False, "Query cannot be empty"
# Basic sanitization - remove dangerous characters
dangerous_chars = ['<', '>', '{', '}', ';', '|', '&']
for char in dangerous_chars:
if char in query:
return False, f"Invalid character: {char}"
return True, None
# In request handler
is_valid, error = validate_query(query)
if not is_valid:
return {"error": error}, 400
What This Catches:
# ❌ Rejected
validate_query("A" * 1000) # Too long
validate_query("<script>alert()</script>") # Dangerous chars
validate_query("BTC; rm -rf /") # Dangerous chars
validate_query("") # Empty
validate_query(None) # Wrong type
# ✅ Accepted
validate_query("What's the price of Bitcoin?")
validate_query("Show me ETH price")
Benefits: - ✅ Prevents extremely long queries - ✅ Blocks obvious injection attempts - ✅ Type checking - ✅ Better than nothing
Limitations: - ⚠️ Easily bypassed (many ways around this) - ⚠️ Blacklist approach (should use whitelist) - ⚠️ Doesn't validate query semantics - ⚠️ No protection against "valid-looking" attacks
Example Bypass:
# Still vulnerable to creative attacks
query = "What is the price of " + "BTC " * 50 # 50 repetitions
# Each repetition is valid, but combined it's weird
💡 Lesson: Basic validation is a start, but comprehensive validation requires whitelisting and semantic checks.
Improvement #4: Better Error Handling¶
What Changed:
# Stage 1: ❌ Reveals everything
try:
result = process_query(query)
except Exception as e:
return {
"error": str(e),
"traceback": traceback.format_exc(),
"system_info": platform.platform()
}
# Stage 2: ⚠️ Better but not perfect
try:
result = process_query(query)
except ValueError as e:
return {"error": "Invalid input"}, 400
except KeyError as e:
return {"error": "Resource not found"}, 404
except Exception as e:
# Log internally, generic message to user
logger.error(f"Unexpected error: {e}")
return {"error": "Internal server error"}, 500
Benefits: - ✅ Generic error messages for users - ✅ Detailed errors logged internally - ✅ Less information disclosure - ✅ Proper HTTP status codes
Limitations: - ⚠️ Still might leak info in certain cases - ⚠️ Logs might contain sensitive data - ⚠️ No structured error reporting
💡 Lesson: Good error handling is a balance between helpful and secure.
🔴 What We DIDN'T Fix (The Critical Part!)¶
This is the most important section. Understanding what's STILL vulnerable is crucial.
Remaining Vulnerability #1: Replay Attacks¶
Severity: CRITICAL
CVSS Score: 8.1
Status: ❌ NOT FIXED
The Problem:
Even with HMAC authentication, an attacker can capture a valid request and replay it!
Attack Demonstration:
Step 1: Attacker captures legitimate request
# Client → Server
POST /query HTTP/1.1
X-Signature: a3d2c9f1...
{
"agent_id": "client-123",
"query": "What's the price of BTC?",
"timestamp": 1234567890
}
Step 2: Attacker replays it (hours or days later)
# attacker.py
import requests
# Captured request (signature is valid!)
captured_request = {
"agent_id": "client-123",
"query": "What's the price of BTC?",
"timestamp": 1234567890
}
captured_signature = "a3d2c9f1..."
# Replay attack - send the EXACT same request
for i in range(1000):
requests.post(
"http://server:8080/query",
json=captured_request,
headers={"X-Signature": captured_signature}
)
What Happens: - ✅ Signature is valid (it was valid originally!) - ✅ Server accepts the request - ❌ Server doesn't know it's a replay - ❌ Attacker can replay indefinitely
Real-World Impact: - Execute unauthorized actions repeatedly - Exhaust resources - Manipulate transaction sequences - Impersonate legitimate users
How to Try This Yourself:
- Run the Stage 2 setup
- Make a legitimate query and capture it:
- Save the request JSON and signature
- Close the client
- Manually send the same request again:
Why This Matters:
In financial systems:
Original: "Transfer $100 from Alice to Bob"
Replay: Attacker replays this 10 times → $1000 transferred!
Fix (in Stage 3): - Use nonces (one-time tokens) - Short timestamp windows (5 minutes max) - Track used nonces - Reject old timestamps
⚠️ CRITICAL: This is Stage 2's biggest vulnerability. Authentication alone isn't enough!
Remaining Vulnerability #2: Shared Secret Distribution¶
Severity: HIGH
CVSS Score: 7.5
Status: ❌ NOT FIXED
The Problem:
How do we give clients the shared secret securely?
Bad Approaches (all used in Stage 2):
# ❌ Hardcoded in source code
SHARED_SECRET = "crypto_agent_secret_key_2024"
# ❌ Environment variable (better but still shared)
SHARED_SECRET = os.getenv("SHARED_SECRET")
# ❌ Config file
with open("secrets.conf") as f:
SHARED_SECRET = f.read()
The Issues: 1. Single Secret for All: One leaked secret compromises everyone 2. Distribution Problem: How to give it to new clients securely? 3. Rotation Difficulty: Changing the secret requires updating ALL clients 4. No Revocation: Can't revoke access for one client without affecting all
Attack Scenario:
Day 1: Client A gets the secret
Day 30: Client A's server is compromised
Attacker now has the secret
Day 31: Attacker can impersonate ANY client
Day 32: You discover the breach
Day 33: You change the secret
Now ALL legitimate clients stop working!
Why This Matters: - Secrets will eventually leak - Scale problems (100s of clients?) - Insider threats - Third-party integrations
Fix (in Stage 3): - Asymmetric cryptography (public/private keys) - Each client has unique credentials - Revoke individual clients without affecting others - Proper key management system
💡 Lesson: Shared secrets don't scale. Use public-key cryptography for production systems.
Remaining Vulnerability #3: No Rate Limiting¶
Severity: HIGH
CVSS Score: 7.5
Status: ❌ NOT FIXED
The Problem:
Even authenticated clients can DoS the server!
Attack Demonstration:
# legitimate_client_attack.py
import requests
import hmac
import hashlib
import json
import time
from concurrent.futures import ThreadPoolExecutor
SHARED_SECRET = "crypto_agent_secret_key_2024" # Leaked or stolen
def make_request():
request = {
"query": "What's the price of BTC?",
"timestamp": int(time.time())
}
signature = hmac.new(
SHARED_SECRET.encode(),
json.dumps(request, sort_keys=True).encode(),
hashlib.sha256
).hexdigest()
requests.post(
"http://localhost:8080/query",
json=request,
headers={"X-Signature": signature}
)
# Attack: 1000 concurrent requests
with ThreadPoolExecutor(max_workers=100) as executor:
for i in range(1000):
executor.submit(make_request)
What Happens: - ✅ All requests are authenticated - ✅ All requests are valid - ❌ Server is overwhelmed - ❌ Legitimate users can't get through - ❌ Server might crash
Real-World Impact: - Service degradation - Increased hosting costs - Poor user experience - Potential downtime
Fix (in Stage 3): - Rate limiting per client - Token bucket algorithm - Exponential backoff - Queue management
💡 Lesson: Authentication ≠ Authorization. Just because someone can authenticate doesn't mean they should make unlimited requests.
Remaining Vulnerability #4: No Request Size Limits¶
Severity: MEDIUM
CVSS Score: 6.5
Status: ❌ NOT FIXED
The Problem:
We validate query length (500 chars), but not the entire request size!
Attack Demonstration:
# large_request_attack.py
import requests
import hmac
import hashlib
import json
request = {
"query": "What's the price of BTC?", # Valid query
"metadata": "A" * (10 * 1024 * 1024), # 10 MB of junk data
"timestamp": 1234567890
}
# Sign it (signature is valid!)
signature = hmac.new(
SHARED_SECRET.encode(),
json.dumps(request, sort_keys=True).encode(),
hashlib.sha256
).hexdigest()
# Server tries to parse 10 MB JSON
requests.post(
"http://localhost:8080/query",
json=request,
headers={"X-Signature": signature}
)
What Happens: - Server accepts the request (authenticated!) - Server tries to parse 10 MB JSON - Memory usage spikes - Other requests are slow
Fix (in Stage 3): - Limit total request size (e.g., 10 KB max) - Check size before parsing - Reject oversized requests early
Remaining Vulnerability #5: Weak Registry Security¶
Severity: HIGH
CVSS Score: 7.8
Status: ❌ NOT FIXED
The Problem:
Anyone can register as an agent!
Attack Demonstration:
# malicious_agent_registration.py
import requests
# Attacker registers fake agent
malicious_agent = {
"agent_id": "crypto-price-agent-FAKE",
"name": "Cryptocurrency Price Agent", # Same name!
"capabilities": ["price_query", "streaming"],
"endpoint": "http://attacker.com:8080", # Attacker's server
"authentication": {
"type": "hmac-sha256",
"required": True
}
}
# Register malicious agent
requests.post("http://localhost:8000/register", json=malicious_agent)
# Clients discover agents
# They might connect to the malicious one!
What Happens: 1. Clients query registry for price agents 2. Registry returns BOTH legitimate and malicious agents 3. Client might connect to the malicious one 4. Attacker steals credentials or sends fake data
Real-World Impact: - Man-in-the-middle attacks - Data theft - Credential harvesting - Supply chain attacks
Fix (in Stage 3): - Authenticate registry registration - Verify agent identity - Sign agent cards cryptographically - Registry whitelist
⚠️ CRITICAL: An insecure registry undermines the entire system!
📊 Security Scorecard: Stage 1 vs Stage 2¶
| Security Control | Stage 1 | Stage 2 | Improvement |
|---|---|---|---|
| Authentication | ❌ 0% | ⚠️ 40% | +40% |
| Input Validation | ❌ 0% | ⚠️ 30% | +30% |
| Rate Limiting | ❌ 0% | ❌ 0% | No change |
| Replay Protection | ❌ 0% | ❌ 0% | No change |
| Encryption (TLS) | ❌ 0% | ❌ 0% | No change |
| Audit Logging | ❌ 0% | ⚠️ 20% | +20% |
| Error Handling | ❌ 0% | ⚠️ 50% | +50% |
| Request Size Limits | ❌ 0% | ❌ 0% | No change |
| Key Management | ❌ 0% | ⚠️ 10% | +10% |
| Registry Security | N/A | ⚠️ 30% | New feature |
Overall Score: 4/10 (was 0/10)
Translation: We're 40% toward production-ready, with 60% still vulnerable.
💪 Hands-On Exercises¶
Exercise 1: Replay Attack Demo (30 min)¶
Task: Demonstrate the replay attack vulnerability.
Steps: 1. Start Stage 2 server and registry 2. Make a legitimate authenticated request 3. Capture the request and signature 4. Replay the request 10 times 5. Observe that all succeed
Proof of Success:
# Show that the same signature works multiple times
print(f"Request 1: {response1.status_code}") # 200
time.sleep(60) # Wait a minute
print(f"Request 2: {response2.status_code}") # 200 (should fail!)
Deliverable: Screenshot or script output showing successful replay.
Exercise 2: Shared Secret Leak Simulation (20 min)¶
Task: Simulate what happens when the shared secret leaks.
Scenario:
You're Company A. You share the secret with:
- Your mobile app
- Your web app
- Your API integration
- Partner Company B
Partner Company B gets hacked. Attacker finds the secret.
Questions to Answer: 1. What can the attacker do? 2. How do you revoke the attacker's access? 3. What's the impact on legitimate users? 4. How long does recovery take?
Deliverable: Written analysis of the scenario.
Exercise 3: Registry Poisoning (45 min)¶
Task: Register a malicious agent and intercept traffic.
Steps: 1. Create a fake agent server that logs requests 2. Register it with the same capabilities as the real agent 3. Run a client that discovers agents 4. Show that the client might connect to the fake agent
Code Skeleton:
# fake_agent.py
from flask import Flask, request
app = Flask(__name__)
@app.route('/query', methods=['POST'])
def fake_query():
# Log the request (steal credentials!)
print(f"Intercepted: {request.json}")
# Return fake data
return {"price": 999999.99, "message": "HACKED!"}
if __name__ == '__main__':
app.run(port=9999)
Deliverable: Proof that the malicious agent can be discovered and used.
Exercise 4: Comparison Analysis (30 min)¶
Task: Create a detailed comparison showing what improved and what didn't.
Template:
| Vulnerability | Stage 1 | Stage 2 | Still Exploitable? |
|---------------|---------|---------|-------------------|
| No Authentication | Yes | Improved | Via replay attack |
| [Add more...] | ... | ... | ... |
Deliverable: Completed comparison table with at least 10 vulnerabilities.
Exercise 5: Fix One Thing (1-2 hours)¶
Task: Add replay attack protection to Stage 2.
Approach: 1. Add nonce to request format 2. Server tracks used nonces 3. Reject duplicate nonces 4. Add timestamp validation (5 min window)
Code to Add:
# In server
used_nonces = set() # In production, use Redis with TTL
def verify_request(request, signature):
# Existing signature check
if not verify_signature(request, signature):
return False, "Invalid signature"
# NEW: Check timestamp
timestamp = request.get('timestamp', 0)
now = time.time()
if abs(now - timestamp) > 300: # 5 minutes
return False, "Request too old or too far in future"
# NEW: Check nonce
nonce = request.get('nonce')
if not nonce:
return False, "Nonce required"
if nonce in used_nonces:
return False, "Nonce already used (replay attack?)"
# Mark nonce as used
used_nonces.add(nonce)
return True, None
Deliverable: Modified server code + test showing replays are now blocked.
✅ Stage 2 Completion Checklist¶
Before moving to Stage 3, make sure you understand:
Improvements (What We Fixed)¶
- How agent registry works
- How HMAC authentication works
- What basic input validation catches
- Why error handling improved
- Benefits of service discovery
Limitations (What We Didn't Fix)¶
- Why replay attacks still work
- Problems with shared secrets
- Why rate limiting is still needed
- Registry security issues
- Request size limit problems
Key Insights¶
- "Better" doesn't mean "secure enough"
- Partial security creates false confidence
- Defense in depth requires multiple layers
- Every component needs security
- Authentication alone isn't enough
Practical Skills¶
- Can set up registry + agents
- Can demonstrate replay attack
- Can explain shared secret problems
- Can identify remaining vulnerabilities
- Ready to learn production patterns
Ready for Stage 3? If you checked most boxes, let's see how to fix everything! →
🎓 Key Takeaways¶
The Big Lessons from Stage 2¶
1. Incremental Security Is Dangerous
Adding some security is better than none, but can create false confidence. Attackers only need ONE vulnerability.
2. Authentication ≠ Security
We added authentication, but replay attacks still work. Authentication is necessary but not sufficient.
3. Shared Secrets Don't Scale
Works for 2-3 clients, breaks with 100+. One leaked secret compromises everyone.
4. Every Component Needs Security
We secured the agent but not the registry. Now the registry is the weak link.
5. Defense in Depth
Multiple security layers are essential. If one fails, others should still protect.
🎯 The Critical Question¶
If Stage 2 is better than Stage 1, why not ship it?
Because: 1. Replay attacks can drain resources or manipulate data 2. Shared secret will eventually leak 3. Registry poisoning allows man-in-the-middle attacks 4. No rate limiting means DoS is trivial 5. An attacker can still cause serious damage
Remember: Attackers don't need to break ALL your security. They only need to break ONE thing.
📚 Additional Resources¶
Dive Deeper¶
Replay Attacks: - OWASP: Replay Attacks - How Bitcoin prevents replays (nonces) - OAuth2 replay protection
Shared Secrets vs. Public Key: - Symmetric vs. Asymmetric Cryptography - When to use each - Key distribution problem
Rate Limiting: - Token bucket algorithm - Leaky bucket algorithm - Sliding window rate limiting
Related Documentation¶
Code Files¶
❓ FAQ¶
"Why teach Stage 2 if it's not secure?"¶
Because understanding WHY partial security fails is crucial. Many real-world systems are stuck at "Stage 2" level security!
"Is HMAC bad? Should I not use it?"¶
HMAC is great! The problem isn't HMAC itself, it's: - Shared secrets (use public-key crypto instead) - No replay protection (add nonces) - Implementation details
"Can I use Stage 2 for internal systems?"¶
Only if: - It's truly internal (no internet exposure) - You trust all users completely - You understand the risks - You plan to upgrade to Stage 3 soon
But honestly? Just start with Stage 3 patterns.
"How do I know what security level is 'enough'?"¶
Ask: 1. What's your threat model? (Who's attacking?) 2. What data are you protecting? (How sensitive?) 3. What are the consequences of breach? (How bad?) 4. What compliance requirements? (GDPR, HIPAA, etc.)
Stage 2 might be okay for low-stakes internal tools. But most systems need Stage 3.
"Why focus so much on replay attacks?"¶
Because: 1. They're easy to execute 2. They bypass authentication 3. They're hard to detect 4. They cause real damage 5. Many systems forget to prevent them
It's a common mistake that leads to serious breaches.
🎯 What's Next?¶
You've Learned:¶
- ✅ Service discovery with registries
- ✅ HMAC authentication basics
- ✅ Basic input validation
- ✅ WHY Stage 2 isn't enough
Moving to Stage 3¶
Stage 3 will show you: - 🔒 Production-grade authentication (asymmetric crypto) - 🔒 Replay attack prevention (nonces + timestamps) - 🔒 Comprehensive input validation - 🔒 Rate limiting and resource protection - 🔒 Secure registry architecture - 🔒 Complete audit logging - 🔒 Defense in depth
Continue to Stage 3 - Secure Implementation →
🎉 Congratulations!¶
You've completed Stage 2! You now understand: - ✅ What incremental security improvements look like - ✅ Why partial security is dangerous - ✅ How to critically evaluate security claims - ✅ The importance of defense in depth - ✅ That "better" ≠ "secure"
This might be the most important lesson in the entire series!
Many systems in production are at Stage 2 level. Now you can identify them and push for better security.
Document Version: 1.0
Stage: 2 of 3 (Improved)
Last Updated: December 2025 Maintained By: Robert Fischer (robert@fischer3.net)
Code Location: /examples/a2a_crypto_simple_registry_example_1/
Ready to learn production-grade security? Let's fix everything in Stage 3 →
⚠️ Remember: Never ship Stage 2 to production. It's a learning stepping stone, not a destination. Real systems need Stage 3 security! 🔒