<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Au</title>
    <description>The latest articles on DEV Community by Alex Au (@alexuau922).</description>
    <link>https://dev.to/alexuau922</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3814910%2Fd1b96662-9d80-4a1f-9705-51ed464bda89.webp</url>
      <title>DEV Community: Alex Au</title>
      <link>https://dev.to/alexuau922</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexuau922"/>
    <language>en</language>
    <item>
      <title>When Tenants Can See One Another: From Signed Artifacts to Authenticated State Transitions (Part 1)</title>
      <dc:creator>Alex Au</dc:creator>
      <pubDate>Fri, 19 Jun 2026 17:02:13 +0000</pubDate>
      <link>https://dev.to/alexuau922/when-tenants-can-see-one-another-from-signed-artifacts-to-authenticated-state-transitions-part-1-4hja</link>
      <guid>https://dev.to/alexuau922/when-tenants-can-see-one-another-from-signed-artifacts-to-authenticated-state-transitions-part-1-4hja</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We encrypted every competition submission and required a &lt;strong&gt;valid team signature&lt;/strong&gt;. Yet another GitHub user could &lt;strong&gt;copy an unchanged public bundle&lt;/strong&gt; and &lt;strong&gt;spend the original team’s daily quota&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This postmortem explains &lt;strong&gt;why the cryptography worked&lt;/strong&gt;, &lt;strong&gt;why the system still failed&lt;/strong&gt;, and &lt;strong&gt;why secure public workflows must authenticate state transitions&lt;/strong&gt; rather than artifacts alone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Postmortem status&lt;/strong&gt;: The workshop has ended, and the affected scoring workflows have been manually disabled. There is &lt;strong&gt;no active scoring endpoint&lt;/strong&gt; associated with this design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How a valid digital signature became a transferable authorization token in a public GitHub competition
&lt;/h2&gt;

&lt;p&gt;We encrypted the submission.&lt;/p&gt;

&lt;p&gt;We signed its manifest.&lt;/p&gt;

&lt;p&gt;We verified the signature against a team certificate.&lt;/p&gt;

&lt;p&gt;The signature was valid.&lt;/p&gt;

&lt;p&gt;And yet, another GitHub user could copy the complete submission into a new pull request and make the original team spend another scoring attempt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33oeorwah6l69wo6v1s5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F33oeorwah6l69wo6v1s5.png" alt="Figure 1 — Original workflow" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No private key had to be stolen.&lt;/p&gt;

&lt;p&gt;No signature had to be forged.&lt;/p&gt;

&lt;p&gt;No ciphertext had to be decrypted.&lt;/p&gt;

&lt;p&gt;The cryptography did &lt;strong&gt;exactly&lt;/strong&gt; what we asked it to do.&lt;/p&gt;

&lt;p&gt;The problem was that we had asked the &lt;strong&gt;wrong security question&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A GitHub-native data-cleaning tournament
&lt;/h2&gt;

&lt;p&gt;In May 2026, we ran a data-cleaning tournament as part of a &lt;a href="https://github.com/pythonhk/20260530-data-workshop" rel="noopener noreferrer"&gt;Python workshop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each team wrote a Python cleaner that transformed public training and test feature files. The official scorer then trained a fixed model, evaluated the cleaned test data against hidden labels, and recorded the result on a shared leaderboard.&lt;/p&gt;

&lt;p&gt;We wanted several properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;teams should &lt;strong&gt;not be able to inspect the hidden test labels&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;organizers should be able to execute each cleaner consistently;&lt;/li&gt;
&lt;li&gt;teams should not need to &lt;strong&gt;reveal their cleaner source code publicly&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;only &lt;strong&gt;an authorized team&lt;/strong&gt; should be able to submit under its assigned team identity;&lt;/li&gt;
&lt;li&gt;each team should receive only a &lt;strong&gt;limited number of scored attempts per day&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub already provided most of the surrounding infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;participant identity;&lt;/li&gt;
&lt;li&gt;forks and pull requests;&lt;/li&gt;
&lt;li&gt;workflow events;&lt;/li&gt;
&lt;li&gt;hosted runners;&lt;/li&gt;
&lt;li&gt;workflow artifacts;&lt;/li&gt;
&lt;li&gt;an auditable repository history;&lt;/li&gt;
&lt;li&gt;GitHub Pages for the leaderboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we used pull requests as the submission transport.&lt;/p&gt;

