A double encoding attack explained simply is an attack that encodes input twice to hide its real meaning from security checks. A filter may decode the value once and see harmless encoded text, while the application decodes it again and gets the original characters. This mismatch can affect paths, redirects, and access controls.
OWASP documents double encoding as a way to bypass filters that decode input only once. This guide explains how the attack works, shows a real Astro case, and covers practical defenses. Secure Coding Practices focuses on safer decoding and validation. Keep reading to see how these attacks work.
Double Encoding: The Core Essentials
Understanding double encoding comes down to knowing how security checks get bypassed when different layers of your application stack decode input at different times.
- The Core Vulnerability: Attackers encode malicious input (like ../) twice using patterns such as %25 so single-decode security filters see harmless text, while the backend decodes it a second time into dangerous actions.
- The Layered Mismatch: The true threat is an architectural mismatch, when WAFs, proxies, middleware, and application routers interpret request paths differently along the execution chain.
- The Core Defense: Applications must establish a single decoding boundary, canonicalize the input before validation and authorization, and reject unexpected residual multi-level encoding.
What Is a Double Encoding Attack?

A double encoding attack encodes already encoded input a second time. Different application layers can decode that input at different points, which may cause them to see different values.
Take ../, a sequence often linked with path traversal. In percent encoding, it can become %2e%2e%2f. Encode that value again, and each % becomes %25, giving %252e%252e%252f.
One security layer removes the outer wrapper and sees encoded data. Later, another component removes the next layer and gets the original characters.
| Stage | Value | Meaning |
| Original | ../ | Traversal characters |
| Encoded once | %2e%2e%2f | Percent-encoded form |
| Encoded twice | %252e%252e%252f | Encoded percent signs |
| After one decode | %2e%2e%2f | Still encoded |
| After two decodes | ../ | Original value |
A double encoding attack becomes dangerous when different parts of an application interpret the same input differently. A request may pass through a WAF, proxy, framework, router, and application, with each layer potentially parsing or decoding the value.
When we review request handling, three questions help expose this problem:
- What value did the client send?
- What does each processing layer produce?
- Which final value controls the security decision?
The encoding itself is not usually the flaw. The mismatch between decoding, normalization, and security decisions is.
Why Can a WAF Miss a Double-Encoded Request?
A WAF is more likely to miss a double-encoded request when it interprets the input differently from the backend. The issue is not that double encoding automatically defeats a WAF.
Several conditions can create a mismatch:
- The WAF decodes fewer times than the application.
- Middleware and the router use different parsing rules.
- Validation happens before final normalization.
- Authorization checks a non-canonical path.
- Another component decodes the value after inspection.
For example:
WAF → decode once → %2e%2e%2f
Backend → decode again → ../
Research from CISA demonstrates
“The web server receives the double-encoded string, decodes it, and then passes it to the application, which decodes it again. This can cause security controls (e.g., input validation, WAF) that only decode input once to miss attacks.” – CISA
At that point, the WAF and backend are no longer evaluating the same representation. This type of mismatch is a broader canonicalization security risk because the resource can receive different security treatment depending on how its representation is processed.
A safer request flow is:
Request → Parse → Canonicalize → Validate → Authorize → Use
Repeated decoding should be avoided unless the application has a clear reason for it.
What Does a Real Double-Encoding Vulnerability Look Like?
As noted by OWASP
“By using double encoding it’s possible to bypass security filters that only decode user input once. The second decoding process is executed by the backend platform or modules that properly handle encoded data, but don’t have the corresponding security checks in place.” – OWASP Foundation
A real Astro vulnerability shows why decoding differences can affect authentication. CVE-2025-66202 affected Astro versions below 5.15.8 and received a CVSS v3.1 base score of 6.5. The issue involved a double URL encoding bypass that could let an unauthenticated attacker bypass path-based authentication checks.
The reported example used a path such as /%2561dmin. After one decoding step, part of the value remained encoded. A later interpretation could turn it into /admin.
The middleware and the application didn’t work from the same path representation. The issue was linked to CWE-647, which covers using non-canonical URL paths for authorization decisions.
For developers, the lesson is broader than Astro. A protected route isn’t fully protected if another representation can reach the same resource without receiving the same authorization decision.
We should test more than the obvious route. Check encoded versions during security reviews, especially around middleware and access controls.
Which Attacks Can Double Encoding Help Hide?

