[{"data":1,"prerenderedAt":442},["ShallowReactive",2],{"blog-vulnerabilities/csrf":3},{"id":4,"title":5,"body":6,"category":418,"date":419,"dateModified":419,"description":420,"draft":421,"extension":422,"faq":423,"featured":421,"headerVariant":427,"image":428,"keywords":428,"meta":429,"navigation":430,"ogDescription":431,"ogTitle":428,"path":432,"readTime":433,"schemaOrg":434,"schemaType":435,"seo":436,"sitemap":437,"stem":438,"tags":439,"twitterCard":440,"__hash__":441},"blog/blog/vulnerabilities/csrf.md","CSRF Explained: Cross-Site Request Forgery in Plain English",{"type":7,"value":8,"toc":400},"minimark",[9,16,21,24,27,46,50,55,70,74,83,93,97,157,161,165,168,177,227,231,234,243,247,256,260,269,273,332,341,369,388],[10,11,12],"tldr",{},[13,14,15],"p",{},"CSRF tricks logged-in users into unknowingly performing actions on your site. When a user visits a malicious page, it can submit forms or make requests to your site using the user's existing session cookies. Protect against CSRF with SameSite cookies, CSRF tokens, and by checking the Origin header on sensitive requests.",[17,18,20],"h2",{"id":19},"what-is-csrf","What Is CSRF?",[13,22,23],{},"Cross-Site Request Forgery (CSRF, pronounced \"sea-surf\") is an attack that forces authenticated users to perform unwanted actions. Unlike XSS where attackers inject code into your site, CSRF uses the victim's own browser to make legitimate-looking requests.",[13,25,26],{},"Here's a simple scenario:",[28,29,30,34,37,40,43],"ol",{},[31,32,33],"li",{},"You're logged into your banking website",[31,35,36],{},"You visit a malicious site (or one that's been hacked)",[31,38,39],{},"That site contains code that submits a money transfer request to your bank",[31,41,42],{},"Your browser automatically includes your banking cookies",[31,44,45],{},"The bank thinks you made the request and transfers the money",[17,47,49],{"id":48},"how-csrf-attacks-work","How CSRF Attacks Work",[51,52,54],"h3",{"id":53},"basic-form-attack","Basic Form Attack",[56,57,59],"code-block",{"label":58},"Malicious page that transfers money",[60,61,66],"pre",{"className":62,"code":64,"language":65},[63],"language-text","\u003C!-- On evil-site.com -->\n\u003Chtml>\n\u003Cbody onload=\"document.forms[0].submit()\">\n  \u003Cform action=\"https://yourbank.com/transfer\" method=\"POST\">\n    \u003Cinput type=\"hidden\" name=\"to\" value=\"attacker-account\" />\n    \u003Cinput type=\"hidden\" name=\"amount\" value=\"10000\" />\n  \u003C/form>\n\u003C/body>\n\u003C/html>\n\n\u003C!-- When a logged-in user visits this page,\n     their browser submits the form with their cookies -->\n","text",[67,68,64],"code",{"__ignoreMap":69},"",[51,71,73],{"id":72},"image-based-attack","Image-Based Attack",[56,75,77],{"label":76},"CSRF via image tag",[60,78,81],{"className":79,"code":80,"language":65},[63],"\u003C!-- GET requests can be triggered by images -->\n\u003Cimg src=\"https://yoursite.com/api/delete-account\" style=\"display:none\" />\n\n\u003C!-- Or change settings -->\n\u003Cimg src=\"https://yoursite.com/api/settings?email=attacker@evil.com\" />\n",[67,82,80],{"__ignoreMap":69},[84,85,86],"warning-box",{},[13,87,88,92],{},[89,90,91],"strong",{},"Why it works:"," Browsers automatically include cookies when making requests to a site, regardless of where the request originates. The server can't tell if the request came from your legitimate page or a malicious one.",[17,94,96],{"id":95},"real-world-csrf-consequences","Real-World CSRF Consequences",[98,99,100,113],"table",{},[101,102,103],"thead",{},[104,105,106,110],"tr",{},[107,108,109],"th",{},"Attack Target",[107,111,112],{},"Potential Damage",[114,115,116,125,133,141,149],"tbody",{},[104,117,118,122],{},[119,120,121],"td",{},"Email settings",[119,123,124],{},"Change recovery email, take over account",[104,126,127,130],{},[119,128,129],{},"Password change",[119,131,132],{},"Lock user out of their account",[104,134,135,138],{},[119,136,137],{},"Payment actions",[119,139,140],{},"Transfer money, make purchases",[104,142,143,146],{},[119,144,145],{},"Admin functions",[119,147,148],{},"Add attacker as admin, modify data",[104,150,151,154],{},[119,152,153],{},"Social actions",[119,155,156],{},"Post content, follow accounts, share data",[17,158,160],{"id":159},"how-to-prevent-csrf","How to Prevent CSRF",[51,162,164],{"id":163},"_1-use-samesite-cookies","1. Use SameSite Cookies",[13,166,167],{},"SameSite cookies won't be sent on cross-site requests:",[56,169,171],{"label":170},"Setting SameSite cookies",[60,172,175],{"className":173,"code":174,"language":65},[63],"// Express.js session with SameSite\napp.use(session({\n  cookie: {\n    httpOnly: true,\n    secure: true, // Requires HTTPS\n    sameSite: 'strict' // Or 'lax' for less strict protection\n  }\n}));\n\n// Next.js API route\nres.setHeader('Set-Cookie',\n  'session=abc123; HttpOnly; Secure; SameSite=Strict');\n",[67,176,174],{"__ignoreMap":69},[98,178,179,192],{},[101,180,181],{},[104,182,183,186,189],{},[107,184,185],{},"SameSite Value",[107,187,188],{},"Behavior",[107,190,191],{},"Use Case",[114,193,194,205,216],{},[104,195,196,199,202],{},[119,197,198],{},"Strict",[119,200,201],{},"Never sent cross-site",[119,203,204],{},"Maximum security",[104,206,207,210,213],{},[119,208,209],{},"Lax",[119,211,212],{},"Sent on top-level navigations only",[119,214,215],{},"Good balance (default in modern browsers)",[104,217,218,221,224],{},[119,219,220],{},"None",[119,222,223],{},"Always sent (requires Secure)",[119,225,226],{},"Third-party integrations",[51,228,230],{"id":229},"_2-use-csrf-tokens","2. Use CSRF Tokens",[13,232,233],{},"Include a unique token in forms that must match the server's expected value:",[56,235,237],{"label":236},"CSRF token implementation",[60,238,241],{"className":239,"code":240,"language":65},[63],"// Generate token and store in session\nconst csrfToken = crypto.randomUUID();\nreq.session.csrfToken = csrfToken;\n\n// Include in form\n\u003Cform action=\"/transfer\" method=\"POST\">\n  \u003Cinput type=\"hidden\" name=\"_csrf\" value=\"{csrfToken}\" />\n  \u003C!-- other fields -->\n\u003C/form>\n\n// Verify on submission\nif (req.body._csrf !== req.session.csrfToken) {\n  return res.status(403).json({ error: 'Invalid CSRF token' });\n}\n",[67,242,240],{"__ignoreMap":69},[51,244,246],{"id":245},"_3-check-origin-header","3. Check Origin Header",[56,248,250],{"label":249},"Origin header validation",[60,251,254],{"className":252,"code":253,"language":65},[63],"function validateOrigin(req, res, next) {\n  const origin = req.headers.origin || req.headers.referer;\n  const allowedOrigins = ['https://yoursite.com'];\n\n  if (origin && !allowedOrigins.some(o => origin.startsWith(o))) {\n    return res.status(403).json({ error: 'Invalid origin' });\n  }\n  next();\n}\n\n// Apply to state-changing routes\napp.post('/api/*', validateOrigin);\n",[67,255,253],{"__ignoreMap":69},[51,257,259],{"id":258},"_4-require-re-authentication-for-sensitive-actions","4. Require Re-authentication for Sensitive Actions",[56,261,263],{"label":262},"Re-authentication for critical actions",[60,264,267],{"className":265,"code":266,"language":65},[63],"// For sensitive actions, require password confirmation\napp.post('/api/change-email', async (req, res) => {\n  const { newEmail, currentPassword } = req.body;\n\n  // Verify current password before making changes\n  const isValid = await verifyPassword(req.user.id, currentPassword);\n  if (!isValid) {\n    return res.status(401).json({ error: 'Invalid password' });\n  }\n\n  // Now safe to change email\n  await updateUserEmail(req.user.id, newEmail);\n});\n",[67,268,266],{"__ignoreMap":69},[17,270,272],{"id":271},"csrf-protection-in-frameworks","CSRF Protection in Frameworks",[98,274,275,288],{},[101,276,277],{},[104,278,279,282,285],{},[107,280,281],{},"Framework",[107,283,284],{},"Built-in Protection",[107,286,287],{},"How to Enable",[114,289,290,301,311,322],{},[104,291,292,295,298],{},[119,293,294],{},"Next.js",[119,296,297],{},"None by default",[119,299,300],{},"Use next-csrf or similar package",[104,302,303,306,308],{},[119,304,305],{},"Express",[119,307,297],{},[119,309,310],{},"Use csurf middleware",[104,312,313,316,319],{},[119,314,315],{},"Django",[119,317,318],{},"Enabled by default",[119,320,321],{},"Include {% csrf_token %} in forms",[104,323,324,327,329],{},[119,325,326],{},"Rails",[119,328,318],{},[119,330,331],{},"protect_from_forgery included",[333,334,335],"success-box",{},[13,336,337,340],{},[89,338,339],{},"Modern browsers help:"," Modern browsers default to SameSite=Lax for cookies without explicit SameSite setting, which prevents most CSRF attacks on POST requests.",[342,343,344,351,357,363],"faq-section",{},[345,346,348],"faq-item",{"question":347},"What is CSRF?",[13,349,350],{},"CSRF (Cross-Site Request Forgery) is an attack that tricks a logged-in user into unknowingly submitting a request to a website. Because the user's browser automatically includes cookies, the site thinks the request is legitimate.",[345,352,354],{"question":353},"Do SameSite cookies prevent CSRF?",[13,355,356],{},"SameSite cookies provide good protection against CSRF. Setting SameSite=Strict prevents cookies from being sent on cross-site requests. SameSite=Lax allows cookies on top-level navigations but blocks them on cross-site POST requests.",[345,358,360],{"question":359},"Does using JSON APIs prevent CSRF?",[13,361,362],{},"JSON APIs have some natural CSRF protection because browsers won't send JSON in cross-origin requests without CORS approval. However, if your API accepts form-encoded data or has permissive CORS settings, it may still be vulnerable.",[345,364,366],{"question":365},"What's the difference between CSRF and XSS?",[13,367,368],{},"XSS injects malicious code into your website that runs in users' browsers. CSRF tricks users' browsers into making requests to your site. XSS exploits trust the user has in your site; CSRF exploits trust your site has in the user's browser.",[370,371,372,378,383],"related-articles",{},[373,374],"related-card",{"description":375,"href":376,"title":377},"Step-by-step guide","/blog/how-to/implement-csrf-protection","Implement CSRF Protection",[373,379],{"description":380,"href":381,"title":382},"Related vulnerability","/blog/vulnerabilities/xss","XSS Explained",[373,384],{"description":385,"href":386,"title":387},"Cookie security issues","/blog/vulnerabilities/insecure-cookies","Insecure Cookies",[389,390,393,397],"cta-box",{"href":391,"label":392},"/","Start Free Scan",[17,394,396],{"id":395},"check-your-csrf-protection","Check Your CSRF Protection",[13,398,399],{},"Our scanner tests your forms and APIs for CSRF vulnerabilities.",{"title":69,"searchDepth":401,"depth":401,"links":402},2,[403,404,409,410,416,417],{"id":19,"depth":401,"text":20},{"id":48,"depth":401,"text":49,"children":405},[406,408],{"id":53,"depth":407,"text":54},3,{"id":72,"depth":407,"text":73},{"id":95,"depth":401,"text":96},{"id":159,"depth":401,"text":160,"children":411},[412,413,414,415],{"id":163,"depth":407,"text":164},{"id":229,"depth":407,"text":230},{"id":245,"depth":407,"text":246},{"id":258,"depth":407,"text":259},{"id":271,"depth":401,"text":272},{"id":395,"depth":401,"text":396},"vulnerabilities","2026-01-15","CSRF tricks users into performing unwanted actions on sites where they're logged in. Learn how CSRF attacks work and how to protect your app with tokens and SameSite cookies.",false,"md",[424,425,426],{"question":347,"answer":350},{"question":353,"answer":356},{"question":359,"answer":362},"red",null,{},true,"Learn how CSRF attacks trick users and how to protect your application.","/blog/vulnerabilities/csrf","8 min read","[object Object]","TechArticle",{"title":5,"description":420},{"loc":432},"blog/vulnerabilities/csrf",[],"summary_large_image","TVJdcA8iusIC_4yoiMRZ-uS8-ESzMR3fiOFlfBLbC9Q",1775843926833]