&lt;p&gt;The system looked approximately like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Team writes clean.py
        │
        │ encrypt to organizer certificate
        ▼
submission/clean.py.cms
        │
        │ SHA-256
        ▼
submission/manifest.json
        │
        │ sign with team private key
        ▼
submission/manifest.sig
        │
        │ public pull request
        ▼
GitHub Actions
        │
        ├── verify manifest signature
        ├── decrypt cleaner
        ├── execute cleaner
        ├── encrypt cleaned outputs
        └── pass outputs to trusted scorer
                │
                ├── decrypt hidden labels
                ├── train fixed model
                ├── calculate score
                └── update leaderboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An official submission contained the encrypted cleaner, the manifest, its detached signature, and the dependency files required to execute the cleaner.&lt;/p&gt;

&lt;p&gt;The repository’s helper script encrypted &lt;code&gt;clean.py&lt;/code&gt;, calculated the ciphertext’s SHA-256 digest, placed that digest in the manifest, and signed the manifest using the team’s private key.&lt;/p&gt;

&lt;p&gt;At first glance, this appeared to provide a &lt;strong&gt;clean chain of trust&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What exactly did the signature authenticate?
&lt;/h2&gt;

&lt;p&gt;A simplified manifest looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"team_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"team-06"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submission/clean.py.cms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code_sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b7a4...64 hexadecimal characters..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verification script performed several sensible checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;validate the format of &lt;code&gt;team_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;require the code path to be &lt;code&gt;submission/clean.py.cms&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;calculate the actual SHA-256 digest of the encrypted cleaner;&lt;/li&gt;
&lt;li&gt;require that digest to match &lt;code&gt;code_sha256&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;look up the team’s certificate in an allowlist;&lt;/li&gt;
&lt;li&gt;validate that certificate against the tournament CA;&lt;/li&gt;
&lt;li&gt;verify the detached signature over the manifest.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The workflow then installed the submitted dependencies, decrypted the cleaner, ran it against the tournament data, validated its outputs, and uploaded encrypted scoring artifacts.&lt;/p&gt;

&lt;p&gt;The verifier was therefore able to establish:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The holder of Team 06’s private key signed this exact manifest, and the encrypted cleaner currently present has the digest recorded in that manifest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That statement was true.&lt;/p&gt;

&lt;p&gt;But it was narrower than the statement we implicitly relied upon:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Team 06 is authorizing this particular GitHub pull request as a new scoring attempt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are not the same statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  The missing subject: who is presenting the signed artifact?
&lt;/h2&gt;

&lt;p&gt;A digital signature binds a key to some bytes.&lt;/p&gt;

&lt;p&gt;It does not automatically bind those bytes to every context in which they might later appear.&lt;/p&gt;

&lt;p&gt;Our signed manifest included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;team identity&lt;/li&gt;
&lt;li&gt;encrypted-code path&lt;/li&gt;
&lt;li&gt;encrypted-code digest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It did not include, or otherwise bind itself to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the current GitHub PR author&lt;/li&gt;
&lt;li&gt;the author’s immutable GitHub user ID&lt;/li&gt;
&lt;li&gt;the source repository&lt;/li&gt;
&lt;li&gt;the pull request number&lt;/li&gt;
&lt;li&gt;the current head commit&lt;/li&gt;
&lt;li&gt;a unique logical attempt ID&lt;/li&gt;
&lt;li&gt;an expiry time&lt;/li&gt;
&lt;li&gt;a one-time challenge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, the workflow did not independently compare the current PR actor or source repository with an identity authorized to submit for the signed &lt;code&gt;team_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The workflow took the submitted files from the pull request’s head repository and verified the team manifest, but the verification decision remained entirely about the artifact. It did not ask whether the &lt;strong&gt;GitHub user presenting that artifact was entitled to act for the team&lt;/strong&gt; named inside it.&lt;/p&gt;

