{"id":1648,"date":"2025-04-30T11:03:55","date_gmt":"2025-04-30T09:03:55","guid":{"rendered":"http:\/\/v32019.vaadata.com\/blog?p=1648"},"modified":"2025-04-30T11:04:50","modified_gmt":"2025-04-30T09:04:50","slug":"jwt-json-web-token-vulnerabilities-common-attacks-and-security-best-practices","status":"publish","type":"post","link":"https:\/\/www.vaadata.com\/blog\/jwt-json-web-token-vulnerabilities-common-attacks-and-security-best-practices\/","title":{"rendered":"JWT (JSON Web Token): Vulnerabilities, Common Attacks and Security Best Practices"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"800\" height=\"450\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-json-web-token.png\" alt=\"JWT (JSON Web Token): Vulnerabilities, Common Attacks and Security Best Practices\" class=\"wp-image-13333\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-json-web-token.png 800w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-json-web-token-300x169.png 300w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>When developing a web application, authentication is an essential component. Depending on the technologies used, authentication can be stateful or stateless.<\/p>\n\n\n\n<p>In a stateful model, authentication is based on server-side session management. When a user connects, the server generates a unique session identifier. This identifier is then sent with each request so that the server can retrieve information relating to the user. It is therefore the server that maintains the state of the session throughout the browsing session.<\/p>\n\n\n\n<p>Conversely, in a stateless approach, it is the client &#8211; often the browser &#8211; that stores the session information, generally in the form of a signed or encrypted token. Each time a request is made, this token is transmitted to the server, which can then check it and extract the necessary data. The server keeps no trace of the session, hence the term stateless. This authentication method is used in particular with JWT tokens (JSON Web Tokens).<\/p>\n\n\n\n<p>Increasingly used, JWTs are not, however, free of vulnerabilities, and poor implementation can expose applications to potentially disastrous attacks.<\/p>\n\n\n\n<p>In this article, we&#8217;ll take a closer look at how JWTs work. We will also review the common vulnerabilities associated with JWTs, attack techniques and best practices for configuration and implementation to minimise these risks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Comprehensive Guide to JSON Web Tokens (JWT)<\/h2>\n\n\n<div class=\"wp-block-aioseo-table-of-contents\"><ul><li><a class=\"aioseo-toc-item\" href=\"#how-does-a-jwt-work\">How Does a JWT Work?<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#what-is-jwt\">What is JWT?<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#jwt-structure-and-generation\">JWT structure and generation<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#jose-header\">JOSE header<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#payload\">Payload<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#signature\">Signature<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#the-crucial-role-of-the-signature-in-jwt-security\">The crucial role of the signature in JWT security<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#signing-algorithms-symmetric-hs256-vs-asymmetric-rs256\">Signing algorithms: symmetric (HS256) vs. asymmetric (RS256)<\/a><\/li><\/ul><\/li><\/ul><\/li><\/ul><\/li><li><a class=\"aioseo-toc-item\" href=\"#pros-and-cons-of-jwts\">Pros And Cons of JWTs<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#what-are-the-most-common-jwt-vulnerabilities-and-exploitations\">What are the Most Common JWT Vulnerabilities and Exploitations?<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#jwt-signature-not-verified\">JWT signature not verified<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#exploiting-the-none-algorithm\">Exploiting the &quot;none&quot; algorithm<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#understanding-the-risks-associated-with-the-none-algorithm\">Understanding the risks associated with the \u2018none\u2019 algorithm<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#how-to-protect-yourself-ban-none-or-explicitly-authorise-the-right-algorithms\">How to protect yourself: ban &quot;none&quot; or explicitly authorise the right algorithms<\/a><\/li><\/ul><\/li><li><a class=\"aioseo-toc-item\" href=\"#weak-secrets-and-brute-force-attacks\">Weak secrets and brute force attacks<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#injection-of-specific-parameters-into-the-jose-header-of-the-jwt\">Injection of specific parameters into the JOSE header of the JWT<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#jwk\">jwk<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#jku\">jku<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#jku-parameter-and-jwk-sets\">jku parameter and JWK Sets<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#exploiting-the-jku\">Exploiting the jku<\/a><\/li><\/ul><\/li><li><a class=\"aioseo-toc-item\" href=\"#kid\">kid<\/a><\/li><\/ul><\/li><li><a class=\"aioseo-toc-item\" href=\"#algorithm-confusion\">Algorithm Confusion<\/a><ul><li><a class=\"aioseo-toc-item\" href=\"#how-the-algorithm-confusion-attack-works\">How the Algorithm Confusion attack works<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#exploiting-the-vulnerability\">Exploiting the vulnerability<\/a><\/li><\/ul><\/li><\/ul><\/li><li><a class=\"aioseo-toc-item\" href=\"#how-to-secure-the-implementation-of-jwt\">How to Secure the Implementation of JWT?<\/a><\/li><li><a class=\"aioseo-toc-item\" href=\"#conclusion\">Conclusion<\/a><\/li><\/ul><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"how-does-a-jwt-work\">How Does a JWT Work?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-95b7c6c395e8adbf7f411be0170d626a\" id=\"what-is-jwt\" style=\"color:#c0b800\">What is JWT?<\/h3>\n\n\n\n<p>As specified in <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519#section-6.1\" target=\"_blank\" rel=\"noopener\" title=\"\">RFC 7519<\/a>, a JSON Web Token (JWT) is a compact format for representing information, known as \u2018claims\u2019, exchanged between two parties. These claims are encapsulated in a JSON object which constitutes the content (or payload) of a signing (JWS &#8211; JSON Web Signature) or encrypted (JWE &#8211; JSON Web Encryption) structure.<\/p>\n\n\n\n<p>In other words, JWT is a standard that can be implemented in two ways: either via a JWS, or via a JWE.<\/p>\n\n\n\n<p>With JWS, the data is not encrypted but digitally signed. This guarantees its integrity: the content remains readable, but only a holder of the secret can generate a valid signing. This is the most commonly used form of JWT.<\/p>\n\n\n\n<p>Conversely, a JWE encrypts its content entirely. The information it contains is only accessible to entities with the decryption key.<\/p>\n\n\n\n<p>Since JWS is by far the most widespread implementation, the rest of this article will be based on this format. For the sake of simplicity, we will use the term JWT even when it is actually a JWS.<\/p>\n\n\n\n<p>JWTs are generated by the server when a user authenticates to a web application. Once issued, these tokens are sent to the client, which automatically attaches them to each subsequent HTTP request. This enables the server to identify the user at each interaction, without requiring any further authentication.<\/p>\n\n\n\n<p>The token contains information encoded and signed using a private key held by the server. Each time a JWT is received, the server can check its authenticity by recalculating the signing using its key and comparing it with the signature in the token. If the two signatures match, the JWT is considered valid.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"533\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2022\/04\/JWT-Tokens-working-principles-1024x533.png\" alt=\"How a JWT works\" class=\"wp-image-4533\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2022\/04\/JWT-Tokens-working-principles-1024x533.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2022\/04\/JWT-Tokens-working-principles-300x156.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2022\/04\/JWT-Tokens-working-principles.png 1156w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">How a JWT works<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-8f29df6fe72bc539bb66d10e37e16b6f\" id=\"jwt-structure-and-generation\" style=\"color:#c0b800\">JWT structure and generation<\/h3>\n\n\n\n<p>Let&#8217;s consider the following JWT:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"116\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-example-1024x116.png\" alt=\"\" class=\"wp-image-13335\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-example-1024x116.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-example-300x34.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-example-1536x174.png 1536w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-example.png 1582w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNBZG1pbiI6ZmFsc2V9.EypViEDiJhjeuXgjtGdibxrFPFZyYKn-KqFeAw3c2No<\/code><\/pre>\n\n\n\n<p>This type of JWT is made up of three distinct parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-vivid-red-color has-text-color has-link-color wp-elements-e9242bdf97cecb71ef6bd98d153f3493\">The JOSE (JSON Object Signing and Encryption) header: <code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9<\/code><\/li>\n\n\n\n<li class=\"has-vivid-purple-color has-text-color has-link-color wp-elements-f6d7eb0979c187d08bf6dcd42722953e\">The payload: <code>eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaXNBZG1pbiI6ZmFsc2V9<\/code><\/li>\n\n\n\n<li class=\"has-pale-cyan-blue-color has-text-color has-link-color wp-elements-97858e16b16fed725b84fdf63472ebd9\">The signature: <code>EypViEDiJhjeuXgjtGdibxrFPFZyYKn-KqFeAw3c2No<\/code><\/li>\n<\/ul>\n\n\n\n<p>Each part is separated by a \u2018.\u2019 with the JOSE header and payload encoded in base64url. The signature, meanwhile, is generated based on a secret or a pair of cryptographic keys.<\/p>\n\n\n\n<p>Let&#8217;s decode this JWT with <a href=\"https:\/\/gchq.github.io\/CyberChef\/\" target=\"_blank\" rel=\"noopener\" title=\"\">CyberChef<\/a>: <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"378\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/decoding-jwt-1024x378.png\" alt=\"\" class=\"wp-image-13337\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/decoding-jwt-1024x378.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/decoding-jwt-300x111.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/decoding-jwt.png 1528w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"jose-header\">JOSE header<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n   \"alg\": \"HS256\",\n   \"typ\": \"JWT\"\n}<\/code><\/pre>\n\n\n\n<p>The JOSE header of a JWT contains two key parameters: <code>alg<\/code> and <code>typ<\/code>. The alg parameter indicates the signing algorithm used, while <code>typ<\/code> specifies the type of token. Only <code>alg<\/code> is mandatory, but other optional parameters can also be added as required. Some of these will be discussed later in this article.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"payload\">Payload<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n   \"sub\": \"1234567890\",\n   \"name\": \"John Doe\",\n   \"isAdmin\": false\n}<\/code><\/pre>\n\n\n\n<p>The payload of a JWT contains what are known as claims, i.e. the information that the token carries. In our example, the claims are <code>sub<\/code>, <code>name<\/code> and <code>isAdmin<\/code>.<\/p>\n\n\n\n<p>There are three types of claim names:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Claims registered by RFC 7519 (such as <code>sub<\/code>)<\/li>\n\n\n\n<li>Public claims, which are standardised and listed in the IANA \u2018JSON Web Token Claims\u2019 registry to avoid name collisions.<\/li>\n\n\n\n<li>Finally, private claims, freely defined by developers according to the needs of the application<\/li>\n<\/ul>\n\n\n\n<p>In our case :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>sub<\/code> (for Subject) is a standard claim in RFC 7519. It designates the main entity to which the JWT refers (often the user ID).<\/li>\n\n\n\n<li><code>name<\/code> is a public claim, used here to indicate the user&#8217;s name.<\/li>\n\n\n\n<li><code>isAdmin<\/code> is a private claim, added by the developer to indicate whether or not the user has administrator rights on the platform.<\/li>\n<\/ul>\n\n\n\n<p>There are many other claims defined in the RFC, but none are strictly mandatory. To find out more, please consult the dedicated section of RFC <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519#section-4.1\" target=\"_blank\" rel=\"noopener\" title=\"\">7519<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"signature\">Signature<\/h4>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"the-crucial-role-of-the-signature-in-jwt-security\">The crucial role of the signature in JWT security<\/h5>\n\n\n\n<p>The signature is the element that gives the JWT its security. It is calculated from the JOSE header, the payload and a secret known only to the server.<\/p>\n\n\n\n<p>Thanks to this mechanism, any modification of the token (for example by an attacker trying to falsify the data) would result in an invalid signature. The server, by recalculating the signature with the secret, will then detect the inconsistency and reject the JWT. This is why it is crucial that this secret remains confidential: if it were to leak, an attacker could generate perfectly valid fraudulent tokens.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"signing-algorithms-symmetric-hs256-vs-asymmetric-rs256\">Signing algorithms: symmetric (HS256) vs. asymmetric (RS256)<\/h5>\n\n\n\n<p>The signature can be generated using various algorithms defined in the JWA specification &#8211; JSON Web Algorithms. Depending on the algorithm used, the secret can be:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Symmetric, with the same shared key for signing and verification (e.g. HS256)<\/li>\n\n\n\n<li>Asymmetric, with a private key\/public key pair (e.g. RS256)<\/li>\n<\/ul>\n\n\n\n<p>In the case of an asymmetric key, the public key can be exposed by the server in a standardised format defined in the JWK &#8211; JSON Web Key specification. This enables third parties to check the validity of the signature, without ever having access to the private key.<\/p>\n\n\n\n<p>Let&#8217;s take a first example: a JWT signed using a symmetric key and the HS256 algorithm. In this case, the signature is generated as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"82\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signing-1024x82.png\" alt=\"\" class=\"wp-image-13339\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signing-1024x82.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signing-300x24.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signing-1536x123.png 1536w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signing.png 1571w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>HMACSHA256(base64UrlEncode(&lt;en-t\u00eate JOSE>) + \".\" + base64UrlEncode(&lt;payload>), \"aVerySecretSecret\")<\/code><\/pre>\n\n\n\n<p>The result is then encoded in base64url and added to the end of the JWT. The server, in possession of the same secret, can recalculate this signature when it receives the token and compare it with the one provided. If the two correspond, the integrity and authenticity of the token are confirmed.<\/p>\n\n\n\n<p>Of course, in a real case, it is essential to use a robust secret that is difficult to guess or <a href=\"https:\/\/www.vaadata.com\/blog\/brute-force-attacks-principles-and-security-best-practices\/\" target=\"_blank\" rel=\"noopener\" title=\"\">brute force<\/a>.<\/p>\n\n\n\n<p>As a second example, it is also possible to sign a JWT using a private\/public key pair, using the RS256 algorithm (based on RSA):<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"82\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signature-1024x82.png\" alt=\"\" class=\"wp-image-13341\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signature-1024x82.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signature-300x24.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signature-1536x123.png 1536w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-signature.png 1577w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>RSASHA256(base64UrlEncode(&lt;en-t\u00eate JOSE>) + \".\" + base64UrlEncode(&lt;payload>), &lt;cl\u00e9 priv\u00e9e>)<\/code><\/pre>\n\n\n\n<p>The advantage here is that only the server with the private key can generate a valid signature, but verification can be delegated to other services via the public key, which is particularly useful in distributed or microservice architectures.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pros-and-cons-of-jwts\">Pros And Cons of JWTs<\/h2>\n\n\n\n<p>Using a JWT instead of a session identifier has certain advantages, but also some notable disadvantages.<\/p>\n\n\n\n<p>The positive points include, above all, interoperability: a JWT can easily be transmitted between several applications, even if they use different technologies. Unlike a traditional session, there is no need to store server-side state, which simplifies management. Simply checking the token&#8217;s signature authenticates the user, without having to query a database or session system.<\/p>\n\n\n\n<p>What&#8217;s more, the JWT format (JSON encoded in base64url) makes it a compact and readable way of transporting data, often used in distributed architectures or APIs.<\/p>\n\n\n\n<p>But this stateless operation also has a major drawback: a JWT cannot be invalidated before its expiry date. If a token is ever compromised or a user needs to be disconnected immediately, you will need to either:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>set up a server-side blacklist (which reintroduces a form of state),<\/li>\n\n\n\n<li>or wait for the JWT to expire, which can cause security problems.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-are-the-most-common-jwt-vulnerabilities-and-exploitations\">What are the Most Common JWT Vulnerabilities and Exploitations?<\/h2>\n\n\n\n<p>For an attacker, compromising a JWT can be particularly interesting, as it amounts to compromising the application&#8217;s entire authentication system. If the attacker manages to manipulate a valid JWT, he can modify its identity, escalate his privileges and even access the accounts of other users.<\/p>\n\n\n\n<p>Normally, such an attack is impossible thanks to the signing mechanism, which guarantees the integrity of the token. But if the application is badly configured or vulnerable, an attacker could exploit certain loopholes to modify the content of the JWT without being detected.<\/p>\n\n\n\n<p>In this section, we&#8217;ll look at several common vulnerabilities related to JWT management, and how they can lead to successful attacks.<\/p>\n\n\n\n<p>To illustrate these attacks, we will use:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the <a href=\"https:\/\/jwt.io\/\" target=\"_blank\" rel=\"noopener\" title=\"\">jwt.io<\/a> site to easily view and manipulate tokens,<\/li>\n\n\n\n<li>and the Burp Suite extension \u2018JWT Editor\u2019 to generate or modify keys and secrets in a test context.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-177b8c4f2aedfb1e3e8d21b7589f487e\" id=\"jwt-signature-not-verified\" style=\"color:#c0b800\">JWT signature not verified<\/h3>\n\n\n\n<p>One of the first tests to be carried out on a JWT is to check whether the signature is actually verified by the server. In some cases, the developer may make the critical mistake of not checking the signature and simply decoding the JWT to read its content.<\/p>\n\n\n\n<p>If this verification is not carried out, it becomes trivial for an attacker to modify the content of the token, without the server noticing. For example, if the payload contains a claim such as <code>role<\/code>, the attacker can simply change its value to grant himself administrator privileges, without needing a valid signature.<\/p>\n\n\n\n<p>This is a serious security error, but unfortunately one that is still encountered during our <a href=\"https:\/\/www.vaadata.com\/blog\/web-application-penetration-testing-objective-methodology-black-box-grey-box-and-white-box-tests\/\" target=\"_blank\" rel=\"noopener\" title=\"\">web penetration tests<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-28f5ce1844477f8dcb1e39ebf7e98156\" id=\"exploiting-the-none-algorithm\" style=\"color:#c0b800\">Exploiting the &#8220;none&#8221; algorithm<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"understanding-the-risks-associated-with-the-none-algorithm\">Understanding the risks associated with the \u2018none\u2019 algorithm<\/h4>\n\n\n\n<p>A look at RFC 7518, which defines the algorithms that can be used to sign a JWT, reveals a surprising feature: the presence of an algorithm called <code>none<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"762\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-algorithms-1024x762.png\" alt=\"JWS algorithm list\" class=\"wp-image-13343\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-algorithms-1024x762.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-algorithms-300x223.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-algorithms.png 1177w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWS algorithm list<\/figcaption><\/figure>\n\n\n\n<p>When the <code>none<\/code> algorithm is used, the JWT is not signed at all, and is therefore considered valid by default. This represents a major risk if the Web application uses a library that implicitly authorises the use of this algorithm.<\/p>\n\n\n\n<p>In such a case, an attacker can simply modify the JOSE header of the JWT, replace the <code>alg<\/code> field with <code>none<\/code>, and then freely alter the contents of the token (for example, by giving himself administrator privileges). All without the need for a valid signature.<\/p>\n\n\n\n<p>This type of attack is possible because the header is interpreted even before the signature has been verified, which opens the door to manipulation if the application does not carry out rigorous checks on the accepted algorithms.<\/p>\n\n\n\n<p>For example, let&#8217;s say a web application returns the following JWT when the user \u2018johndoe\u2019 logs in:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"400\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/johndoe-jwt-1024x400.png\" alt=\"johndoe's JWT\" class=\"wp-image-13345\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/johndoe-jwt-1024x400.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/johndoe-jwt-300x117.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/johndoe-jwt.png 1532w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">johndoe&#8217;s JWT<\/figcaption><\/figure>\n\n\n\n<p>Even if the application correctly verifies the JWT signature, a dangerous behaviour may persist: it accepts the none algorithm.<\/p>\n\n\n\n<p>This means that if an attacker modifies the JWT header to indicate <code>\"alg\": \"none\"<\/code> and removes the signature, the server will still accept the token as valid.<\/p>\n\n\n\n<p>Once this stage has been completed, the payload can be freely modified, enabling a malicious user to be assigned an administrator role, for example. This type of vulnerability is therefore based on the absence of strict verification of the algorithm used.<\/p>\n\n\n\n<p>For the application, the following JWT is therefore valid:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"393\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/forged-admin-jwt-1024x393.png\" alt=\"admin JWT forged by johndoe\" class=\"wp-image-13347\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/forged-admin-jwt-1024x393.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/forged-admin-jwt-300x115.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/forged-admin-jwt.png 1525w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">admin JWT forged by johndoe<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"how-to-protect-yourself-ban-none-or-explicitly-authorise-the-right-algorithms\">How to protect yourself: ban &#8220;none&#8221; or explicitly authorise the right algorithms<\/h4>\n\n\n\n<p>To counter this vulnerability without changing the library, some developers implement a blacklist banning the explicit use of the <code>none<\/code> algorithm.<\/p>\n\n\n\n<p>But this approach remains fragile: nothing prevents an attacker from attempting variants such as <code>NonE<\/code> or <code>NoNe<\/code>, which are likely to bypass poorly implemented checks.<\/p>\n\n\n\n<p>This is why the use of a blacklist is not recommended. It is preferable to adopt a white list, explicitly defining the authorised algorithms (such as <code>HS256<\/code> or <code>RS256<\/code>). This guarantees strict control and avoids loopholes caused by lax interpretations of the algorithm.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-badae42f4178abd8050354e26d7b7932\" id=\"weak-secrets-and-brute-force-attacks\" style=\"color:#c0b800\">Weak secrets and brute force attacks<\/h3>\n\n\n\n<p>Even if a Web application correctly verifies the JWT&#8217;s signature and rejects the <code>none<\/code> algorithm, vigilance is still required: a JWT can still be vulnerable.<\/p>\n\n\n\n<p>Let&#8217;s take the case of a JWT signed with the <code>HS256<\/code> algorithm. This is based on a shared secret key, used both to sign and to verify the token. This means that if the secret is too simple, predictable or poorly protected, an attacker can try to find it by brute force, by testing a large list of common secrets.<\/p>\n\n\n\n<p>As soon as one of the secrets tested generates a signature identical to that of the original JWT, the attacker has succeeded: he can then generate and sign his own valid tokens, and thus impersonate any user in the application.<\/p>\n\n\n\n<p>For example, consider the following JWT:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"386\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-known-secret-1024x386.png\" alt=\"\" class=\"wp-image-13349\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-known-secret-1024x386.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-known-secret-300x113.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-known-secret.png 1527w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT with a known secret<\/figcaption><\/figure>\n\n\n\n<p>To brute force the secret used to sign this JWT, it is possible to use the \u2018hashcat\u2019 tool with <a href=\"https:\/\/github.com\/danielmiessler\/SecLists\/blob\/master\/Passwords\/scraped-JWT-secrets.txt\" target=\"_blank\" rel=\"noopener\" title=\"\">lists of known JWT secrets<\/a>.<\/p>\n\n\n\n<p>In our case, the following command finds the secret used to sign our JWT:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpvaG5kb2UiLCJlbWFpbCI6ImRvZWpAZXhhbXBsZS5jb20iLCJpYXQiOjE3NDQwMzM0MDAsImV4cCI6MTc0NDExOTgwMH0.If5smKFyDhdY5fPN-qQOawSREJIQxRLRslQeUYTn070 \/usr\/share\/seclists\/Passwords\/scraped-JWT-secrets.txt<\/code><\/pre>\n\n\n\n<p>The <code>-a 0<\/code> option indicates the attack mode used by Hashcat. Here, the value <code>0<\/code> corresponds to a dictionary attack, i.e. the default approach. The <code>-m 16500<\/code> option specifies the type of hash to be attacked &#8211; in this case, type <code>16500<\/code> corresponds to a JWT signature.<\/p>\n\n\n\n<p>You then simply need to supply the JWT to be tested and a list of secrets to try.<\/p>\n\n\n\n<p>Once the search is complete and the secret has been found, you can display the result using the <code>--show<\/code> option.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"209\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-secret-found-with-hashcat-1024x209.png\" alt=\"secret found with hashcat\" class=\"wp-image-13351\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-secret-found-with-hashcat-1024x209.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-secret-found-with-hashcat-300x61.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-secret-found-with-hashcat.png 1531w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">secret found with hashcat<\/figcaption><\/figure>\n\n\n\n<p>In green is the secret used to sign the JWT.<\/p>\n\n\n\n<p>With this secret, the attacker can forge as many JWTs as he likes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-832b7172310d7145a72e485b48b50a66\" id=\"injection-of-specific-parameters-into-the-jose-header-of-the-jwt\" style=\"color:#c0b800\">Injection of specific parameters into the JOSE header of the JWT<\/h3>\n\n\n\n<p>In some cases, it is possible to manipulate or add parameters in the JOSE header to exploit the JWT. These parameters are specified in <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7515?ref=codecurated.com#section-4.1\" target=\"_blank\" rel=\"noopener\" title=\"\">RFC 7515<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"jwk\">jwk<\/h4>\n\n\n\n<p>Sometimes the JWT&#8217;s public key is included directly in the header. In this case, the key is in JWK (JSON Web Key) format and is placed in the <code>jwk<\/code> parameter of the header. A public key in JWK format is represented in the form of a JSON object. Here is an example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n    \"kty\": \"RSA\",\n    \"e\": \"AQAB\",\n    \"kid\": \"09979990-6f43-4b28-acf1-a99dcb455c9f\",\n    \"n\": \"u1bmuBchhoNbOuEYeyEjE_sOTng7boN7hdbcnQoNQNheSQCOwGcfZXE8vpFFdIFY6zVm8loYok6wEUtq-JjDj2jFrj68asuJrFbvAyC4M6FJhP6Ox4K4UzUQlLBEJvmbFzU-CfjyqV9xPR1q09Bg9Qc3qNeg7UYfXVgniFm5CkVjlpvtn1Xj6UHLWQ1NAqknYKcB13S4vNdAEyx69PFaRVjco9PzbJefubaZ78YpRrMKEvknim1bH1XHCj-JKb8enhgf78J4uTwG6CMvunVkY1KKbZI-AJjnRprAYHW26_fQg5GJkD13taTGSkNtpzrji6IY4ls-3z7Zz-IPpA4iHQ\"\n}<\/code><\/pre>\n\n\n\n<p>However, if the server does not set up a white list of authorised public keys, an attacker can sign the JWT with his own private key and include his public key in the <code>jwk<\/code> parameter, associated with this private key.<\/p>\n\n\n\n<p>Take the following JWT:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"421\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-1024x421.png\" alt=\"JWT signed with RS256\" class=\"wp-image-13353\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-1024x421.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-300x123.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt.png 1529w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT signed with RS256<\/figcaption><\/figure>\n\n\n\n<p>This JWT is signed with the RS256 algorithm, using a private\/public key pair. The private key is known only to the server. However, in this scenario, the server accepts the use of the <code>jwk<\/code> parameter and allows any public key to be added. An attacker can therefore:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Modify the JWT (for example, change the value of the <code>username<\/code> claim from \u2018johndoe\u2019 to \u2018admin\u2019).<\/li>\n\n\n\n<li>Add their own public key to the header&#8217;s <code>jwk<\/code> parameter.<\/li>\n\n\n\n<li>Sign the JWT with its own private RSA key.<\/li>\n<\/ol>\n\n\n\n<p>The final JWT will look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"657\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jwk-1024x657.png\" alt=\"JWT with jwk \" class=\"wp-image-13355\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jwk-1024x657.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jwk-300x192.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jwk.png 1534w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT with jwk<\/figcaption><\/figure>\n\n\n\n<p>We can see the inclusion of the <code>jwk<\/code> parameter, which contains the attacker&#8217;s public key in JWK format. It is also essential to add the <code>kid<\/code> parameter, which specifies which public key the server should use to verify the signature. In this case, the value of <code>kid<\/code> corresponds to that of <code>jwk<\/code>, which tells the server that it must use the public key included in the <code>jwk<\/code> parameter.<\/p>\n\n\n\n<p>This JWT will then be accepted by the server, and the attacker will be able to access the administrator account.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"jku\">jku<\/h4>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"jku-parameter-and-jwk-sets\">jku parameter and JWK Sets<\/h5>\n\n\n\n<p>The public key can also be exposed on an external server. In this case, it generally forms part of a list of public keys (called a JWK Set), where each key is associated with a unique identifier, the <code>kid<\/code>. This list is often accessible via endpoints such as <code>.well-known\/jwks.json<\/code> or <code>\/jwks.json<\/code>.<\/p>\n\n\n\n<p>Here&#8217;s an example:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"447\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwk-set-1024x447.png\" alt=\"JWK Set\" class=\"wp-image-13357\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwk-set-1024x447.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwk-set-300x131.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwk-set.png 1529w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWK Set<\/figcaption><\/figure>\n\n\n\n<p>In this example, the JWK Set contains a single public key, which is presented in JWK format in a JSON array named <code>keys<\/code>.<\/p>\n\n\n\n<p>To tell the server which JWK Set to use, it is possible to add the header parameter <code>jku<\/code> to the JWT, which specifies the URL of the server exposing this JWK Set.<\/p>\n\n\n\n<p>However, like the attack on the <code>jwk<\/code> parameter, if the server accepts the jku parameter without whitelisting public keys or authorised domains, an attacker can:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Modify the JWT.<\/li>\n\n\n\n<li>Host the public key associated with the private key they are using on their own server.<\/li>\n\n\n\n<li>Add the <code>jku<\/code> parameter to the header, with the URL of their own server as the value.<\/li>\n\n\n\n<li>Add the <code>kid<\/code> parameter to point to the identifier of the public key hosted on its server.<\/li>\n\n\n\n<li>Sign the JWT with your own private key.<\/li>\n<\/ol>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"exploiting-the-jku\">Exploiting the jku<\/h5>\n\n\n\n<p>Let&#8217;s take the previous JWT:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"421\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-1024x421.png\" alt=\"JWT signed with RS256\" class=\"wp-image-13353\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-1024x421.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt-300x123.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rs256-signed-jwt.png 1529w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT signed with RS256<\/figcaption><\/figure>\n\n\n\n<p>The attacker can then host the public key in a JWK Set on his own server, as shown in the previous example. He can then add the <code>jku<\/code> parameter, pointing to the URL of his server, include the <code>kid<\/code> parameter and sign the JWT with his own private key.<\/p>\n\n\n\n<p>The final JWT will look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"396\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jku-1024x396.png\" alt=\"JWT with jku\" class=\"wp-image-13359\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jku-1024x396.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jku-300x116.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-with-jku.png 1530w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT with jku<\/figcaption><\/figure>\n\n\n\n<p>It is crucial that the <code>kid<\/code> in the JWT matches exactly that of the public key exposed on the server, so that the server uses the correct key in the JWK Set. Once this stage has been completed, the JWT will be accepted by the server, allowing the attacker to gain access to the administrator account.<\/p>\n\n\n\n<p>It is also important to note that, as it is the server that makes the request to retrieve the public key from an external server, this opens the door to a risk of <a href=\"https:\/\/www.vaadata.com\/blog\/understanding-web-vulnerability-server-side-request-forgery-1\/\" target=\"_blank\" rel=\"noopener\" title=\"\">SSRF (Server-Side Request Forgery)<\/a>. However, this specific exploitation will not be detailed in this article.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"kid\">kid<\/h4>\n\n\n\n<p>As you can see, the <code>kid<\/code> header parameter tells the server which public key to use to verify the JWT&#8217;s signature. Regardless of the location of the public key, the server generally looks for one that has the same <code>kid<\/code> as the one present in the JWT. However, depending on how the server manages the value of the <code>kid<\/code>, classic vulnerabilities such as <a href=\"https:\/\/www.vaadata.com\/blog\/sql-injections-principles-impacts-exploitations-security-best-practices\/\" target=\"_blank\" rel=\"noopener\" title=\"\">SQL injection<\/a> or <a href=\"https:\/\/www.vaadata.com\/blog\/understanding-preventing-path-traversal-vulnerability\/\" target=\"_blank\" rel=\"noopener\" title=\"\">path traversal<\/a> can occur.<\/p>\n\n\n\n<p>In the following example, a path traversal vulnerability is present in the <code>kid<\/code> parameter, and the JWT is signed using a symmetric algorithm, HS256.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"397\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/hs256-signed-jwt-1024x397.png\" alt=\"JWT signed with HS256\" class=\"wp-image-13361\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/hs256-signed-jwt-1024x397.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/hs256-signed-jwt-300x116.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/hs256-signed-jwt.png 1527w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT signed with HS256<\/figcaption><\/figure>\n\n\n\n<p>To arbitrarily modify a JWT, the attacker can exploit the <code>kid<\/code> parameter by directing it to a file whose contents are known, then sign the JWT with a symmetric key whose value corresponds to the contents of this file.<\/p>\n\n\n\n<p>Taking the example of the previous JWT, the attacker can first use a path traversal vulnerability in the <code>kid<\/code> parameter to redirect it to the <code>\/dev\/null<\/code> file. Then he creates a symmetric key whose value is an empty character string. Since the kid points to an empty value, the attacker can modify the JWT as he wishes and sign it with his empty symmetric key. Since the <code>\/dev\/null<\/code> file (and therefore the symmetric key) has an empty value, the JWT&#8217;s signature will be valid.<\/p>\n\n\n\n<p>The final JWT may look like the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"562\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/null-byte-secret-signed-jwt-1024x562.png\" alt=\"JWT signed with a null byte secret\" class=\"wp-image-13363\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/null-byte-secret-signed-jwt-1024x562.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/null-byte-secret-signed-jwt-300x165.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/null-byte-secret-signed-jwt.png 1530w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT signed with a null byte secret<\/figcaption><\/figure>\n\n\n\n<p>We can see that the JWT is signed with a secret whose value is empty. It will therefore be accepted by the server and the attacker will have an \u2018admin\u2019 role.<\/p>\n\n\n\n<h3 class=\"wp-block-heading has-text-color has-link-color wp-elements-e4d184e48b0128e1679bd009886d3d66\" id=\"algorithm-confusion\" style=\"color:#c0b800\">Algorithm Confusion<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"how-the-algorithm-confusion-attack-works\">How the Algorithm Confusion attack works<\/h4>\n\n\n\n<p>An application becomes vulnerable to an algorithm confusion attack when the developer uses the same public key to verify the JWT signature, whether the algorithm is symmetric or asymmetric.<\/p>\n\n\n\n<p>Although it may seem unlikely, a developer can sometimes assume that the algorithm used will always be asymmetric, without explicitly checking.<\/p>\n\n\n\n<p>This type of behaviour can be illustrated by the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>publicKey = &lt;public-key-of-server>;\ntoken = request.getCookie(\"jwt\");\nverify(token, publicKey); <\/code><\/pre>\n\n\n\n<p>If the server allows the user to modify the algorithm in the JWT, an attacker can choose a symmetric algorithm and use the public key as a secret.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"exploiting-the-vulnerability\">Exploiting the vulnerability<\/h4>\n\n\n\n<p>To exploit this vulnerability, the attacker must follow several steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Recover the public key<\/li>\n\n\n\n<li>Transform the public key into a valid format<\/li>\n\n\n\n<li>Modify the targeted claims and the JWT algorithm<\/li>\n\n\n\n<li>Sign the JWT forged with the public key<\/li>\n<\/ol>\n\n\n\n<p>For the first stage, if the public key is not exposed by the server (via the <code>\/jwks.json<\/code> endpoint, for example), it can be calculated from two generated JWTs. Tools such as \u2018<a href=\"https:\/\/github.com\/silentsignal\/rsa_sign2n\" target=\"_blank\" rel=\"noopener\" title=\"\">rsa_sign2n<\/a>\u2019 can be used for this.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><\/ol>\n\n\n\n<p>As for the second stage, it is crucial to transform the public key into a valid format, as it must be exactly identical to the one used by the server to verify the signature.<\/p>\n\n\n\n<p>For example, if the attacker obtains the public key in JWK format but the server uses an X.509 PEM format to store and verify the key, the attacker will have to convert the JWK key into X.509 PEM format before signing his JWT.<\/p>\n\n\n\n<p>These two steps are performed by the rsa_sign2n tool. To install and use it, here are the commands to run:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"246\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/installing-and-using-rsa-sign2n-1024x246.png\" alt=\"installing and using rsa_sign2n\" class=\"wp-image-13365\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/installing-and-using-rsa-sign2n-1024x246.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/installing-and-using-rsa-sign2n-300x72.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/installing-and-using-rsa-sign2n.png 1531w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">installing and using rsa_sign2n<\/figcaption><\/figure>\n\n\n\n<p>The last command calculates the public key from two generated JWTs. In addition, the tool returns one or more JWTs signed in HS256 with the calculated public key, in X.509 PEM format, in order to test their validity.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"227\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rsa-sign2n-generated-jwt-1024x227.png\" alt=\"JWTs generated by rsa_sign2n\" class=\"wp-image-13367\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rsa-sign2n-generated-jwt-1024x227.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rsa-sign2n-generated-jwt-300x66.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/rsa-sign2n-generated-jwt.png 1536w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWTs generated by rsa_sign2n<\/figcaption><\/figure>\n\n\n\n<p>If one of the JWTs is valid, this indicates that the Web application is vulnerable to an algorithm confusion attack. The next step is to modify the JWT to achieve the desired objective and to sign it again with the public key.<\/p>\n\n\n\n<p>With CyberChef:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"453\" src=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-forged-by-attacker-1024x453.png\" alt=\"JWT forged by the attacker\" class=\"wp-image-13369\" srcset=\"https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-forged-by-attacker-1024x453.png 1024w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-forged-by-attacker-300x133.png 300w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-forged-by-attacker-1536x679.png 1536w, https:\/\/www.vaadata.com\/blog\/wp-content\/uploads\/2016\/12\/jwt-forged-by-attacker.png 1549w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">JWT forged by the attacker<\/figcaption><\/figure>\n\n\n\n<p>This JWT will then be accepted by the server. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-to-secure-the-implementation-of-jwt\">How to Secure the Implementation of JWT?<\/h2>\n\n\n\n<p>Here are several best practices for securing the implementation of your JWTs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Clearly define the JWT configuration<\/strong>: specify the algorithm to be used and the claims expected in the payload. This configuration must be well documented.<\/li>\n\n\n\n<li><strong>Avoid reimplementing JWT management<\/strong>: use libraries that are recognised and well maintained in the programming language used. These libraries manage the generation and verification of signatures and the handling of claims in a secure manner.<\/li>\n\n\n\n<li><strong>Respect the defined configuration<\/strong>: the chosen library must follow the configuration initially defined to avoid any inconsistencies that could introduce vulnerabilities.<\/li>\n\n\n\n<li><strong>Secure the use of the <code>jku<\/code> parameter<\/strong>: if this parameter is used, set up a white list of authorised domains on the server side to avoid requests to uncontrolled sources.<\/li>\n\n\n\n<li><strong>Always include the <code>exp<\/code> (Expiration Time) claim<\/strong>: this limits the validity of the JWT and reduces the risks in the event of compromise.<\/li>\n\n\n\n<li><strong>Use separate secrets<\/strong>: if you use several JWTs for different purposes or environments, make sure you sign each one with a unique secret.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>In this article, we take a detailed look at how JSON Web Tokens (JWTs) work: their usefulness, structure, advantages and limitations. We have also identified the most common vulnerabilities, possible exploitation techniques and best practices for protecting against them.<\/p>\n\n\n\n<p>If you are interested in this subject, we strongly encourage you to put these concepts into practice through interactive labs such as those offered by the <a href=\"https:\/\/portswigger.net\/web-security\/jwt\" target=\"_blank\" rel=\"noopener\" title=\"\">Burp Academy<\/a>. Not only are these labs of the highest quality, they are also backed up by clear, in-depth educational content.<\/p>\n\n\n\n<p><strong>Authors: Lorenzo CARTE &#8211; Pentester &amp; Amin TRAOR\u00c9 &#8211; CMO @ Vaadata<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":{"0":"post-1648","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-technical"},"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/posts\/1648","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/comments?post=1648"}],"version-history":[{"count":7,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/posts\/1648\/revisions"}],"predecessor-version":[{"id":14796,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/posts\/1648\/revisions\/14796"}],"wp:attachment":[{"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/media?parent=1648"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/categories?post=1648"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vaadata.com\/blog\/wp-json\/wp\/v2\/tags?post=1648"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}