Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Authenticated Encryption (AEAD) Explained

By Kokil Thapa | Last reviewed: September 2026

Plain encryption hides data, but it does not prove the ciphertext is intact. An attacker can flip bits, replay old tokens, or swap blocks without the receiver noticing. Authenticated Encryption (AEAD) Explained in practical terms means one cryptographic operation that both encrypts plaintext and produces an authentication tag you must verify before decryption. If you build REST APIs with sensitive payloads, client portals, or payment flows, AEAD is the default pattern you should reach for instead of rolling your own MAC-plus-cipher stack.

What is Authenticated Encryption (AEAD) and why does it matter?

AEAD is a symmetric cipher mode that outputs ciphertext plus a fixed-length authentication tag. Decryption verifies the tag first. A mismatch means you discard the message and never release plaintext. That single gate closes entire classes of attacks that classic CBC or ECB modes leave open.

Historically, teams stitched together AES-CBC plus HMAC-SHA256 in an encrypt-then-MAC arrangement. That works when implemented perfectly. In practice, ordering mistakes, key reuse, and timing leaks show up in code reviews and incident reports. AEAD modes bake the contract into the algorithm: one key (or derived subkeys), one nonce, one API surface.

On legal-tech portals and document-sharing systems I have shipped, AEAD protects session cookies, API tokens, and uploaded file metadata at the application layer. The same idea applies whether you store secrets in Redis or pass encrypted IDs in URLs. Integrity is not optional when a forged token could expose someone else's divorce filing or notary appointment.

AEAD Encrypt and Verify FlowPlaintextSensitive dataNonceUse once per keyAADHeaders, contextAEAD CipherGCM or ChaCha20CiphertextAuth TagDecrypt verifies tag firstBad tag equals reject, no plaintext
Authenticated Encryption (AEAD) encrypts plaintext and binds ciphertext to optional associated data and a unique nonce.

The term AEAD appears in RFC 5116, which defines authenticated-encryption modes for IPsec and general use. Modern libraries expose AEAD through a small set of well-tested primitives. Your job is to call them correctly, not to invent a new mode.

Confidentiality plus integrity in one API

Confidentiality stops passive eavesdroppers from reading content. Integrity stops active attackers from modifying ciphertext without detection. AEAD delivers both. Associated Authenticated Data (AAD) lets you authenticate context—such as a user ID or API version—without encrypting it. The tag covers AAD plus ciphertext, so tampering with either fails verification.

How AEAD differs from older patterns

Compare AEAD to three legacy approaches:

  • Encrypt-only (AES-CBC, AES-ECB): No integrity. Padding-oracle and bit-flipping attacks are real.
  • MAC-only (HMAC over plaintext): No confidentiality. Fine for signed webhooks, not for secret payloads.
  • Encrypt-then-MAC: Secure when done right, but two keys, two code paths, and more review surface.

For greenfield work in 2026, default to AEAD. Reach for hybrid encryption when you need asymmetric key exchange, then use AEAD for the bulk data key.

Which AEAD algorithms should developers use in 2026?

Two algorithms dominate production stacks today: AES-GCM and ChaCha20-Poly1305. Both are AEAD. Both require a unique nonce per encryption under the same key. Pick based on hardware and library support, not brand preference.

AlgorithmKey sizeNonce sizeBest onNotes
AES-256-GCM256 bits96 bits (12 bytes)CPUs with AES-NIStandard in TLS 1.3, AWS KMS, many databases
ChaCha20-Poly1305256 bits96 bits (12 bytes)Mobile, older x86, embeddedConstant-time friendly; default in libsodium
AES-128-GCM128 bits96 bitsSame as AES-GCMAcceptable; prefer 256 for long-lived app keys

AES-GCM security depends on never reusing a (key, nonce) pair. Duplicate nonces can leak authentication keys and plaintext XOR relationships. NIST SP 800-38D documents GCM constraints that every implementer should read once.

Choosing an AEAD AlgorithmNew encryption taskServer has AES-NI?YesNoAES-256-GCMFast on modern CPUChaCha20-Poly1305Portable, constant-timeNever reuse nonce under same key
Algorithm selection for Authenticated Encryption (AEAD): AES-GCM on AES-NI hardware, ChaCha20-Poly1305 elsewhere.