&lt;p&gt;In effect, our valid signed bundle had become a &lt;strong&gt;bearer artifact&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anyone possessing an unchanged copy could present it, while the system continued attributing the action to the original signing team.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The signature was public evidence of &lt;strong&gt;past authorization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We had accidentally treated it as reusable authority for a new action.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Threat Model (Replay Attack)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9erjumb79fw2ktuu12x0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9erjumb79fw2ktuu12x0.png" alt="Figure 2 — Replay sequence" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider two participants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice   — a legitimate member of Team A
Charlie — a different GitHub user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alice prepares a valid submission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submission/clean.py.cms
submission/manifest.json
submission/manifest.sig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Her manifest says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"team_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"team-a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"submission/clean.py.cms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code_sha256"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"H"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Team A’s private key signs that manifest.&lt;/p&gt;

&lt;p&gt;Alice opens a pull request.&lt;/p&gt;

&lt;p&gt;The submission is validly scored.&lt;/p&gt;

&lt;p&gt;Because the pull request belongs to a public repository, Charlie can obtain the submitted encrypted cleaner, manifest, signature, and associated public dependency files.&lt;/p&gt;

&lt;p&gt;Charlie does not edit them.&lt;/p&gt;

&lt;p&gt;He does not need to understand the cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;He simply places the same files in another branch or fork and opens another pull request&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The verifier now observes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manifest signature: valid
Team certificate: valid
Ciphertext SHA-256: matches
Team ID: team-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every cryptographic check succeeds, because the signed artifact has not changed.&lt;/p&gt;

&lt;p&gt;The trusted scorer subsequently obtains &lt;code&gt;team_id&lt;/code&gt; from that manifest. It counts the day’s previous leaderboard submissions carrying the same team ID, &lt;strong&gt;checks the configured daily limit&lt;/strong&gt;, and appends each accepted result as another submission for that team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Therefore Charlie’s replay is charged to Team A&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Repeated replays can consume Team A’s available daily attempts.&lt;/p&gt;

&lt;p&gt;After that, Alice may be &lt;strong&gt;unable to submit another scored attempt&lt;/strong&gt; that day.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem was not duplicate content
&lt;/h2&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;It may be perfectly legitimate for Alice to submit exactly the same cleaner twice.&lt;/p&gt;

&lt;p&gt;Perhaps she wants to confirm reproducibility. Perhaps she accidentally changed something elsewhere. Perhaps competition policy simply says that every intentional submission consumes one attempt, even when the code is identical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wj7ljz6y1in1geldu0j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wj7ljz6y1in1geldu0j.png" alt="Figure 3 — Same content, different attempts" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The desired policy may be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alice intentionally submits content H as attempt A-001
→ accepted and charged to Alice’s team

Alice intentionally submits content H again as attempt A-002
→ accepted and charged again to Alice’s team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vulnerability was not that identical content could ever be processed more than once.&lt;/p&gt;

&lt;p&gt;The vulnerability was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Charlie could make that decision on Alice’s behalf.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A secure redesign therefore should not necessarily deduplicate by file hash.&lt;/p&gt;

&lt;p&gt;It should distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;content identity&lt;/strong&gt; — whether two submissions contain the same files;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;attempt identity&lt;/strong&gt; — whether they represent the same logical submission attempt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;principal identity&lt;/strong&gt; — who is requesting the attempt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;team attribution&lt;/strong&gt; — which team the authenticated principal belongs to.
An explicit, signed attempt identifier can allow Alice to resubmit the same content intentionally, while still making workflow retries idempotent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Binding the request to Alice’s authenticated GitHub identity prevents Charlie from replaying Alice’s signed attempt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why stronger encryption would not have fixed it
&lt;/h2&gt;

