Dev.to Security 🔐 Cybersecurity 👁 0 📖 8 min read

Protecting your Keyright activation endpoint from abuse and key-sharing

Here is a fact about your license server that sounds alarming until you understand it: the busiest, most important endpoint it exposes — the one your customers' software calls to turn a key into a running licence — has n

Here is a fact about your license server that sounds alarming until you understand it: the busiest, most important endpoint it exposes — the one your customers' software calls to turn a key into a running licence — has no authentication on it at all. /v1/activate and /v1/validate accept a request carrying a license key and nothing else. No admin token, no API secret, no bearer header. That is not an oversight. It is the only design that can work, and this post is about how you defend an endpoint you have deliberately left open to the public.

Key takeaway: The Keyright runtime hot path (/v1/activate, /v1/validate) is unauthenticated by design — it is the path your customers' apps call with a license key, and they have no admin token to present. So you do not protect it with auth. You protect it in layers: seat limits and node-locking cap how many machines one key can ever light up, duplicate-activation detection surfaces shared keys and cloned VMs, revocation kills a leaked key, a rate limit or WAF at your own reverse proxy absorbs abuse in front of /v1/*, and structured logs (with the key redacted) let you see it all.

Why the hot path has no auth — and why that is correct

Think about who calls /v1/activate. It is a copy of your software, installed on a customer's machine, that has just been handed a license key by a user who typed it into a dialog. That installed app has no account with you, no admin credential, no secret beyond the key itself. The license key is the credential. The server takes it, verifies its signature against the tenant's signing key, looks up the activation record, checks the seat count, and either binds a seat and returns a signed lease or refuses. Demanding a second credential on top of the key would mean shipping a shared secret inside every copy of your app — which is no secret at all, and a far worse exposure than the open endpoint.

