[{"data":1,"prerenderedAt":562},["ShallowReactive",2],{"post-\u002Fis-jwt-safe-or-is-it-vulnerable":3},{"page":4,"translation":439,"nav":441,"related":554,"random":555},{"id":5,"title":6,"body":7,"categories":418,"category":420,"date":421,"description":422,"draft":423,"extension":424,"image":425,"kind":420,"lang":426,"meta":427,"navigation":428,"path":429,"readingTime":430,"seo":431,"slug":432,"stem":432,"tags":433,"translationKey":432,"type":419,"updated":437,"__hash__":438},"posts\u002Fis-jwt-safe-or-is-it-vulnerable.md","Is JWT Safe or Is It Vulnerable?",{"type":8,"value":9,"toc":402},"minimark",[10,52,55,58,65,68,73,170,172,177,180,183,191,198,205,207,211,214,221,224,227,234,237,241,244,247,249,253,305,307,311,314,318,333,341,345,352,359,361,365,375,378,381,386],[11,12,13,21],"blockquote",{},[14,15,16,17],"p",{},"💡 ",[18,19,20],"strong",{},"Quick Summary (TL;DR):",[22,23,24,31,46],"ul",{},[25,26,27,30],"li",{},[18,28,29],{},"The Paradox:"," JWT is stateless, meaning once issued, it is valid until it expires. You cannot easily invalidate a single token without query checks.",[25,32,33,36,37,41,42,45],{},[18,34,35],{},"The Security Gaps:"," Common vulnerabilities include leaked tokens (no immediate logout), storage vulnerabilities (XSS in ",[38,39,40],"code",{},"localStorage","), and algorithm attacks (e.g., the ",[38,43,44],{},"\"alg\": \"none\""," or HS256\u002FRS256 key confusion).",[25,47,48,51],{},[18,49,50],{},"The Solution:"," Use short-lived Access Tokens (minutes) and database-backed Refresh Tokens. For maximum security, evaluate if a traditional stateful session cookie is a simpler, safer fit for your architecture.",[14,53,54],{},"Let's start by explaining what JWT (JSON Web Token) stands for. However, the main focus of this post is not about how JWT can increase security, but rather how it can harm it if not applied with caution.",[14,56,57],{},"JWT is an open standard used for creating access tokens. This token is usually created on the server (though not strictly required), sent to the client, and ensures that the information signed by the server cannot be tampered with by anyone else.",[14,59,60,61,64],{},"Because a JWT is signed with the server's private key at the time of creation, no one can modify this information without that key. For example, if the JWT token holds the payload ",[38,62,63],{},"{\"username\": \"evrenbal\", \"userId\": 1}",", when the server receives this token back later, it doesn't need to query the database to verify the user ID. It can safely trust the ID provided by the client, simply by verifying the token's signature.",[66,67],"hr",{},[69,70,72],"h3",{"id":71},"common-jwt-vulnerabilities-mitigations","Common JWT Vulnerabilities & Mitigations",[74,75,76,93],"table",{},[77,78,79],"thead",{},[80,81,82,87,90],"tr",{},[83,84,86],"th",{"align":85},"left","Vulnerability",[83,88,89],{"align":85},"Attack Vector",[83,91,92],{"align":85},"Modern Mitigation",[94,95,96,120,133,157],"tbody",{},[80,97,98,107,113],{},[99,100,101],"td",{"align":85},[18,102,103,106],{},[38,104,105],{},"alg: \"none\""," Exploit",[99,108,109,110,112],{"align":85},"Attacker removes the signature and changes header to ",[38,111,44],{},".",[99,114,115,116,119],{"align":85},"Use modern, up-to-date JWT libraries; explicitly whitelist allowed algorithms (e.g., ",[38,117,118],{},"[\"HS256\"]",").",[80,121,122,127,130],{},[99,123,124],{"align":85},[18,125,126],{},"Key Confusion",[99,128,129],{"align":85},"Attacker signs token using public RS256 key as an HS256 secret.",[99,131,132],{"align":85},"Force algorithm checking; do not let the token header dictate the verification key.",[80,134,135,140,146],{},[99,136,137],{"align":85},[18,138,139],{},"Token Theft (XSS)",[99,141,142,143,145],{"align":85},"Token stored in ",[38,144,40],{}," stolen via malicious script.",[99,147,148,149,152,153,156],{"align":85},"Store JWTs in ",[38,150,151],{},"httpOnly"," secure cookies with ",[38,154,155],{},"SameSite=Strict\u002FLax"," flags.",[80,158,159,164,167],{},[99,160,161],{"align":85},[18,162,163],{},"Stateless Logout Gap",[99,165,166],{"align":85},"User logs out, but token remains valid until expiration.",[99,168,169],{"align":85},"Use short-lived access tokens combined with a blacklist or a database-backed refresh token.",[66,171],{},[173,174,176],"h2",{"id":175},"what-jwt-is-not","What JWT Is Not",[14,178,179],{},"Contrary to popular belief, JWT is not a magic security bullet. If it is not applied consciously, it can introduce serious vulnerabilities, and it should not be blindly trusted for authentication in critical operations.",[14,181,182],{},"Consider this common line of reasoning:",[11,184,185],{},[14,186,187],{},[188,189,190],"em",{},"\"The information inside the JWT is signed and reliable. There is a username 'evrenbal' in the token, so my server verified that he is indeed 'evrenbal' when he logged in. What could possibly go wrong?\"",[14,192,193,194,197],{},"Imagine if a user named ",[18,195,196],{},"evrenbal"," discovers that his password has been stolen, or realizes he forgot to log out on a public computer he used recently. Naturally, he will immediately log in to his account, change his password, and assume the problem is solved.",[14,199,200,201,204],{},"The problem is, if your backend developer only checks whether the incoming JWT token is structurally valid and hasn't expired yet, ",[18,202,203],{},"changing the password does not invalidate the active token"," on that public computer. The attacker still has full access until the token naturally expires.",[66,206],{},[173,208,210],{"id":209},"the-expiration-and-refresh-token-dilemma","The Expiration and Refresh Token Dilemma",[14,212,213],{},"You might argue:",[11,215,216],{},[14,217,218],{},[188,219,220],{},"\"I can set a very short validity period (expiration) on the JWT to fix this!\"",[14,222,223],{},"Indeed, a good approach is to keep the validity period of the access token short (e.g., a few minutes or hours) and generate a \"refresh token\" with a longer duration (e.g., a few weeks or months).",[14,225,226],{},"In this way, while ensuring that an access token becomes ineffective quickly if stolen, you can automatically issue a new access token without forcing the user to log in again.",[14,228,229,230,233],{},"However, there is a catch. In order to validate the long-term refresh token and allow instant revocation, you ",[18,231,232],{},"must store the refresh token in a database or cache"," (like Redis).",[14,235,236],{},"If the incoming refresh token is valid and matches the one stored in your database, you generate a new access token. If the user changes their password, you simply delete or modify the refresh token in the database, invalidating all previously created sessions. You can even offer the user an option to \"Log out of all other devices\" when changing their password.",[69,238,240],{"id":239},"did-we-really-solve-the-problem","Did We Really Solve the Problem?",[14,242,243],{},"Not entirely. During the active window of the short-lived access token, you still have no way of knowing if the token has been hijacked. You will only detect it and block the user once the access token expires and they attempt to request a new one using the refresh token.",[14,245,246],{},"If you reduce the access token lifetime to just 1-2 minutes to minimize this window, your client application will have to hit your server constantly to refresh it. In doing so, you end up querying the database so frequently that you lose the \"stateless\" performance benefit that made you choose JWT in the first place!",[66,248],{},[173,250,252],{"id":251},"critical-security-vulnerabilities-to-watch-out-for","Critical Security Vulnerabilities to Watch Out For",[254,255,256,279,285],"ol",{},[25,257,258,264,265,268,269,272,273,275,276,278],{},[18,259,260,261,263],{},"The ",[38,262,105],{}," Vulnerability:"," Early implementations of JWT libraries allowed the algorithm header to be set to ",[38,266,267],{},"\"none\"",". An attacker could decode the token, modify the payload (e.g., setting ",[38,270,271],{},"\"isAdmin\": true","), set ",[38,274,44],{},", strip the signature, and send it to the server. Some backend libraries would accept it as a valid, unsigned token. Ensure your library is updated and explicitly configured to reject ",[38,277,267],{}," algorithms.",[25,280,281,284],{},[18,282,283],{},"Key Confusion Attacks:"," If your server supports both asymmetric (RS256) and symmetric (HS256) algorithms, an attacker can obtain your public RS256 key (which is public by design) and use it as a symmetric secret to sign a malicious token using HS256. The server, seeing HS256 in the header, might verify the signature using the public key as the secret.",[25,286,287,290,291,293,294,297,298,300,301,304],{},[18,288,289],{},"Storage Security (XSS vs. CSRF):"," Many tutorials recommend storing JWTs in ",[38,292,40],{}," or ",[38,295,296],{},"sessionStorage",". This makes them highly vulnerable to Cross-Site Scripting (XSS) attacks. If an attacker injects a malicious script, they can read the token easily. Storing JWTs in ",[38,299,151],{}," secure cookies protects them from XSS, though you must implement proper CSRF mitigations (like ",[38,302,303],{},"SameSite=Lax\u002FStrict"," and anti-CSRF tokens).",[66,306],{},[173,308,310],{"id":309},"safer-implementation-patterns","Safer Implementation Patterns",[14,312,313],{},"If you must use JWT, consider implementing these strategies:",[69,315,317],{"id":316},"pattern-1-password-age-verification-quick-fix","Pattern 1: Password Age Verification (Quick Fix)",[14,319,320,321,324,325,328,329,332],{},"When verifying a JWT, check its creation timestamp (",[38,322,323],{},"iat",") against the user's ",[38,326,327],{},"last_password_change"," date stored in a fast cache (like Redis). If the password was changed ",[188,330,331],{},"after"," the token was issued, deny the request.",[22,334,335],{},[25,336,337,340],{},[18,338,339],{},"Result:"," The user is instantly logged out from all devices upon a password change, though this does force a logout on their legitimate devices as well.",[69,342,344],{"id":343},"pattern-2-device-linked-session-tracking-highly-recommended","Pattern 2: Device-Linked Session Tracking (Highly Recommended)",[14,346,347,348,351],{},"Every time a user logs in, generate a unique session ID (e.g., ",[38,349,350],{},"\"ABCDE\"",") linked to the device's metadata (IP, user agent, OS). Store this session ID in a fast-access cache (like Redis) and embed it inside the JWT payload. On every request, compare the session ID in the token with the active sessions in your cache.",[22,353,354],{},[25,355,356,358],{},[18,357,339],{}," When the user changes their password, you can clear all active sessions from Redis, instantly invalidating all issued JWTs. You can also build a \"Devices\" management page allowing users to revoke specific sessions individually.",[66,360],{},[173,362,364],{"id":363},"is-jwt-actually-necessary","Is JWT Actually Necessary?",[14,366,367,368,371,372,112],{},"If you are already storing session IDs (",[38,369,370],{},"ABCDE",") in a cache, querying it on every request, and managing session lifetimes on the server, you have built a ",[18,373,374],{},"stateful system",[14,376,377],{},"If your backend is stateful, why carry the overhead of a large base64-encoded JWT payload and include JWT parsing libraries? In most traditional monolithic or single-domain web applications, a simple, lightweight secure cookie containing a session ID is a much simpler, more secure, and battle-tested choice.",[14,379,380],{},"JWT is an exciting technology, and when developers first discover it, they often want to use it for everything. However, before integrating JWT into your next project, take a moment to ask yourself if you genuinely need it, or if a traditional session cookie would be simpler and safer.",[382,383,385],"h5",{"id":384},"bu-yazıda-yapılan-değişiklikler","Bu Yazıda Yapılan Değişiklikler",[22,387,388],{},[25,389,390,391,393,394,397,398,401],{},"21.06.2026: Metin içindeki Türkçe-İngilizce kelime karışmaları (çeviri sızıntıları) temizlendi. Makaleye ",[38,392,105],{},", Key Confusion ve depolama (XSS vs. CSRF) gibi yaygın JWT güvenlik açıkları ve çözüm yöntemleri eklendi. Değişken ve fiil yazımları (",[38,395,396],{},"logout"," -> ",[38,399,400],{},"log out"," vb.) düzeltildi. Özet paneli ile zafiyet karşılaştırma tablosu eklendi.",{"title":403,"searchDepth":404,"depth":404,"links":405},"",2,[406,408,409,412,413,417],{"id":71,"depth":407,"text":72},3,{"id":175,"depth":404,"text":176},{"id":209,"depth":404,"text":210,"children":410},[411],{"id":239,"depth":407,"text":240},{"id":251,"depth":404,"text":252},{"id":309,"depth":404,"text":310,"children":414},[415,416],{"id":316,"depth":407,"text":317},{"id":343,"depth":407,"text":344},{"id":363,"depth":404,"text":364},[419],"technical",null,"2022-05-12","JSON Web Tokens (JWT) are incredibly popular, but are they safe? Explore stateless session vulnerabilities, leaked tokens, storage exploits, and design gotchas.",false,"md","\u002Fimages\u002Fhero\u002Fjwt-security.avif","en",{},true,"\u002Fis-jwt-safe-or-is-it-vulnerable",7,{"title":6,"description":422},"is-jwt-safe-or-is-it-vulnerable",[434,435,436],"security","vulnerability","jwt","2026-06-21","UsoJrsWknWpCivia5kFQ1JK5FBBgvUHNr0fLKrEGSzk",{"path":440},"\u002Ftr\u002Fjwt-guvenli-mi-guvenlik-acigi-olusturmayin",{"prev":442,"next":445,"others":448,"lucky":553,"readingTime":430},{"path":443,"title":444},"\u002Fwhat-is-an-api-gateway","What is an API Gateway? Should You Use It?",{"path":446,"title":447},"\u002Ftailwind-css-to-use-or-not-to-use-that-is-the-question","Tailwind CSS! To use? Or not to use? That is the question.",[449,452,455,458,461,464,467,470,473,476,479,482,485,488,491,494,497,500,503,506,509,512,515,518,521,524,527,530,533,536,539,542,543,544,547,550],{"path":450,"title":451},"\u002Ffull-stack-project-development","Sample REST API Project",{"path":453,"title":454},"\u002Frest-api-authentication","How to Perform REST API Authentication?",{"path":456,"title":457},"\u002Frest-api-design","REST API Design: Principles and Output Format",{"path":459,"title":460},"\u002Frest-api-documentation-and-testing","How to Document and Test a REST API?",{"path":462,"title":463},"\u002Frest-api-error-handling","How to Perform REST API Error Handling?",{"path":465,"title":466},"\u002Frest-api-security","How to Secure a REST API?",{"path":468,"title":469},"\u002Frest-api-uri-structure","What Should the REST API URI Structure Be?",{"path":471,"title":472},"\u002Ftroubleshooting-cyberpanel-inaccessibility-after-ubuntu-release-upgrade","Troubleshooting CyberPanel Inaccessibility After Ubuntu Release Upgrade",{"path":474,"title":475},"\u002Freset-wordpress-admin-password-using-wp-cli","Reset WordPress Admin Password Using WP-CLI",{"path":477,"title":478},"\u002Fgraphql-vs-rest-api-which-is-the-best-choice-for-headless-wordpress","GraphQL vs REST API: Which is the Best Choice for Headless WordPress?",{"path":480,"title":481},"\u002Fgrow-your-business-in-turkey-with-expert-wordpress-plugin-and-theme-localization-and-support-services","Grow Your Business in Turkey with Expert WordPress Plugin and Theme Localization and Support Services",{"path":483,"title":484},"\u002Fgetting-started-with-devops-understanding-the-principles-and-adopting-the-tools","Getting Started with DevOps: Understanding the Principles and Adopting the Tools",{"path":486,"title":487},"\u002Fphp-graphql-development-advanced-techniques-for-optimizing-your-apis","PHP GraphQL Development: Advanced Techniques for Optimizing Your APIs",{"path":489,"title":490},"\u002Fadvanced-techniques-for-dependency-injection-in-php-tips-code-samples-and-faqs","Advanced Techniques for Dependency Injection in PHP: Tips, Code Samples, and FAQs",{"path":492,"title":493},"\u002Fmaximize-the-potential-of-headless-wordpress-with-graphql","Maximize the Potential of Headless WordPress with GraphQL",{"path":495,"title":496},"\u002Fwriting-clean-modular-and-reusable-code-in-php","Best Practices for Writing Clean, Modular, and Reusable Code in PHP",{"path":498,"title":499},"\u002Fheadless-cmss-an-overview-of-popular-alternatives-to-contentful-and-wordpress","Headless CMSs: An Overview of Popular Alternatives to Contentful and WordPress",{"path":501,"title":502},"\u002Fci-cd-for-php-a-comprehensive-guide","CI\u002FCD for PHP: A Comprehensive Guide",{"path":504,"title":505},"\u002Fintroduction-to-php-namespaces-a-beginners-guide-to-structuring-your-code","Introduction to PHP Namespaces: A Beginner's Guide to Structuring Your Code",{"path":507,"title":508},"\u002Fwhat-is-graylog-a-powerful-tool-for-collecting-indexing-and-analyzing-log-data","What is Graylog? A Powerful Tool for Collecting, Indexing, and Analyzing Log Data",{"path":510,"title":511},"\u002Felevate-your-turkish-audience-experience-with-professional-wordpress-plugin-and-theme-translation","Elevate Your Turkish Audience Experience with Professional WordPress Plugin and Theme Translation",{"path":513,"title":514},"\u002Fhow-to-set-up-a-self-hosted-api-gateway-a-comprehensive-guide","How to Set Up a Self-Hosted API Gateway: A Comprehensive Guide",{"path":516,"title":517},"\u002Fdifference-between-generators-and-iterators-in-php","The Key Differences Between PHP Generators and Iterators",{"path":519,"title":520},"\u002Fphp-and-machine-learning-a-winning-combination-with-php-ml","PHP and Machine Learning: A Winning Combination with PHP-ML",{"path":522,"title":523},"\u002Fphp-generators-a-beginners-guide-to-iteration","PHP Generators: A Beginner's Guide to Iteration",{"path":525,"title":526},"\u002Fmastering-closures-in-javascript-a-beginners-guide","Mastering Closures in JavaScript: A Beginner's Guide",{"path":528,"title":529},"\u002Fthe-top-php-certification-programs-for-developers","The Top PHP Certification Programs for Developers",{"path":531,"title":532},"\u002Fhow-to-revalidate-next-js-isr-cache-on-demand-cache-regeneration","How to Revalidate Next.js ISR Cache? On-Demand Cache Regeneration",{"path":534,"title":535},"\u002Ftips-for-translating-a-wordpress-plugin-wordpress-theme-to-turkish","Tips for Translating a WordPress Plugin \u002F WordPress Theme to Turkish",{"path":537,"title":538},"\u002Fall-about-headless-wordpress","All About Headless WordPress",{"path":540,"title":541},"\u002Finstall-composer-on-ubuntu","How to Install Composer on Ubuntu \u002F Linux",{"path":443,"title":444},{"path":446,"title":447},{"path":545,"title":546},"\u002Fwhat-is-hateoas","What is HATEOAS?",{"path":548,"title":549},"\u002Fhello-world","Hello World: A New Multilingual Journey",{"path":551,"title":552},"\u002Fwhat-is-ecmascript","What is ECMAScript? What is not?",{"path":492,"title":493},[],[556,558,560],{"path":513,"title":514,"date":557},"2023-01-12",{"path":534,"title":535,"date":559},"2022-10-25",{"path":483,"title":484,"date":561},"2023-01-20",1782141982190]