PHP 8.3 and later ship sodium by default. That extension uses ChaCha20-Poly1305 for sodium_crypto_secretbox. OpenSSL exposes AES-GCM through openssl_encrypt with mode aes-256-gcm. Laravel's built-in Crypt facade uses AES-256-CBC with HMAC—a sound encrypt-then-MAC design, but not AEAD. For new custom crypto inside Laravel 12 or 13 apps, prefer sodium or explicit GCM calls over ad-hoc OpenSSL modes.

How does AES-GCM work under the hood?

GCM combines CTR-mode encryption with Galois-field multiplication for authentication. CTR turns AES into a stream cipher: you XOR a keystream with plaintext. The auth tag is a polynomial hash over ciphertext and AAD. Verification recomputes the tag and compares in constant time inside the library.

You do not implement GCM math yourself. You supply key, nonce, plaintext, and optional AAD. The library returns ciphertext concatenated with tag (or separate outputs, depending on API). Document your wire format so the next developer knows byte layout.

Nonce rules that actually matter

A 96-bit nonce is standard for GCM. Common strategies:

  1. Random 96-bit nonce: Safe if you use a CSPRNG and keep message volume far below birthday bounds.
  2. Counter nonce: Store a monotonic counter in the database or Redis. Predictable but easy to audit.
  3. Random key per message (envelope): Generate a fresh data key, encrypt with a master key, embed key ID in payload. See envelope encryption patterns for KMS-style designs.

Never derive the nonce from a timestamp alone. Clock skew and replay collide. Never use a fixed nonce "because it is easier." That breaks GCM catastrophically.

Wire format you should standardise

A practical on-disk or on-the-wire blob often looks like this:

version (1 byte) | key_id (4 bytes) | nonce (12 bytes) | ciphertext | tag (16 bytes)

Store the version byte so you can rotate algorithms later. Include key_id when multiple keys exist during rotation. Encode the whole structure with Base64 only for transport, not as a security layer. Our Base64 encoder and decoder helps debug payloads during integration tests.

How do you implement AEAD safely in PHP and Laravel?

Below are copy-paste starting points for PHP 8.3+ with libsodium and OpenSSL AES-GCM. Run them on PHP 8.5 or any supported 8.3+ build in production. Test on staging before you touch live client data.

ChaCha20-Poly1305 with libsodium

<?php
declare(strict_types=1);

function aead_encrypt(string $plaintext, string $key, string $aad = ''): string
{
    if (strlen($key) !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new InvalidArgumentException('Key must be 32 bytes.');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    $ciphertext = sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
        $plaintext,
        $aad,
        $nonce,
        $key
    );
    return $nonce . $ciphertext;
}

function aead_decrypt(string $blob, string $key, string $aad = ''): string
{
    $nonceLen = SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
    $nonce = substr($blob, 0, $nonceLen);
    $ciphertext = substr($blob, $nonceLen);
    $plaintext = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
        $ciphertext,
        $aad,
        $nonce,
        $key
    );
    if ($plaintext === false) {
        throw new RuntimeException('AEAD verification failed.');
    }
    return $plaintext;
}

Bind AAD to tenant ID or document type on multi-tenant portals. A ciphertext moved from one tenant context to another must fail decrypt. I use that pattern on production Laravel applications that handle per-client legal documents.

AES-256-GCM with OpenSSL

<?php
declare(strict_types=1);

function aes_gcm_encrypt(string $plaintext, string $key, string $aad = ''): string
{
    $nonce = random_bytes(12);
    $tag = '';
    $ciphertext = openssl_encrypt(
        $plaintext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $nonce,
        $tag,
        $aad,
        16
    );
    if ($ciphertext === false) {
        throw new RuntimeException('Encryption failed.');
    }
    return $nonce . $tag . $ciphertext;
}

function aes_gcm_decrypt(string $blob, string $key, string $aad = ''): string
{
    $nonce = substr($blob, 0, 12);
    $tag = substr($blob, 12, 16);
    $ciphertext = substr($blob, 28);
    $plaintext = openssl_decrypt(
        $ciphertext,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $nonce,
        $tag,
        $aad
    );
    if ($plaintext === false) {
        throw new RuntimeException('AEAD verification failed.');
    }
    return $plaintext;
}