Double encoding is an evasion technique. Its impact depends on what the decoded value becomes and how the application uses it.
| Attack type | What encoding can hide | Possible impact |
| Path traversal | Encoded ../ | Unauthorized file access |
| XSS | Encoded characters | Filter evasion |
| Authentication bypass | Protected paths | Access-control failure |
| Injection | Special characters | Validation mismatch |
| Redirect abuse | Encoded destinations | Unsafe navigation |
OWASP discusses double encoding in connection with path traversal, XSS, and other filter-bypass cases. Still, encoding doesn’t magically turn harmless input into an attack.
The application must eventually decode and use the value in a security-sensitive context. This is why preventing canonicalization attacks in web applications matters when different representations can reach the same resource.
A developer may block the literal ../, while an encoded version passes the first check. If another layer decodes it later, the application can receive the traversal sequence after validation has finished.
XSS and injection need more care. A double encoded payload matters only if the application later decodes it and places the result into a context where it can cause harm.
We’ve found that this distinction keeps security testing grounded. Don’t label every encoded string malicious. Trace what the application does with it.
How Should Developers Test for Decoding Mismatches?
Credits: StatQuest with Josh Starmer
Developers should test decoding behavior across the whole request chain in an authorized test environment. Checking whether a WAF blocks a request isn’t enough.
The useful part is seeing what each layer receives and produces.
A practical test can follow these steps:
- Record the original request.
- Identify every decoding point.
- Capture values after each boundary.
- Compare those values with security checks.
- Test protected paths in alternate forms.
- Review unexpected residual encoding.
A small test matrix can help:
| Test | Expected result |
| Normal input | Consistent interpretation |
| Single-encoded input | Same intended resource |
| Double-encoded input | Rejected or normalized |
| Residual %XX | Investigated or rejected |
| Alternate protected path | Same authorization result |
The useful signal is a mismatch. If a gateway logs one path but the application routes another, the team has found something worth reviewing.
We should also examine query parameters, cookies, headers, and redirect values where encoded data is accepted. A double encoded redirect URI can create problems if one component validates the encoded value while another consumes the decoded destination.
But don’t reject every %XX sequence. Some applications legitimately use encoded data.
Test the real requirements first. Then define what should be accepted, normalized, or rejected.
How Can You Prevent Double Encoding Attacks?

The strongest defense is to establish one clear canonical representation before validation and authorization. Understanding input canonicalization helps clarify why the same resource should receive consistent treatment even when it arrives in different representations.
Our secure development approach follows a defined sequence:
- Parse the request.
- Decode at one boundary.
- Canonicalize the value.
- Validate the result.
- Authorize that same value.
- Pass it forward without unexpected decoding.
This matters because a security check is only useful if it examines the value that later controls the action.
Canonicalization should be part of the prevention process because the same resource can sometimes be represented by different strings. If security checks examine one representation while the application later uses another, authorization can become inconsistent.
Many double encoding problems come from small assumptions about where input has already been decoded. A proxy may decode once, middleware may process it again, and the router may normalize the path separately. The application can then receive a value that earlier security checks never inspected.
Common mistakes include:
- Filtering before canonicalization.
- Decoding untrusted input in several places.
- Comparing raw paths with canonical paths.
- Relying on a WAF as the only access-control layer.
- Blocking known payloads instead of validating the expected value.
- Re-decoding input simply because it appears encoded.
We’ve seen how scattered decoding can make security reviews much harder. A developer may fix one validation rule without realizing another component changes the value later.
A better approach is to centralize request parsing and establish a clear decoding boundary. Once the canonical value exists, validation and authorization should use that same value.
Authorization deserves particular attention. Checking /admin is not enough if another representation can resolve to the same protected resource. The application should authorize the canonical resource identity, not whichever string happened to arrive first.
Regex filters can still provide useful defense in depth, but they should support canonicalization rather than replace it.
Applications can also reject unnecessary multi-level encoding. If the system has no legitimate reason to accept repeatedly encoded paths, residual encoding after the defined decoding stage can be treated as invalid.
Allowlists can provide another layer of protection. Instead of trying to block every suspicious representation, define which canonical routes or values the application actually expects.
A WAF can still help with detection, logging, and defense in depth. It should not carry the full responsibility for authorization.
FAQ
How can you tell whether an input contains a double encoded payload?
Look for input that remains encoded after the application processes it once. The %25 encoding pattern can indicate another encoding layer because % may become %25. However, this pattern does not confirm an attack by itself.
Compare the original request with the value the application uses. This can reveal an encoding mismatch or unexpected decoding.
What makes a double encoded query string different from normal encoded input?
A double encoded query string has an extra encoding layer that the application may not expect. Normal percent encoding is often required for valid web requests. Repeated encoding becomes risky when different components process it differently.
Check whether the application has clear decoding rules. Unexpected application layer decoding can change the input and create an encoding discrepancy.
How can developers find a recursive decoding vulnerability during testing?
Developers can send controlled test values through each request-processing stage and record the results. Compare what reaches the proxy, gateway, middleware, and application.
A recursive decoding vulnerability may exist when input is decoded repeatedly without a defined boundary. Pay close attention to backend decoding behavior when encoded characters become meaningful after another decoding step.
What should a security team test for double encoded parameters?
A security team should test double encoded parameters wherever user input affects routes, resources, or security decisions. Test paths, query strings, redirects, forms, and headers with normal and encoded values.
Compare how each layer handles them. Look for input validation failure, unexpected gateway normalization, or different results between intermediary systems and the backend. Document every transformation clearly.
Why can encoding normalization cause unexpected security problems?
Encoding normalization can cause security problems when different components transform the same input differently. One layer may preserve an encoded character, while another converts it before processing.
This difference can create an encoding discrepancy and inconsistent security decisions. Developers should define accepted input formats and apply the same parsing, normalization, validation, and resource-handling rules across the entire application.
Build Consistent Request Processing
Double encoding shows why secure applications must agree on what input means at every security-sensitive boundary. Define where decoding happens, canonicalize the result, then validate and authorize that same value. That’s the core defense. Don’t rely on another filter alone.
Help your developers build this habit through practical exercises with the Secure Coding Practices Bootcamp, designed to turn secure development principles into skills they can apply in real projects.
References
- https://www.cisa.gov/sites/default/files/publications/defending_against_software_supply_chain_attacks_508.pdf
- https://owasp.org/www-community/Double_Encoding

