DEV Community

Puneet Khandelwal
Puneet Khandelwal

Posted on

Threat Modeling a Citizen Ballot API

When a municipal government exposes a ballot tracking API, the threat model changes. You aren't protecting corporate data anymore. You're defending civic trust.

While auditing a regional voting status service last quarter, we found a classic vulnerability dressed up in JSON. The endpoint took raw string parameters for ballot IDs and returned precinct statuses without schema validation or rate limits. An attacker with a cluster of headless scrapers could easily deny service to citizens checking their status on election night.

Engineers default to web application firewalls and API keys. But civic infrastructure needs a different baseline. Public APIs can't use secret credentials because the public is the user base. Anyone needs to query their status. That constraint invalidates authentication as a primary defense against scraping. The burden shifts entirely to behavioral analysis and input sanitization.

Let's look at the SQL injection vector. The backend used dynamic string concatenation to build query filters. A reporting route bypassed the ORM for performance, leaving a wide-open door for tautology payloads. We stripped out the raw query builder and dropped in parameterized statements enforced at the driver level. We also added strict TypeScript interfaces for all incoming query parameters, rejecting any payload with unexpected keys or special characters before it hit the connection pool.

Stopping scrapers required a nuanced approach. We couldn't block automated tools outright. News orgs, researchers, and civic tech groups scrape public data with custom scripts. Instead of blocking non-browser user agents, we built an algorithmic rate limiter using sliding windows tied to subnet blocks and query costs. Requesting a single ballot ID cost almost nothing. Paginating through millions of sequential IDs triggered escalating delays and token bucket exhaustion. We also dropped cryptographic nonces into the frontend payload, forcing automated clients to run a lightweight proof-of-work challenge that made large-scale scraping too expensive to run.

Civic tech gets probed by hostile actors who view election infrastructure as a high-value target. Code meeting the reality of public administration means our responsibility goes past clean architecture and low latency. We have to build systems that stay transparent to legitimate users while resisting bad actors.

Top comments (0)