&lt;p&gt;It is tempting to look at a replay involving an encrypted file and conclude that the encryption mechanism needs to be strengthened.&lt;/p&gt;

&lt;p&gt;But confidentiality was not the failed property.&lt;/p&gt;

&lt;p&gt;Charlie never learned the contents of the encrypted cleaner.&lt;/p&gt;

&lt;p&gt;Changing the encryption algorithm would not stop him from &lt;strong&gt;&lt;em&gt;copying the exact ciphertext&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Authenticated encryption can detect modification. It does not, by itself, prevent an unchanged valid ciphertext from being presented again.&lt;/p&gt;

&lt;p&gt;Likewise, the digital signature correctly prevented Charlie from changing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;team-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;team-charlie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any such modification would invalidate Team A’s signature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But Charlie did not need to modify anything&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Replay attacks live in the gap between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This message is authentic.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This message is authorized here, now, by the actor currently presenting it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cryptographic authenticity is one input to authorization. It is not the &lt;strong&gt;complete authorization decision&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Public multi-tenancy changes the threat model
&lt;/h2&gt;

&lt;p&gt;In many multi-tenant systems, tenants are separated by &lt;strong&gt;confidentiality boundaries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tenant A cannot inspect Tenant B’s records, credentials, or requests.&lt;/p&gt;

&lt;p&gt;Our environment was different.&lt;/p&gt;

&lt;p&gt;Every team interacted through the same public repository. Pull requests were &lt;strong&gt;visible&lt;/strong&gt;. Signed artifacts were &lt;strong&gt;visible&lt;/strong&gt;. Encrypted submissions were &lt;strong&gt;visible&lt;/strong&gt;. Tenants could inspect and copy one another’s transport-level records.&lt;/p&gt;

&lt;p&gt;This was not an accidental data leak. &lt;strong&gt;Public visibility was part of the platform model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That leads to a useful design rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a publicly observable multi-tenant system, every public artifact must be assumed copyable and replayable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Security cannot depend on another tenant being unable to obtain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a manifest
a public key
a signature
an encrypted bundle
a previous request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, each proposed action must be evaluated using both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;the submitted artifact
+
the authenticated context in which it is being presented
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For GitHub, that context may include the event’s immutable actor ID, repository ID, pull request metadata, a protected identity registry, and the current authoritative system state.&lt;/p&gt;




&lt;h2&gt;
  
  
  The root cause in one sentence
&lt;/h2&gt;

&lt;p&gt;The root cause was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We treated possession of a valid signed artifact as authorization to initiate a new state transition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The signature answered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who signed these bytes?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scorer needed answers to several additional questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Who is presenting them now?

Is that GitHub identity registered to the signing key?

Does that identity belong to the claimed team?

Is this a new intentional attempt or a retry of an existing attempt?

Is the requested transition allowed under the team’s current quota?

Which authoritative state should be updated?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those questions belong to an authorization and state-management layer, not to the signature algorithm alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Operational response
&lt;/h2&gt;

&lt;p&gt;By the time of this publication, the workshop had ended and the relevant cleaner and scoring workflows had been manually disabled. GitHub’s workflow pages show the affected workflows as disabled, so the historical design is no longer an active scoring surface.&lt;/p&gt;

&lt;p&gt;We are publishing this as a postmortem rather than as a vulnerability in GitHub.&lt;/p&gt;

&lt;p&gt;GitHub Actions behaved according to its configuration.&lt;/p&gt;

&lt;p&gt;The design error existed in our application-level trust model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public pull requests
+
reusable signed artifacts
+
team-based quotas
+
no binding between the current actor and the signed team identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  From signed artifacts to authenticated state transitions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zrwg3jqyxu9o0ydqrwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zrwg3jqyxu9o0ydqrwm.png" alt="Figure 4 — Authenticated state transition" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The redesigned system begins with a different abstraction.&lt;/p&gt;

&lt;p&gt;A pull request does not directly become a registration or submission.&lt;/p&gt;

&lt;p&gt;Instead, it is an &lt;strong&gt;untrusted proposal&lt;/strong&gt; for a state transition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Public PR artifact
        +