Wrap these helpers in a service class. Inject keys from environment variables or a secrets manager. Never hard-code keys in Git. For Ansible-managed servers, compare with encrypting secrets in Ansible Vault at the infrastructure layer while AEAD protects data inside the app.

AEAD in a Laravel Request PathHTTP ClientMiddlewareRate limit checkControllerBusiness logicAEAD ServiceEncrypt on writeMySQL 9.7Ciphertext at restRedis 8.10Session cacheTLS protects data in transit; AEAD protects stored fields
Application-layer Authenticated Encryption (AEAD) sits below controllers and above encrypted database columns.

Laravel integration checklist

When wiring AEAD into Laravel 12 or 13:

  • Register an EncryptionService singleton with keys loaded from config/services.php.
  • Encrypt PII columns in model mutators or use a custom cast class.
  • Log verification failures without logging ciphertext or keys.
  • Add PHPUnit tests that flip one tag byte and assert decrypt throws.
  • Plan key rotation: decrypt with old key, re-encrypt with new key in a queued job.

Pair this with database encryption at rest and in transit. TLS covers the wire. Disk encryption covers stolen drives. AEAD covers logical access when someone dumps a table.

What are the most common AEAD mistakes in production?

Most AEAD failures are operational, not mathematical. The algorithm is fine; the integration is not.

Nonce reuse and key management errors

Reusing a nonce under the same AES-GCM key leaks the auth subkey and XOR relationships between messages. Rotating keys without updating key_id in stored blobs causes decrypt failures at 2 a.m. Storing the master key next to ciphertext in the same database defeats the purpose. Use environment secrets, Ansible Vault for deployment secrets, or a KMS.

Skipping verification or leaking failure details

Never catch AEAD failures and continue with empty strings. Never branch on "which byte of the tag failed." Return a generic 401 or 400 to the client. Log internally with a correlation ID. Detailed crypto errors help attackers refine forgeries.

Confusing encoding with encryption

Base64 is not encryption. JSON wrapping is not integrity. Hashing is not reversible encryption. Use the password generator for user passwords, then password_hash with Argon2id. Use AEAD for reversible secrets like API tokens you must read back.

Insecure vs AEAD OutcomesBefore: Encrypt-only CBCBit flip attackReplay old tokenSilent corruptionApp accepts bad dataAfter: AEAD with verifyTamper ciphertextSwap AAD contextTag verify failReject, no plaintext leakLegal-tech portals: forged doc IDs must fail closedSee portfolio examples with client document workflows
Authenticated Encryption (AEAD) blocks tampering that encrypt-only modes accept silently.

On portals like Mijar Law Associates and Notary Nepal, a forged document reference must fail closed. AEAD on opaque identifiers makes guessing and bit-flipping impractical when combined with proper authorisation checks.

Performance myths

AEAD adds overhead, but it is small next to database and network latency. AES-GCM on AES-NI hardware often encrypts faster than your ORM loads relations. Profile before you disable integrity. If you process bulk exports, stream chunks and encrypt in jobs on a queue worker.

For API designs that expose encrypted IDs publicly, also read API rate limiting and abuse prevention. Crypto slows attackers; rate limits stop brute force at the edge.

How does AEAD fit with asymmetric and envelope encryption?

AEAD is symmetric. You still need a safe way to share or wrap keys. Typical stack:

  1. Generate a random 256-bit data key per file or per session.
  2. Encrypt the payload with AEAD under that data key.
  3. Wrap the data key with RSA-OAEP or another KEM using the recipient public key.
  4. Ship wrapped key plus AEAD blob together.

That is envelope encryption. AWS KMS and similar services automate wrapping. Read symmetric vs asymmetric encryption for the full split of responsibilities. AEAD handles bulk data; asymmetric crypto handles key establishment.

At rest, PostgreSQL 18 and MySQL 9.7 offer transparent encryption options, but application-layer AEAD still helps when you must encrypt single columns or pass tokens to third parties who should not read raw SQL backups.

Key Takeaways

  • Use AEAD (AES-GCM or ChaCha20-Poly1305) instead of encrypt-only modes or hand-rolled MAC composition.
  • Generate a unique 96-bit nonce for every encryption under the same key; document your blob format and rotation plan.
  • Verify the authentication tag before parsing JSON or loading ORM models; fail closed with generic errors.
  • Bind associated data to tenant, user, or resource context so ciphertext cannot be cut-and-pasted across scopes.
  • Combine TLS in transit, disk encryption at rest, and AEAD at the field layer for defense in depth.
  • Test tamper cases in CI: one flipped tag byte must always throw, never return partial plaintext.

