[{"data":1,"prerenderedAt":304},["ShallowReactive",2],{"blog-stories/admin-panel-found":3},{"id":4,"title":5,"body":6,"category":285,"date":286,"dateModified":286,"description":287,"draft":288,"extension":289,"faq":290,"featured":288,"headerVariant":285,"image":290,"keywords":290,"meta":291,"navigation":292,"ogDescription":293,"ogTitle":290,"path":294,"readTime":290,"schemaOrg":295,"schemaType":296,"seo":297,"sitemap":298,"stem":299,"tags":300,"twitterCard":302,"__hash__":303},"blog/blog/stories/admin-panel-found.md","When Someone Found a Health-Tech Startup's Unprotected Admin Panel",{"type":7,"value":8,"toc":277},"minimark",[9,16,19,24,30,33,37,40,43,59,62,91,95,98,109,112,118,122,125,164,170,194,198,201,221,224,246,269],[10,11,12],"tldr",{},[13,14,15],"p",{},"A health-tech startup left their admin panel at /admin with no authentication. Anyone who guessed the URL could view all patient data, modify settings, and delete accounts. A kind stranger found it and emailed the team before anything bad happened. They immediately added authentication, moved to a non-guessable URL, and implemented proper access controls.",[13,17,18],{},"The team at a small health-tech startup thought they were being clever by not linking to the admin panel from anywhere on the site. Security through obscurity, right? Wrong. Very, very wrong.",[20,21,23],"h2",{"id":22},"the-email-that-changed-everything","The Email That Changed Everything",[25,26,27],"story-block",{},[13,28,29],{},"\"Hi, I was poking around your site and noticed your admin panel at /admin doesn't require any login. I can see a list of all users with their email addresses, and there are buttons to delete accounts. You should probably fix this. I didn't touch anything, just wanted to let you know.\"",[13,31,32],{},"The founder's heart stopped. After rushing to check, there it was - the entire admin dashboard, completely exposed. User emails, subscription statuses, internal notes, delete buttons, everything. Zero authentication required.",[20,34,36],{"id":35},"how-did-this-happen","How Did This Happen?",[13,38,39],{},"The admin panel was built during a late-night coding session. The team planned to add authentication \"later.\" Later never came. They deployed it, it worked, and moved on to other features.",[13,41,42],{},"The thought process was dangerous:",[44,45,46,50,53,56],"ul",{},[47,48,49],"li",{},"\"Nobody knows the URL exists\"",[47,51,52],{},"\"We don't link to it anywhere\"",[47,54,55],{},"\"We'll add auth before we launch properly\"",[47,57,58],{},"\"It's just a small internal tool\"",[13,60,61],{},"But attackers don't need links. They have tools that scan for common paths like /admin, /dashboard, /manage, /internal, and hundreds of others.",[63,64,65,71],"warning-box",{},[13,66,67],{},[68,69,70],"strong",{},"What Was Exposed",[44,72,73,76,79,82,85,88],{},[47,74,75],{},"Complete user list with email addresses",[47,77,78],{},"User subscription and payment status",[47,80,81],{},"Internal notes the team had written about users",[47,83,84],{},"Ability to modify any user's account",[47,86,87],{},"Delete buttons that actually worked",[47,89,90],{},"System configuration settings",[20,92,94],{"id":93},"the-immediate-fix","The Immediate Fix",[13,96,97],{},"The team dropped everything and implemented authentication within the hour:",[99,100,105],"pre",{"className":101,"code":103,"language":104},[102],"language-text","// middleware/adminAuth.js\nexport function requireAdmin(req, res, next) {\n  const user = req.session?.user;\n\n  if (!user) {\n    return res.redirect('/login?redirect=/admin');\n  }\n\n  if (user.role !== 'admin') {\n    return res.status(403).send('Access denied');\n  }\n\n  next();\n}\n\n// routes/admin.js\nrouter.use(requireAdmin);  // Protect all admin routes\n","text",[106,107,103],"code",{"__ignoreMap":108},"",[13,110,111],{},"They also moved the admin panel to a randomly generated path that couldn't be guessed:",[99,113,116],{"className":114,"code":115,"language":104},[102],"// Instead of /admin\napp.use('/internal-mgmt-7f3k9x2m', adminRoutes);\n",[106,117,115],{"__ignoreMap":108},[20,119,121],{"id":120},"defense-in-depth","Defense in Depth",[13,123,124],{},"After the immediate panic subsided, the team implemented proper layered security:",[126,127,128,134,140,146,152,158],"ol",{},[47,129,130,133],{},[68,131,132],{},"Authentication",": Login required for all admin routes",[47,135,136,139],{},[68,137,138],{},"Authorization",": Role-based access control (RBAC)",[47,141,142,145],{},[68,143,144],{},"IP Restrictions",": Admin access only from office/VPN IPs",[47,147,148,151],{},[68,149,150],{},"Audit Logging",": Every admin action logged with user and timestamp",[47,153,154,157],{},[68,155,156],{},"Two-Factor Auth",": Required for all admin accounts",[47,159,160,163],{},[68,161,162],{},"Rate Limiting",": Prevent brute force on admin login",[99,165,168],{"className":166,"code":167,"language":104},[102],"// IP whitelist middleware for admin\nconst allowedIPs = process.env.ADMIN_ALLOWED_IPS?.split(',') || [];\n\nfunction ipWhitelist(req, res, next) {\n  const clientIP = req.ip || req.connection.remoteAddress;\n\n  if (!allowedIPs.includes(clientIP)) {\n    console.warn(`Admin access denied from ${clientIP}`);\n    return res.status(403).send('Access denied');\n  }\n\n  next();\n}\n",[106,169,167],{"__ignoreMap":108},[171,172,174],"lesson-box",{"title":173},"Key Lessons Learned",[44,175,176,179,182,185,188,191],{},[47,177,178],{},"Never rely on obscurity for security - attackers will find hidden URLs",[47,180,181],{},"Implement authentication BEFORE deploying any admin functionality",[47,183,184],{},"Use defense in depth - multiple layers of protection",[47,186,187],{},"Add audit logging to track who accesses sensitive areas",[47,189,190],{},"Regularly scan your own site for exposed endpoints",[47,192,193],{},"\"We'll add security later\" means \"We'll get hacked eventually\"",[20,195,197],{"id":196},"what-could-have-happened","What Could Have Happened",[13,199,200],{},"The startup got incredibly lucky. The person who found their admin panel was ethical. They could have:",[44,202,203,206,209,212,215,218],{},[47,204,205],{},"Downloaded the entire user database",[47,207,208],{},"Deleted all user accounts",[47,210,211],{},"Modified subscription statuses to steal service",[47,213,214],{},"Used the user list for phishing attacks",[47,216,217],{},"Sold the data on dark web forums",[47,219,220],{},"Held the company ransom for the data",[13,222,223],{},"Instead, they sent a polite email. The team owes them more than they can express.",[225,226,227,234,240],"faq-section",{},[228,229,231],"faq-item",{"question":230},"Is changing the admin URL to something random enough?",[13,232,233],{},"No. It's a useful additional measure, but never your only defense. URLs can leak through browser history, referer headers, or employee mistakes. Always combine with proper authentication.",[228,235,237],{"question":236},"How do attackers find hidden admin panels?",[13,238,239],{},"They use automated scanners that try thousands of common paths (/admin, /administrator, /manage, /backend, etc.). Some also use dictionaries of leaked paths from other sites or check JavaScript files for route definitions.",[228,241,243],{"question":242},"What's the minimum security for an admin panel?",[13,244,245],{},"At minimum: strong authentication (password + 2FA), proper authorization (role checks), HTTPS only, audit logging, and rate limiting. IP restrictions and non-standard URLs add extra layers.",[247,248,249,255,260,265,267],"related-articles",{},[250,251],"related-card",{"description":252,"href":253,"title":254},"After scanning 100 AI-generated projects, clear patterns emerged. Here are the most common vulnerabilities in vibe coded","/blog/stories/100-scans-lessons","What I Learned Scanning 100 Vibe Coded Projects",[250,256],{"description":257,"href":258,"title":259},"In early 2025, AI-assisted attackers compromised 50,000 FortiGate firewalls in weeks. Here's what happened and why it ma","/blog/stories/ai-assisted-fortigate-attack","How Attackers Used AI to Breach 50,000 FortiGate Firewalls",[250,261],{"description":262,"href":263,"title":264},"The emotional journey of dealing with security as a solo founder. The overwhelm, the near-surrender, and how I found a s","/blog/stories/almost-gave-up","Why I Almost Gave Up on Security",[250,266],{"description":257,"href":258,"title":259},[250,268],{"description":262,"href":263,"title":264},[270,271,274],"cta-box",{"href":272,"label":273},"/","Check Your Vibe Now",[13,275,276],{},"Scan your vibe coded projects for exposed admin panels and authentication issues.",{"title":108,"searchDepth":278,"depth":278,"links":279},2,[280,281,282,283,284],{"id":22,"depth":278,"text":23},{"id":35,"depth":278,"text":36},{"id":93,"depth":278,"text":94},{"id":120,"depth":278,"text":121},{"id":196,"depth":278,"text":197},"stories","2026-01-05","A stranger found a health-tech startup's admin panel at /admin with no authentication. They could see all patient data, modify settings, and delete accounts. How the team fixed it.",false,"md",null,{},true,"A stranger found a health-tech startup's admin panel at /admin with no authentication.","/blog/stories/admin-panel-found","[object Object]","BlogPosting",{"title":5,"description":287},{"loc":294},"blog/stories/admin-panel-found",[301],"Security Story","summary_large_image","T5ih40_kE66Y3nG1SvcdhUht2Zg8PV6XQOOvdWTKNSQ",1775843937677]