Authenticated GitHub actor
        +
Registered participant key
        +
Immutable team membership
        +
Current quota and attempt state
        ↓
Policy verification
        ↓
Authorized state transition
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important question is no longer only:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this signature valid?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this authenticated actor authorized to request this exact transition in the system’s current state?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next part of this series will examine the first half of that redesign:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;self-service&lt;/strong&gt; participant onboarding;&lt;/li&gt;
&lt;li&gt;binding a GitHub identity to a participant public key;&lt;/li&gt;
&lt;li&gt;allowing organizers to &lt;strong&gt;accept previously unknown participants&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;requiring every member to &lt;strong&gt;consent to the same complete team manifest&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;making team membership immutable for the duration of the competition;&lt;/li&gt;
&lt;li&gt;storing the resulting identity and team records as auditable authoritative state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final part will return to submissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;actor-bound submission manifests&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;explicit attempt identifiers;&lt;/li&gt;
&lt;li&gt;intentional resubmission of identical content;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cross-principal replay prevention&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;quota reservation;&lt;/li&gt;
&lt;li&gt;concurrency;&lt;/li&gt;
&lt;li&gt;and scoring attribution.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;The most important lesson was not about a particular encryption mode or signature algorithm.&lt;/p&gt;

&lt;p&gt;It was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A valid signature proves that a key endorsed some bytes. It does not prove that the person presenting those bytes now is entitled to trigger a new action.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a system where every tenant can see every other tenant’s artifacts, signed objects should be treated as public claims—not as bearer authority.&lt;/p&gt;

&lt;p&gt;Authentication, authorization, freshness, identity binding, and state transition policy must still happen at the moment the artifact is used.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cryptography</category>
      <category>githubactions</category>
      <category>postmortem</category>
    </item>
    <item>
      <title>Zero-Trust at the Edge: Rethinking the eDMZ Perimeter (Part 1)</title>
      <dc:creator>Alex Au</dc:creator>
      <pubDate>Mon, 09 Mar 2026 15:58:52 +0000</pubDate>
      <link>https://dev.to/alexuau922/zero-trust-at-the-edge-rethinking-the-edmz-perimeter-part-1-g86</link>
      <guid>https://dev.to/alexuau922/zero-trust-at-the-edge-rethinking-the-edmz-perimeter-part-1-g86</guid>
      <description>&lt;h3&gt;
  
  
  Evolving the Asymmetric WAF-Pass Architecture for Speed and Scale
&lt;/h3&gt;



&lt;p&gt;A few months ago, cloud security architect &lt;a href="https://www.linkedin.com/in/chinglong-kevin-yu/" rel="noopener noreferrer"&gt;Kevin Yu&lt;/a&gt; published an excellent article titled '&lt;a href="https://www.linkedin.com/pulse/copy-designing-asymmetric-waf-pass-jwt-assertion-zonal-yu-togaf--edjsc/" rel="noopener noreferrer"&gt;Designing Asymmetric WAF-Pass JWT Assertion&lt;/a&gt;'. He highlighted a massive, often-ignored vulnerability in modern cloud architectures: the reliance on static custom headers (e.g., &lt;code&gt;X-WAF-Checked: true&lt;/code&gt;) to verify that traffic hitting an Origin actually passed through the CDN and Web Application Firewall (WAF).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1zukgksckgsacw0k6al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1zukgksckgsacw0k6al.png" alt="The Static Header Vulnerability"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kevin is absolutely right about the problem. Static headers provide &lt;strong&gt;zero cryptographic integrity&lt;/strong&gt;. They are essentially shared passwords; if they leak, your WAF is permanently bypassed, and your Origin is exposed to the open internet.&lt;/p&gt;