People Also Ask

Is AES-GCM the same as AEAD?

AES-GCM is one AEAD mode, not the whole category. AEAD is the property: encryption plus authentication in one operation. GCM is the most common AES-based AEAD mode in TLS, cloud KMS APIs, and OpenSSL. ChaCha20-Poly1305 is equally AEAD and often preferred on systems without hardware AES acceleration.

What happens if an AEAD tag fails verification?

The decrypt function must return failure and release no plaintext. Your application should treat that like any other auth failure: stop processing, return a safe error, and log internally. Do not retry with stripped tags or fall back to unauthenticated parsing.

Can I use Laravel Crypt instead of AEAD?

Laravel's default Crypt facade uses AES-256-CBC with a valid HMAC—encrypt-then-MAC, not AEAD, but sound for framework-managed payloads like signed cookies. For custom token formats, cross-service compatibility, or explicit GCM requirements, call sodium or OpenSSL GCM directly inside a dedicated service class.

Do I still need HTTPS if I use AEAD?

Yes. AEAD protects stored fields and application-level blobs. TLS protects data on the network, including keys and nonces in transit. They solve different layers. Use both, plus access control and audit logging, on any system handling payments or personal data.

Build encryption you can maintain after launch

Authenticated Encryption (AEAD) Explained boils down to a simple rule: one well-vetted algorithm, unique nonces, verified tags, and clear key rotation. Skip custom cipher modes. Pick AES-GCM or ChaCha20-Poly1305, write tests that tamper with tags, and document the byte layout your future self will need at 2 a.m.

If you are adding field-level encryption to a Laravel portal, hardening API tokens, or reviewing crypto before a compliance audit, treat AEAD as the default—not an optional extra. Need help designing or reviewing encryption for a production app? Contact us or explore custom software development and ongoing support and maintenance. For related reading, see the blog encryption series and secure legal-tech implementations in our portfolio.

Frequently Asked Questions

AEAD is symmetric encryption that outputs ciphertext plus a fixed-length authentication tag. Decryption verifies the tag first and releases no plaintext if verification fails.

No. AES-GCM is one AEAD mode. AEAD is the property of combining confidentiality and integrity in one operation. ChaCha20-Poly1305 is equally AEAD.

Two algorithms dominate production: AES-256-GCM and ChaCha20-Poly1305. Both require a unique 96-bit nonce per message under the same key. Pick AES-GCM on CPUs with AES-NI; use ChaCha20-Poly1305 on mobile, older x86, or embedded hardware without hardware AES. PHP 8.3 and later ship libsodium by default for ChaCha20-Poly1305. OpenSSL exposes AES-GCM through openssl_encrypt with mode aes-256-gcm. For greenfield work, default to one of these rather than encrypt-only CBC or hand-rolled MAC stacks.

Decrypt must fail and release no plaintext. Treat it like any auth failure: stop processing, return a generic error, log internally.

Laravel's default Crypt facade uses AES-256-CBC with HMAC, which is encrypt-then-MAC rather than AEAD, but it is sound for framework-managed payloads like signed cookies. For custom token formats, cross-service compatibility, or explicit GCM requirements in Laravel 12 or 13 apps, call sodium or OpenSSL GCM directly inside a dedicated service class instead of ad-hoc OpenSSL modes.

Yes. AEAD protects stored fields and application-level blobs at the logical layer. TLS protects data on the network, including keys and nonces in transit. They solve different problems. Use both, plus access controls and authorisation checks on top. A forged document reference on a client portal must fail closed at decrypt, but TLS still stops passive eavesdropping and many active wire attacks before data ever reaches your application code.

AAD is context you authenticate but do not encrypt, such as a tenant ID, user ID, API version, or document type. The authentication tag covers both AAD and ciphertext, so tampering with either causes verification to fail. On multi-tenant legal-tech portals I have shipped, binding AAD to tenant or resource context stops ciphertext cut-and-paste across scopes. A blob decrypted with the wrong AAD must fail even if the key is valid, which closes a common lateral-movement mistake in shared-database applications.

