{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiftjikmfbrmp2k43xjzo6k5nlrqu2yzxgr2uwywbawjbxhw64dceq",
"uri": "at://did:plc:anldby4lwneunjl777bq6ih7/app.bsky.feed.post/3mgmcd7ybk622"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreihn7yu3katrau76fkipa5bavw62ksrf3hixjrcgv7h3fnvyadgvq4"
},
"mimeType": "image/png",
"size": 1651214
},
"description": "[Tip #127] Without an `exp` claim, a JWT can remain valid forever, turning a leaked token into permanent access.",
"path": "/security-tip-your-jwt-might-be-a-forever-key/",
"publishedAt": "2026-03-09T07:08:28.000Z",
"site": "https://securinglaravel.com",
"tags": [
"** _Security Tips_**",
"_**In Depth** articles_",
"_Laravel Security Audit and Penetration Test_",
"_Security Reviews_",
"_Bluesky_",
"_other socials_",
"_Practical Laravel Security_"
],
"textContent": "JSON Web Tokens (JWT) were designed as a tamper-resistant method of passing data between different systems, in the form of a set of claims. These claims are typically used as some part of an authentication and authorisation system - asserting who the bearer is and what they are allowed to do. Note that JWTs aren't encrypted, they're just signed to prevent modification.\n\nJWTs look something like this:\n\n\n eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30\n\nExample JWT\n\nIf you recognise the two `eyJ` in there, you'll quickly figure out that you're looking at a couple of Base64URL encoded JSON strings, separated by `.`:\n\n\n eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\n {\"alg\":\"HS256\",\"typ\":\"JWT\"}\n\n eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0\n {\"sub\":\"1234567890\",\"name\":\"John Doe\",\"admin\":true,\"iat\":1516239022}\n\n KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30\n <HMAC Signature>\n\nJWT Decoded\n\nWe'll have to dig into how JWTs work in greater detail another time, but for now I just want to point out something specific that is missing here...\n\nIn the middle section, we have the user `John Doe`, identified by `sub=1234567890` (their user ID), marked as `admin=true`, with this token issued at `iat=1516239022` (18th January 2018).\n\nI often see JWTs used to authenticate users, either via some form of API, in an SPA, or via magic links. But the problem is, this token is 8 years old (at time of writing), and as far as the application is concerned **it's still perfectly valid!** If the application accepts this JWT, then anyone who gains access to it will become Admin John Doe!\n\nJohn might have left the company, their requests might have been logged somewhere and recovered in a data breach, their emails might have been compromised, etc. The possibilities are endless... like this token. It is effectively a **forever key** that will always allow the bearer in.\n\nOh, and **JWTs cannot be revoked** without some form of server-side state.\n\nWithout adding server-side state, rotating keys, or maintaining blocklists, the easiest way to limit abuse is to add an Expiration Time (`exp`) claim. This sets a limited time window on the JWT, so if it's stolen or discovered later, it's no longer useful.\n\nFor example, this payload gives the JWT one month validity:\n\n\n {\n \"sub\": \"1234567890\",\n \"name\": \"John Doe\",\n \"admin\": true,\n \"iat\": 1516239022,\n \"exp\": 1518881422\n }\n\nIt would have stopped working on the 18th February 2018, and be completely useless to anyone who discovers it after this date.\n\nSure, you have to issue new JWTs periodically, **but the alternative is a forever key you are unaware of and cannot block.**\n\nSo make sure your JWTs have a reasonable `exp` set!\n\n_Oh, and make sure your JWT package actually enforces`exp`, apparently not all of them do by default. _🤦\n\n* * *\n\n** _If you found this security tip useful?_ 👍**\n _Subscribe now_ _to get weekly_** _Security Tips_** _straight to your inbox, filled with practical, actionable advice to help you build safer apps._\n\n**_Want to learn more?_ 🤓**\n _Upgrade to a_ _Premium Subscription_ _for exclusive monthly_ _**In Depth** articles_ _, or support my work with a_ _one-off tip_ _! Your support directly funds my security work in the Laravel community._ 🥰\n\n _**Need a second set of eyes on your code?**\nBook in a __Laravel Security Audit and Penetration Test_ _today! I also offer budget-friendly_ _Security Reviews_ _too._\n\n_Finally, connect with me on_ _Bluesky_ _, or_ _other socials_ _, and check out_ _Practical Laravel Security_ _, my interactive course designed to boost your Laravel security skills._ 1",
"title": "Security Tip: Your JWT Might Be a Forever Key!",
"updatedAt": "2026-03-09T07:08:29.135Z"
}