&lt;p&gt;To solve this, Kevin proposed an innovative architecture: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using a &lt;code&gt;Lambda@Edge&lt;/code&gt; function to make a synchronous network call to a Regional API Gateway&lt;/li&gt;
&lt;li&gt;Triggering a Regional Lambda, which calls AWS KMS to generate an Asymmetric JWT&lt;/li&gt;
&lt;li&gt;Passing it all the way back to the Edge to be forwarded to the Origin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is a highly secure, effective solution that brilliantly solves 95% of the architectural challenge. But as a Cybersecurity-backgrounded Cloud Platform Engineer who spends my days obsessing over &lt;em&gt;system performance&lt;/em&gt; and &lt;em&gt;cloud unit economics&lt;/em&gt;, it got me thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if we could extend this design to &lt;strong&gt;99.9% operational efficiency&lt;/strong&gt; — maintaining that Zero-Trust perimeter with just &lt;strong&gt;a fraction of the latency and cost&lt;/strong&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Security at the edge is a delicate balancing act. In this three-part series, we are going to break down the cryptography of the Edge perimeter. I will propose a cloud-native, symmetric alternative that achieves this Zero-Trust pattern entirely at the Edge, open-source a custom benchmarking engine to prove the latency savings mathematically, and dive into the FinOps reality of securing the eDMZ.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Threat Model of the eDMZ
&lt;/h3&gt;



&lt;p&gt;Before we talk about cryptography, we must define the exact threat model we are solving.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fu0aam5je2dw7e76pjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fu0aam5je2dw7e76pjh.png" alt="The Threat Model and how eDMZ solves it"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you place a WAF in front of an Application Load Balancer (ALB), EC2 instance, or custom cloud backend, you are creating an Enterprise DMZ (eDMZ). The Origin backend must have absolute mathematical certainty of two things before accepting a request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The request definitely passed through CloudFront and AWS WAF.&lt;/li&gt;
&lt;li&gt;The routing parameters (URI, Headers) have not been tampered with in transit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an attacker discovers your Origin's raw IP address or public DNS, they will attempt to &lt;strong&gt;bypass the WAF entirely&lt;/strong&gt;. We need a way for the Edge to &lt;strong&gt;mathematically "sign"&lt;/strong&gt; the request so the Origin can definitively trust its provenance.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Cryptography Refresher: Asymmetric vs. Symmetric
&lt;/h3&gt;



&lt;p&gt;Kevin’s architecture relies on Asymmetric Cryptography (RSA/ECDSA) to generate JWTs via AWS KMS.&lt;/p&gt;

&lt;p&gt;Asymmetric cryptography is a beautiful mathematical construct, perfect for &lt;em&gt;human identity verification&lt;/em&gt; (like OIDC) or highly distributed claims where the verifier cannot be trusted with the signing key. However, for &lt;em&gt;Machine-to-Machine (M2M)&lt;/em&gt; perimeter routing inside the same organization, it can be computationally heavy.&lt;/p&gt;