Reusing a nonce under the same AES-GCM key is catastrophic. It can leak the authentication subkey and XOR relationships between messages, undermining both integrity and confidentiality. NIST SP 800-38D documents GCM constraints every implementer should read. Safe nonce strategies include a random 96-bit value from a CSPRNG when message volume stays well below birthday bounds, or a monotonic counter stored in a database or Redis. Never use a fixed nonce for convenience, and never derive a nonce from a timestamp alone because clock skew and replay collisions break uniqueness guarantees.

Encrypt-then-MAC, such as AES-CBC plus HMAC-SHA256, is secure when implemented perfectly, but it needs two keys, two code paths, and more review surface where ordering mistakes and timing leaks appear in real codebases. AEAD bakes confidentiality and integrity into one API with one key or derived subkeys, one nonce, and one library primitive. Encrypt-only modes like AES-CBC or AES-ECB provide no integrity and remain vulnerable to padding-oracle and bit-flipping attacks. MAC-only HMAC over plaintext gives integrity without confidentiality, fine for signed webhooks but not secret payloads.

On PHP 8.3 or later, including PHP 8.5 in production, use libsodium for ChaCha20-Poly1305 via sodium_crypto_aead_chacha20poly1305_ietf_encrypt and decrypt, or OpenSSL for AES-256-GCM via openssl_encrypt and openssl_decrypt with a 12-byte nonce and 16-byte tag. Wrap helpers in a service class, load keys from environment variables or a secrets manager, never hard-code keys in Git, and test on staging first. In Laravel 12 or 13, register an EncryptionService singleton, encrypt PII in model mutators or custom casts, log verification failures without logging ciphertext or keys, and add PHPUnit tests that flip one tag byte and assert decrypt throws.

Most failures are operational, not mathematical. Top issues include nonce reuse under the same key, storing the master key next to ciphertext in the same database, rotating keys without updating key_id in stored blobs, catching verification failures and continuing with empty strings, and returning detailed crypto errors that help attackers refine forgeries. Teams also confuse Base64 or JSON wrapping with integrity, or use password hashing where reversible AEAD is needed for API tokens. Return generic 401 or 400 responses on failure, log internally with a correlation ID, and use Ansible Vault or a KMS for deployment secrets separate from encrypted application data.

AEAD is symmetric, so you still need a safe way to share or wrap keys. Envelope encryption generates a random 256-bit data key per file or session, encrypts the payload with AEAD under that data key, then wraps the data key with RSA-OAEP or another key encapsulation method using the recipient public key. You ship the wrapped key and AEAD blob together. AWS KMS and similar services automate wrapping. AEAD handles bulk data encryption; asymmetric crypto handles key establishment. This pattern suits document-sharing systems where each upload or session gets its own data key while a master key stays in a secrets manager.

A practical on-disk or on-the-wire structure often looks like: version byte, key_id four bytes, nonce twelve bytes, ciphertext, then tag sixteen bytes. Store the version byte so you can rotate algorithms later. Include key_id when multiple keys exist during rotation. Document byte layout clearly so the next developer knows whether nonce precedes tag or ciphertext. Encode the whole structure with Base64 only for transport, not as a security layer. Prepend a random nonce per encryption and never treat encoding as a substitute for the authentication tag that proves integrity before any JSON parsing or ORM loading occurs.

AEAD adds overhead, but it is usually small compared to database and network latency. AES-GCM on AES-NI hardware often encrypts faster than an ORM loads relations, so profile before disabling integrity for perceived speed gains. If you process bulk exports, stream chunks and encrypt in queue workers rather than skipping authentication. For API designs that expose encrypted IDs publicly, pair AEAD with rate limiting at the edge. Crypto slows attackers who tamper with ciphertext; rate limits stop brute force against opaque identifiers. Do not treat performance myths as a reason to drop the authentication tag.

Reach for AEAD whenever you encrypt sensitive payloads that active attackers might modify, including session cookies, API tokens, uploaded file metadata, encrypted IDs in URLs, and PII columns. On legal-tech portals and document-sharing systems, integrity is not optional because a forged token could expose someone else's filing or appointment data. AEAD sits below controllers and above encrypted database columns as application-layer protection. Combine it with TLS in transit, disk encryption at rest, proper authorisation checks, and fail-closed decrypt behaviour. Use password_hash with Argon2id for user passwords, and reserve AEAD for reversible secrets you must read back.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: