01Goal and principle
We protect the ENISA portal from attacks. You keep your servers and your application; we sit in front and filter attacks.
The point where we decrypt is the point where we could see content. For your sensitive data we make sure that either we never decrypt (Alternative 1) or the content is sealed so we cannot read it even when we do (Alternative 2). Authorization always stays with your application; we are the attack filter.
02Build CDN-first — protect only what is truly dynamic
The single most effective step: put as much of the site on the CDN as possible, so active protection only covers the small dynamic part.
Static content — pages, images, styles, scripts, documents — is cached at our edge, close to visitors, and served without touching your origin. A flood against a cached page is absorbed at the edge. Only truly dynamic and sensitive requests (login, account actions, data submission) need the active protection in this document.
Why this matters
A CDN-first design shrinks the attack surface — and it shrinks the integration work for Alternative 2 (E2EE), because only a few endpoints need sealing.
| Do | Effect |
|---|---|
| Serve static assets from a separate static domain with long cache lifetimes | Maximum cache hits; keeps static traffic off the origin; avoids coalescing with the sensitive host |
| Use correct cache headers and hashed asset names | Safe long-term caching and instant cache-busting on change |
| Keep dynamic APIs small and specific | Small, clear surface to protect; smaller E2EE integration |
| Allow microcaching of semi-dynamic pages where possible | Edge relief during spikes |
03Traffic planes and three zones
We split the site so that as little as possible — and as controlled as possible — reaches the sensitive part.
| Plane | Content | TLS | How we protect |
|---|---|---|---|
| CDN (static) | Images, CSS, scripts — separate domain, separate certificate | terminated at CDN | Fully cached; near-unattackable; separate domain avoids HTTP/2 coalescing with the sensitive host |
| Zone 1 — portal | Landing page (static) | terminated | Fully cached + microcaching |
| Zone 2 — login | Sign-in | terminated | Background bot detection + tiered challenge; the profile is learned here |
| Zone 3 — sensitive | Accounts, data submission | depends on alternative | Alternative 1: pass-through (we never decrypt). Alternative 2: terminated but sealed end-to-end. See Part II. |
04Control layers — anycast POP and dedicated box
Every point has several layers. On the dedicated box, one shared table holds state across the whole box; a fast store (Redis) distributes it to other boxes and up to the edge.
05What happens to a request
Depending on the content type and the visitor state (unknown / OK / KO), a request goes to a different place. Switch the scenario to see the path. The sensitive case is where the two alternatives differ (Part II).
06Profile and signals
The profile has two levels. A rich profile decides on the box (where we terminate). A thin key propagates down so the edge can evaluate OK/KO.
| Signal | Level | At the edge? | Note |
|---|---|---|---|
| IP | thin key | yes | unstable on mobile — strict: change = block + re-login |
| JA4 | thin key | from learning | native after termination |
| JA4T | thin key | from first packet | from the network handshake (being added) |
| ASN / country / IP class | thin key | yes | stable complement; drives challenge choice |
| JA4H · HTTP/2 · QUIC fingerprint | rich | no | only after termination; refines the decision |
For the protected portal we enforce HTTP/2 only (disable HTTP/1.1 and HTTP/3): one transport, no fingerprint change mid-session. Also JA4T needs a TCP handshake, which HTTP/3 (QUIC) does not have.
Combining signals increases uniqueness, not unforgeability — a targeted attacker can clone a fingerprint. So this is a filter, not a gate; the real boundary remains authorization at your origin.
07Propagation, latency and failure
State is written only by the terminating layer. The edge only reads. The hot path always reads locally; the times below are for replication only.
| Propagation layer | Latency | Note |
|---|---|---|
| Local (same process) | instant | process memory |
| Box-shared (local peers) | sub-ms – a few ms | between processes on one box |
| To other boxes (in region) | a few – tens of ms | replication, not hot path |
| To edge / cross-continent | tens – ~100+ ms | WAN; a block propagates globally |
We balance traffic across all locations, so a failure means only reduced capacity. The edge removes the failed location; a new box reads the profile from the shared store, so the visitor continues without re-login (if within the window). A blocked visitor stays blocked.
08Topology and failover — we recommend 3+ locations
We balance traffic across all dedicated locations (active/active). A failure of one means reduced capacity, not an outage.
| Locations | Assessment |
|---|---|
| 1 | Single point of failure; failure = outage; maintenance = outage. Not for production. |
| 2 | HA, but on failure the other carries 100% load (needs 2× headroom); no quorum. |
| 3+ recommended | Survives one failure with distributed headroom; better coverage; rolling maintenance without outage. |
09Multi-layer limits and rules
On every layer we can limit or block counts and rates. Each counter applies per any dimension, over any time window, and settings can differ per value.
| What we count / limit | Per which dimension | Over which window |
|---|---|---|
| Connections (total, concurrent, new-connection rate) | per IP · per prefix · per ASN · per country · per continent · per IP category · per URL/path · per browser (UA) · per JA4 / JA4H / JA4T — and any combination | 1 s · 10 s · 1 min · 1 h · or a custom window |
| Requests (rate, burst, concurrency) | ||
| Requests per URL |
IP categories are open-ended — we can define any categories we need (datacenter, residential, mobile, hosting, VPN, Tor, search engine, and custom ones), each with its own settings. Settings can also differ per individual IP or per group. Any signal × any counter × any dimension × any time window × any action can be freely combined.
Examples: "max 100 requests per 10 seconds per IP on /api/submit"; "max 5 new connections per second per datacenter IP"; "challenge a country's VPN ranges on the login path"; "rate-limit per ASN per hour worldwide, except an allow-listed partner network."
10Challenges — CAPTCHA and Proof-of-Work
Yes, we have CAPTCHA — and several types of Proof-of-Work. Our approach is invisible-first, so genuine users rarely see a challenge. We tune the trigger, difficulty and type per site and per path.
| Type | What it is | Typical use |
|---|---|---|
| Invisible detection | Silent background checks | Default for most traffic |
| Proof-of-Work (tiered) | A puzzle solved quietly by the device; difficulty scales with risk | Suspicious / high-rate; datacenter, VPN, Tor |
| CAPTCHA | A visible human check | Highest-risk cases or on request |
Quick security check
WEDOS Global Protection · verifying your connection
We are making sure you are a real visitor. For most people this happens automatically, in the background, and needs no action. Only when something looks unusual do we show a short check.
Difficulty scales with risk. Most visitors never see levels 2 or 3. We tune which level applies, per site and per path. Available in all 24 official EU languages.
CAPTCHA and Proof-of-Work are configured and tuned as part of the managed service. There is no extra charge for these features.
11Block page (from CDN cache)
The page a blocked visitor sees: an explanation, contact, and a copyable request summary. It is fully cached in the CDN, so attacking it costs no origin resources. Available in all official EU languages.
Access temporarily restricted
Request blocked by WEDOS Global Protection
This request was temporarily blocked by attack protection. If you are an authorized user, please try again shortly, or sign in. If the problem persists, contact support and include the request summary below.
Ref: a1b2c3d4-ee55-46f7-9a01-d3f2c7b18e90 Time: 2026-06-28 09:41:07 UTC Reason: rate-limit / reputation Node: edge-anycast (LU)
Contact: soc@enisa.europa.eu · wedos.com
Available in all 24 official EU languages. The page is fully cached in the CDN — attacking it requires no origin resources.
12The two alternatives at a glance
For the sensitive flow there are two alternatives. Both share one promise: we cannot read your content. They differ in how much L7 protection we can give and how much you change in your app.
| Alternative 1 — pass-through | Alternative 2 — E2EE payload | |
|---|---|---|
| We decrypt sensitive content? | No (we do not terminate it) | No (it is sealed end-to-end) |
| L7 protection on sensitive flow | Network level + profile | Full L7 (over the sealed data) |
| Change in your app | None | A small change (we provide the parts) |
| Start | Immediately | When your team is ready |
13L7 protection without seeing content (pass-through)
We give full protection where it is safe to look, and we deliberately do not decrypt the sensitive zone. This needs no change in your application.
For the sensitive zone we do not hold the keys and do not see the content. We decide using signals that do not need the content: the source (IP, ASN, country), the fingerprint learned at login, the request rate and size, and the trusted/blocked/unknown state carried from the login zone. Known-bad and over-limit traffic is stopped near the visitor; only good traffic reaches your origin, which decrypts and authorizes.
| On the sensitive flow, do we... | |
|---|---|
| See the source and the amount/shape of traffic | yes |
| See the connection fingerprint learned at login | yes |
| Read the actual content | no |
What happens to a request here
- Arrive. The visitor reaches the nearest edge location (anycast).
- Sort. We classify the visitor as trusted, unknown, or blocked — from signals that do not need the content.
- Serve or check. Static parts come straight from the CDN cache; the login zone is terminated, with bot detection and a challenge only if the visitor looks risky.
- Pass through. For the sensitive zone we do not decrypt. Known-bad and over-limit traffic is stopped first; the rest is forwarded to your origin, still sealed.
- Open at your origin. Your server decrypts and authorizes. We never saw the content.
Termination vs pass-through — where the lock opens
The point where the lock opens is where we can see content. In this alternative, for the sensitive zone it opens only at your origin.
Nothing in your application. This alternative is a routing and protection design on our side — the fastest way to protect the sensitive flow while guaranteeing we never see its content.
14L7 protection with sealed content (E2EE / HPKE)
Here we do terminate the sensitive flow and give it full L7 protection — yet still cannot read it, because the content is sealed end-to-end with a key we never hold.
In plain terms: your website locks the sensitive content in a box before it leaves the visitor. We guard that box the whole way — but we never hold the key. Only your own server can open it.
Think of a locked box travelling inside a glass tube. Everyone can see the box move, and we can guard it and check its outside — but only the person with the key can open it. That is end-to-end encryption.
How it works, in plain words
It works like a second layer of HTTPS, carried all the way to the ENISA origin. Your origin publishes its public key. We only deliver that key to the browser — without the matching private key it is useless to us. The browser uses it to protect the content, sends it through us encrypted, and only your origin can open it. We see the ciphertext and the metadata, never the content.
This is not “RSA and prime numbers”. The browser takes the origin's public key and its own one-time key and mathematically derives a shared key (Elliptic-Curve Diffie–Hellman over P-256). It encrypts the content with that shared key (AES-256-GCM). Your origin derives the same shared key from its private key — and only it can. So the public key does not encrypt the data directly; it is used to agree a shared key. We never hold the private half, so we cannot derive the key and cannot read the payload.
Forward secrecy. The browser makes a fresh one-time (ephemeral) key for every message, sends its public part in the envelope, and discards the private part. Even if a key were compromised later, past messages still cannot be decrypted.
Tamper-evidence — the bonus. This does not come from the encryption alone; it comes from authenticated encryption (AES-GCM), which attaches a cryptographic tag to every message. If anyone, including us, changes a single bit of the envelope, decryption at your origin fails and the message is rejected. The envelope header (method, path, time, request id) is bound into that tag too, so a sealed body cannot even be replayed to a different endpoint. The “Simulate tampering” toggle in the demo shows exactly this.
It works like a second layer of HTTPS taken all the way to your origin. You publish your public key; we only deliver it, and without your private key it is useless to us. The browser derives a shared key from it (ECDH over P-256), encrypts the content (AES-GCM) and sends it through us — we see only ciphertext and metadata. And thanks to authenticated encryption, no change in transit goes unnoticed: your origin rejects it.
How it works — in four steps
The technical version: the client (a browser script or a machine client) wraps only the body of the request in a sealed envelope (using HPKE, RFC 9180) addressed to your server's public key. Only the payload is sealed — structure, headers, sizes and fingerprint stay visible, so we apply rate-limiting, bot detection, challenges and a structural firewall before your origin. Your origin unseals it with its private key (in an HSM). We do not use nested TLS — only the payload is sealed.
What happens to a request here
- Seal. The visitor's app wraps the request body in a sealed envelope (HPKE) addressed to your origin's public key.
- Arrive. It reaches the nearest edge over HTTP/2, sealed the whole way.
- Guard. We terminate the outer connection and apply full L7 — rate limits, bot checks, a challenge if needed, and a structural firewall — reading only the outside (headers, size, fingerprint).
- Forward. We pass the still-sealed body to your origin.
- Unseal. Your origin opens it with its key (HSM), checks the client is authorized, processes it, and seals the reply.
14.1 For people and for machines
The same sealed-envelope format is used by everyone; only how the client obtains its key differs. Confidentiality to the server needs no client key. Proving "authorized only" uses a client key, or a password-based method (OPAQUE) for people.
| Participant | Key handling | Proof of "authorized" | Another device |
|---|---|---|---|
| Machine (M2M) | Long-term key at setup; private key in a secure store / HSM, never leaves the client | Registered public key | n/a |
| Person · trusted device | Browser generates a non-exportable key at first sign-in | Registered public key | Password fallback or key backup |
| Person · password only | No stored key — a password method (OPAQUE) unlocks a wrapped key | Password (OPAQUE) session | Any device (password is enough) |
14.2 Can you just add an encryption JavaScript to the browser?
Yes — a small drop-in JavaScript SDK. But it always needs a matching piece at your origin, because the data must be decrypted somewhere to be used. There is no browser-only version. With a CDN-first site, only a few dynamic endpoints need sealing, so the integration is small.
- Client: add one
<script>tag and a short config (which endpoints to seal, your server's public key). The SDK seals request bodies and unseals responses transparently. - Server: add a small decrypt/encrypt step on those endpoints, with your private key in an HSM.
- Delivery integrity: serve the SDK and the page that loads it via pass-through or your own origin, and pin the file hash with SRI.
- Identity: register client keys (machines) or use OPAQUE (people).
14.3 The one real weakness — delivery of the crypto code
In the browser, E2EE is only as strong as the integrity of the code that encrypts. Whoever serves the page could substitute crypto that leaks the key. Because we terminate the outer connection, we are structurally in that position — so the guarantee against us is only as strong as the code delivery.
| Measure | What it addresses |
|---|---|
| Root of trust outside WGP | Serve the bootstrap and SDK via pass-through or your origin — trust must not pass through a layer we can modify |
| SRI | The bootstrap pins the SDK hash; a tampered file is rejected |
| Code signing / installed app | Stronger binding than SRI alone |
| Native / machine client | No JavaScript delivery — the strongest option |
Even with SRI, the page carrying the hash must arrive with integrity, so the root of trust must sit outside WGP (your origin or pass-through). The strongest guarantee is an installed client. Metadata (sizes, timing, paths) stays visible — mitigate with padding and uniform endpoints.
14.4 Deployment effort and CRA
Effort: low-to-moderate and phased. You add the SDK on a few endpoints, a decrypt step at the origin, a server keypair (HSM), and choose the identity model. Alternative 1 needs zero app change and can start now; Alternative 2 is added later on the endpoints that need it.
With per-client keys, this also answers "authorized clients only" for the machine-to-machine data-submission endpoint at the application layer. For the EU Cyber Resilience Act it is a strong, provable story: full protection while demonstrably never seeing the content — data minimization by design.
15What we see outside the envelope
A clear line for the record: in both alternatives we never see the content. Everything else is operational metadata. This is what “we protect it but cannot read it” means in practice.
The design rule that follows: sensitive data goes only inside the sealed body — never in the URL, headers, or cookies, because those are visible to us in Alternative 2.
| What it is | Alt 1 — pass-through | Alt 2 — E2EE (we terminate outer TLS) |
|---|---|---|
| Source IP, port, geo, network (ASN) | visible | visible |
| Time, request rate, sizes, RTT, connection reuse | visible | visible |
| SNI (target hostname), ALPN, TLS version | visible | visible |
| TLS / network fingerprint — JA4 / JA4T | visible | visible |
| HTTP method (POST…) | hidden | visible |
| URL / path, query string | hidden | visible |
| HTTP headers — User-Agent, Referer, Cookie, Authorization, Content-Type | hidden | visible |
| HTTP fingerprint — JA4H | hidden | visible |
| Response status and size | size only | visible |
| Envelope header — v, suite, key_id, ts, rid | n/a (nothing in the clear) | visible (metadata, not content) |
| Approx. body length (from ciphertext size) | leaks via packet sizes | leaks via ciphertext size |
| Body content — the actual data | hidden | hidden |
Alternative 1 hides more — we do not terminate, so method, path and headers stay inside TLS — but gives only network-level protection on the sensitive flow. Alternative 2 exposes the request envelope and headers to us so we can apply full L7, yet the body stays sealed.
What can be inferred without reading the content
Even sealed, some things leak from the shape of traffic: the message length (ciphertext ≈ plaintext + 16 bytes), timing and frequency, which endpoint was called (a path like /api/delete reveals intent), and per-user correlation via IP, fingerprint, cookie or request id.
| Leak | How to reduce it |
|---|---|
| Message length | Pad the body to fixed-size buckets |
| Which endpoint / action | Use one uniform endpoint; put the action inside the sealed body |
| Headers / cookies exposure | Keep headers minimal; carry nothing sensitive outside the body |
| Timing / frequency | Batch or add jitter where feasible |
We see the envelope and operational metadata — who, where, when, how big, which endpoint, which fingerprint — but never the content. Length and timing cannot be fully hidden (that is traffic physics), only blurred.
16Choosing between them
You do not have to choose forever now — the two can coexist.
Alternative 1 today
Instant protection of the sensitive flow, with no change in your app. We can switch it on for you.
Alternative 2 later
Full L7 protection on the few dynamic endpoints that need it, sealed end-to-end. Smaller if the site is CDN-first.
Our suggestion
Begin with Alternative 1 for immediate cover, then add Alternative 2 on the data-submission endpoints when your team is ready.
17Full configuration list — everything we can do
Everything below is configurable per site and can be combined. You do not need to learn this — we set it up and tune it for you.
16.1 Signals we can decide on
| Group | Signals |
|---|---|
| Source / identity | IP, prefix, ASN, country, continent, IP category (datacenter / residential / mobile / hosting / VPN / Tor / search engine) |
| Request target | URL / path, HTTP method, host, Content-Type, request size |
| Client / browser | User-Agent, Referrer, headers, login cookies; UA quality classes (blacklisted / bad / outdated / AI-agent / missing / too short / whitespace) |
| Fingerprint | JA4, JA4H, JA4T (being added), HTTP/2 and HTTP/3 fingerprint, JA4 composite |
| Reputation lists | Spamhaus (DROP / ASN-DROP), abuse.ch (Feodo / ThreatFox), GreyNoise (RIOT allow-list) |
| Behaviour | Request rate, parallel connections, new-connection rate, error patterns, honeypots, learned baseline, automation signals |
| Application | CMS autodetection (WordPress, Drupal, Magento, PrestaShop, Joomla, and more) → tailored rules; WebSocket; combined rules |
16.2 Limits — per dimension, per time window
Connections, requests, and requests per URL — over 1 s / 10 s / 1 min / 1 h or a custom window — per IP, prefix, ASN, country, continent, IP category, URL/path, browser, or fingerprint, and in any combination. IP categories are open-ended, and per-IP settings can differ.
16.3 Actions
| Action | Meaning |
|---|---|
| ALLOW | Let it through |
| LOG | Record only |
| RATE-LIMIT | Slow it down, per any dimension and window |
| CHALLENGE | Invisible check / Proof-of-Work (tiered) / CAPTCHA |
| BLOCK | Drop, error code, or cached info page |
16.4 Other controls
| Area | What we can do |
|---|---|
| Caching / CDN | Full cache, microcache, static offload, Brotli/gzip, cached error/block pages |
| Protocol | Enforce HTTP/2 only, HSTS, TLS management; pass-through vs termination per zone |
| Web firewall (WAF) | Rules on path, method, headers, content type; structural validation (sealed-envelope format); CMS-aware rules |
| Visitor state | Trusted / blocked / unknown, shared across locations; demote on anomaly |
| Logging & reports | Traffic logs (metadata only for sealed traffic), cross-layer correlation, 18-month retention, reports |
18Appendix — Due diligence (summary)
| Question | Answer |
|---|---|
| Penetration testing | As part of onboarding and handover we perform penetration testing of the deployed L7 protection. It is included in the service. We can share relevant results and a summary on completion, or sample reports under NDA if required. |
| Sanctions | WEDOS has never been subject to any sanctions — regulatory, financial, or political. |
| Data portability & exit | The service is transparent and non-locking. Transition in and out is straightforward, with no dependency on proprietary components. We do not store customer content or application data; upon termination no customer data remains, aside from agreed operational records. |
| GDPR | We do not process or store customer application data. We only handle CDN cache data (temporary, auto-expiring) and traffic logs (limited, pre-agreed retention). WEDOS has never been subject to GDPR enforcement actions or fines. |
| Company profile | Largest domain registrar in the Czech Republic (~23% share). ICANN-accredited registrar, RIPE member; among the top registrars for several TLDs (e.g. top 20 for .EU). DNS with DNSSEC, web and email hosting for hundreds of thousands of domains, thousands of virtual servers. References on request. |
| Ownership & build | Privately owned, no external investors, loans, or liabilities. All products are open-source based with our own know-how added; nothing is outsourced — built in-house. |
19Versions
| Version | Date | Change |
|---|---|---|
| 2.3.0 | 2026-06-28 | Added a plain-words explainer at the top of section 14: the HTTPS analogy, the ECDH shared-key derivation (not RSA/primes), forward secrecy, and tamper-evidence via authenticated encryption. |
| 2.2.0 | 2026-06-28 | Added section 15 “What we see outside the envelope” — a visible/hidden table contrasting Alternative 1 and Alternative 2, plus metadata-leakage mitigations. |
| 2.1.0 | 2026-06-28 | Separated the two alternatives more strongly with full-width chapter banners, and added a parallel “What happens to a request here” step-by-step walkthrough inside each alternative. |
| 2.0.0 | 2026-06-28 | Merged the architecture and the protection plan into one document, reorganized into three parts, with the two alternatives clearly separated. Added CDN-first, time-windowed limits, a CAPTCHA/PoW challenge design, and a step-by-step E2EE explainer. |
Draft for discussion. No production change is proposed here; final configuration is set after agreement and validated in our environment before deployment.