The admin surface is the mirror image. /admin/* — issuing licenses, revoking keys, reading the activation list — does require the admin token, because only you call it, from your own systems. The discipline that keeps this safe is simple and absolute: expose only /v1/* publicly, and keep /admin/* reachable only from inside your network. Your customers never need any path but /v1/*. Get that split right and the "unauthenticated endpoint" is just the activation path doing its job; get it wrong and you have published your control plane.

So the threat model for /v1/* is not "someone without credentials calls it" — everyone calls it without credentials, that is the point. The threats are abuse of the open path: one key pasted across a hundred machines, a cloned VM army, a leaked key on a forum, or a crude flood of requests. Each has its own layer.

Layer 1 — seat limits and node-locking: the licensing throttle

The first and most important throttle is not a network control at all; it is the licensing logic itself. A node-locked key binds to a machine fingerprint, and the server enforces a seat count: activate more machines than the license permits and the server records the cap is reached and refuses the next one, returning a seat-limit result and no lease. There is no client-side counter to patch — the count lives on the server.

This matters enormously for the open endpoint, because it means the damage one key can do is bounded by the license, regardless of how the endpoint is hammered. A key sold as three seats can activate three machines and no more, whether it is called ten times or ten thousand. Node-locking and seat caps turn "anyone can call /v1/activate" into "anyone can call it, and it still only ever lights up the seats the key paid for." The full mechanics — fingerprint swap tolerance, idempotent re-activation, self-service seat release — are in node-locking and seat management, and the broader anti-sharing picture is in how to stop license key sharing.

Layer 2 — detecting shared keys and cloned VMs

Seat limits bound the blast radius; they do not tell you a key is being abused. For that you watch the pattern. Because clients re-validate online periodically — refreshing a short-lived signed lease — a shared key becomes visible: a single-seat key that suddenly heartbeats from a dozen machines across three countries is not ambiguous. Cloned VMs are subtler, because a clone copies the machine fingerprint verbatim, so node-locking alone cannot tell two copies apart. Keyright separates them with a per-run runtime instance id the clone cannot meaningfully copy: two copies of one activation heartbeating at the same time reveal themselves as concurrent, which a fingerprint never could. The full mechanism is in detecting cloned VMs and duplicate activations.

When you see it, you have graded responses rather than a hair-trigger. Deactivate the offending activation to free the seat for a legitimate machine; for a confirmed clone, let the server pick a winner (newest-wins evicts the older copy on its next heartbeat) or flag-and-hold so it surfaces on your dashboard. For a key that is genuinely compromised, you escalate to the next layer.

Layer 3 — revocation for a leaked key

When a key leaks outright — posted on a forum, refunded, charged back — you kill that specific key. Revocation is a signed negative statement: mark the key revoked on the admin side (POST /admin/licenses/{id}/revoke, or one click in the dashboard) and the client drops to the unlicensed state on its next re-validation. Because leases are short-lived, "next re-validation" comes around on its own, so the forum-posted key stops working everywhere it propagated without you lifting a finger after the click. For offline installs, a signed revocation list shipped with an update reaches even disconnected clients. The exposure window and the online-versus-offline trade-offs are laid out in license revocation strategies — the short version is that revocation turns a leaked key from a permanent loss into a bounded, recoverable one.

Layer 4 — the edge protection you add in front of /v1/*

Everything above is licensing logic. The last layer is network hygiene, and it is yours to add — Keyright has no built-in rate limiter, and it does not need one, because the app is stateless behind a load balancer. Throttling belongs at the edge you already run: the reverse proxy, load balancer or CDN where you terminate TLS. That is where you put a rate limit, a connection cap, or a full WAF in front of /v1/*.

A few lines of nginx cap requests per client IP and absorb bursts:

# Throttle the activation hot path at your own reverse proxy.
# The app is stateless behind this, so the rate limit lives here.
limit_req_zone  $binary_remote_addr  zone=kr_activate:10m  rate=10r/s;
limit_conn_zone $binary_remote_addr  zone=kr_conn:10m;

server {
    listen 443 ssl;
    server_name licensing.example.com;

    location /v1/ {
        limit_req  zone=kr_activate burst=20 nodelay;  # absorb short bursts
        limit_conn kr_conn 20;                          # cap concurrent conns per IP

        proxy_pass         http://keyright_upstream;
        proxy_set_header   X-Forwarded-For   $remote_addr;   # real client IP
        proxy_set_header   X-Forwarded-Proto $scheme;
    }

    # The admin surface is never public. Restrict it to your network.
    location /admin/ {
        allow 10.0.0.0/8;
        deny  all;
        proxy_pass http://keyright_upstream;
    }
}

A managed cloud WAF or an API gateway does the same job with richer rules — IP reputation, bot scoring, geo controls, automatic DDoS mitigation — but the principle is identical: the limit lives in front of the app, keyed on the client IP, scoped to /v1/*. Keep latency low by sizing the limit from the real workload. Activation and validation are not per-launch calls; a client activates once, caches a signed lease, and validates offline until the lease nears expiry. A given machine therefore touches /v1/* a handful of times a day. A per-IP limit of tens of requests per second with a burst allowance is invisible to that traffic and only clips the IP hammering the endpoint with a shared or scripted key. The hot path itself is a signature check plus a seat count, so the overhead you add is a few milliseconds of proxy work, not a bottleneck.

One detail worth getting right: make sure the real client IP reaches your rate limiter. If another proxy sits in front, forward the genuine client address (here via X-Forwarded-For) so the per-IP limit keys on the abuser, not on your own edge node — otherwise every request looks like it comes from one address and the limit either never fires or clips everyone.

Layer 5 — structured logs that redact the key

You cannot throttle what you cannot see, so log license events structurally — and never log the key. A license key is a bearer credential; writing it to a log that gets shipped to an aggregator or pasted into a support ticket is the same class of mistake as logging a password. Emit one structured event per activation and validation — status, licensee, a short fingerprint of the key, seat and activation counts — so a dashboard can graph "distinct fingerprints seen this week" or alert on a single key activating from dozens of IPs, all without the raw key ever leaving the process. The full pattern — a source-generated event the key is literally unpassable through, mapped onto log levels your alerting already understands — is in structured logging for license events. Those logs are what turn the other four layers from hope into evidence: they are how you notice the shared key, confirm the clone, and decide to revoke.

Where Keyright fits

The thing to internalize is that an unauthenticated activation endpoint is not a weakness to apologize for — it is the correct shape for a path your customers' software must call with nothing but a key. Keyright gives you the licensing layers out of the box: node-locked seats enforced on the server, duplicate-activation and clone detection, one-click revocation that reaches online and offline clients, and structured license events you can log safely. The edge layer — the rate limit, the WAF, the /admin/* lockdown — is yours to run in front, exactly as you already run a proxy in front of any stateless service. Self-hosted Keyright is priced per deployment across Standard, Pro and Enterprise; see the pricing for the current tiers. Defend the open path the way it is meant to be defended — with licensing logic and edge controls, not with a credential you would have had to leak to ship.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.