&lt;p&gt;For a fast, internal perimeter, &lt;strong&gt;Time-Based Symmetric Cryptography (HMAC-SHA256)&lt;/strong&gt; is an incredibly powerful alternative. This is the exact cryptographic algorithm AWS uses for &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html" rel="noopener noreferrer"&gt;SigV4&lt;/a&gt; to authenticate billions of internal API requests per second.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;small&gt;🔬 Crypto Deep-Dive: The Math of HMAC-SHA256&lt;/small&gt;&lt;/strong&gt;&lt;br&gt;
&lt;small&gt;&lt;/small&gt;&lt;br&gt;
&lt;small&gt;When designing secure cloud perimeters, engineers often confuse confidentiality with integrity. We frequently hear acronyms like &lt;b&gt;IND-CPA&lt;/b&gt; (Indistinguishability under Chosen Plaintext Attack) or &lt;b&gt;IND-CCA&lt;/b&gt; — but those are security models for encryption schemes. In eDMZ routing, we aren't encrypting the HTTP headers; we are authenticating them&lt;/small&gt;.&lt;br&gt;
&lt;small&gt;&lt;/small&gt;&lt;br&gt;
&lt;small&gt;Because &lt;code&gt;HMAC-SHA256&lt;/code&gt; is a Message Authentication Code (MAC) rather than an encryption cipher, the mathematically correct security proof is &lt;b&gt;EUF-CMA&lt;/b&gt; (Existential Unforgeability under Chosen Message Attack). In the realm of Authenticated Encryption, this provides the critical &lt;b&gt;INT-CTXT&lt;/b&gt; (Integrity of Ciphertext/Tag) guarantee.&lt;/small&gt;&lt;br&gt;
&lt;small&gt;&lt;/small&gt;&lt;br&gt;
&lt;small&gt;What does this mean in plain English? It means that even if a hacker sits on the network and captures 10 million valid HTTP requests passing through your CDN (the "chosen messages"), along with their valid HMAC signatures, the mathematically chaotic avalanche effect of SHA-256 ensures they learn absolutely zero bits of information about your secret key. They cannot reverse-engineer the key, and they cannot forge a valid signature for a slightly modified URI or payload.&lt;/small&gt;&lt;br&gt;
&lt;small&gt;&lt;/small&gt;&lt;br&gt;
&lt;small&gt;Because symmetric math relies on &lt;b&gt;rapid bitwise operations&lt;/b&gt; (XOR, AND, bit-shifts) rather than the heavy prime factorization or elliptic curve scalar multiplication used in Asymmetric RSA/ECDSA, this EUF-CMA proof can be calculated by a V8 engine in less than a millisecond.&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By taking a highly secure shared 256-bit Symmetric Key and hashing the URI, critical Headers, and a tightly constrained timestamp header (e.g., &lt;code&gt;x-cloud-date&lt;/code&gt;), we generate an &lt;strong&gt;unforgeable signature&lt;/strong&gt;. You achieve enterprise-grade cryptographic integrity, but at a fraction of the computational cost.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Proposed Architecture: CloudFront Functions + KVS
&lt;/h3&gt;



&lt;p&gt;The main friction point in using KMS for Edge validation is &lt;strong&gt;state&lt;/strong&gt; and &lt;strong&gt;distance&lt;/strong&gt;. Making a regional API call from an Edge location natively introduces cross-ocean latency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslmsx9afr5m62fj1kvew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslmsx9afr5m62fj1kvew.png" alt="Architecture &amp;amp; Latency Comparison"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of heavy Lambda@Edge functions fetching regional secrets, we can execute this Zero-Trust pattern using &lt;strong&gt;CloudFront Functions (CFF)&lt;/strong&gt; and &lt;strong&gt;CloudFront KeyValueStore (KVS)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Compute&lt;/strong&gt;: We write the &lt;code&gt;HMAC-SHA256&lt;/code&gt; mathematical signing logic in vanilla JavaScript and execute it directly within the CFF V8 JavaScript Engine isolates.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Key Rotation&lt;/strong&gt;: We store the Symmetric Key in CloudFront KVS. KVS allows for instant, global key rotation via API, mimicking the operational security benefits of AWS KMS, but doing so natively at the Edge with &lt;strong&gt;sub-millisecond&lt;/strong&gt; read latency.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  So... Why isn't this architecture adopted by Kevin?
&lt;/h3&gt;



&lt;p&gt;I want to be &lt;strong&gt;completely transparent&lt;/strong&gt;: my symmetric CFF solution is not a silver bullet. Engineering is always about risk and balances.&lt;/p&gt;

&lt;p&gt;My architecture relies on a few critical assumptions and tradeoffs that security engineers must understand:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj8dwrhw52v3ffy743iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj8dwrhw52v3ffy743iy.png" alt="The CloudFront Execution Lifecycle"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h4&gt;
  
  
  1. The Bayes Theorem &amp;amp; Execution Order
&lt;/h4&gt;



&lt;p&gt;In AWS, CloudFront Functions run at the Viewer Request phase (before the WAF). Lambda@Edge can run at the Origin Request phase (after the WAF).&lt;/p&gt;

&lt;p&gt;We want to ensure: 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Has&amp;nbsp;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≈&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kevin's Architecture (Post-WAF)&lt;/strong&gt;: Because execution is sequential, if the signature exists, the WAF must have already passed. The probability 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Has&amp;nbsp;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 by definition.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My Architecture (Pre-WAF)&lt;/strong&gt;: Because CFF signs the request "blindly" before inspection, we must apply &lt;em&gt;Bayes' Theorem&lt;/em&gt;. &lt;/li&gt;
&lt;/ul&gt;




&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mtable"&gt;&lt;span class="col-align-r"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Has&amp;nbsp;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Has&amp;nbsp;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Has&amp;nbsp;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class="mord"&gt;∣&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;⋅&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Passed&amp;nbsp;WAF&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;




&lt;p&gt;To maintain Zero-Trust integrity, you must rigorously ensure your WAF processes every signed request.&lt;/p&gt;





&lt;h4&gt;
  
  
  2. Replay Attacks and Missing Payloads
&lt;/h4&gt;



&lt;p&gt;CloudFront Functions &lt;strong&gt;cannot read or hash the request body&lt;/strong&gt;. This means the payload is unprotected by the signature.&lt;/p&gt;

&lt;p&gt;To mitigate this and prevent &lt;em&gt;Replay Attacks&lt;/em&gt;, your &lt;code&gt;HMAC-SHA256&lt;/code&gt; signature must be strictly time-bound. A 30-to-60 second TTL on the timestamp header ensures that even if an attacker intercepts a signed header, the window to exploit it is practically closed.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Why Kevin's Architecture might be a Better Choice
&lt;/h4&gt;

&lt;p&gt;If your backend APIs involve &lt;em&gt;heavy asynchronous processing&lt;/em&gt; that takes 5 to 10 seconds to resolve, &lt;strong&gt;latency is not your primary concern&lt;/strong&gt;. In those scenarios, Kevin’s Asymmetric KMS approach is the better fit. The latency overhead won't be noticed, and asymmetric cryptography executed post-WAF provides stronger isolation without relying on WAF configuration hygiene.&lt;/p&gt;

&lt;p&gt;However, if your APIs are &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;highly latency-sensitive,&lt;/li&gt;
&lt;li&gt;read-heavy, or &lt;/li&gt;
&lt;li&gt;serving dynamic content,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the unit economics and speed of the CFF pattern are mathematically undeniable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Coming Up in Part 2: The Benchmarks
&lt;/h3&gt;



&lt;p&gt;Theory is great, but &lt;strong&gt;hard data is better&lt;/strong&gt;. In Part 2 of this series, I am going to open-source a custom-built, TypeScript benchmarking engine.&lt;/p&gt;

&lt;p&gt;Most CDN benchmarks are deeply flawed by &lt;em&gt;TCP Keep-Alive hoarding&lt;/em&gt; and &lt;em&gt;submarine cable jitter&lt;/em&gt;. We are going to parse standard deviations, isolate pure V8 engine compute overhead, and prove exactly how much latency these architectures add.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spoiler alert: We reduced the latency penalty from &lt;code&gt;~100ms&lt;/code&gt; down to &lt;code&gt;2.5ms&lt;/code&gt; and a substantial price drop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;




&lt;h3&gt;
  
  
  Acknowledgments &amp;amp; Notes
&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;A Special Thanks:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I want to extend a massive thank you to my employer, &lt;a href="https://hket.com" rel="noopener noreferrer"&gt;HKET&lt;/a&gt;, and my supervisor, &lt;a href="https://www.linkedin.com/in/jack-yeung-083b23236/" rel="noopener noreferrer"&gt;Jack Yeung&lt;/a&gt;. Fostering an engineering culture that actively supports open-source benchmarking, technical knowledge sharing, and public writing is rare. Their full backing and encouragement made this research and blog series possible. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Author's Note: The architectural concepts, cryptography math, code, and benchmarking data in this series are 100% my original work. I utilized an LLM strictly as an editorial assistant to help structure and refine the prose of this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>zerotrust</category>
      <category>cloudnative</category>
      <category>cryptography</category>
    </item>
  </channel>
</rss>
