{"id":49859,"date":"2026-10-27T11:15:51","date_gmt":"2026-10-27T05:45:51","guid":{"rendered":"https:\/\/www.getastra.com\/blog\/?p=49859"},"modified":"2026-10-27T11:15:54","modified_gmt":"2026-10-27T05:45:54","slug":"cors-misconfiguration","status":"publish","type":"post","link":"https:\/\/www.getastra.com\/blog\/security-audit\/cors-misconfiguration\/","title":{"rendered":"CORS Misconfiguration Was the #1 Mistake in Every App We Tested"},"content":{"rendered":"<div class=\"gb-container gb-container-e43a8917\">\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Key_Takeaways\"><\/span>Key Takeaways<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The Security Risks with Vibe Coding: A study by Astra Security found CORS misconfiguration across all four AI-built applications tested.<\/li>\n\n\n\n<li>Permissive CORS settings can let malicious websites read sensitive data from authenticated users.<\/li>\n\n\n\n<li>Explicitly define allowed origins, credentials, methods, and headers in your AI coding prompts.<\/li>\n\n\n\n<li>Astra\u2019s autonomous pentesting helps identify and validate security misconfigurations before attackers exploit them.<\/li>\n<\/ul>\n\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">We gave four AI coding tools the same prompt and asked each one to build the same application. Emergent, Replit, Lovable, and Base44 all shipped something that ran, and none of them knew a pentest was coming. We then pointed our <a href=\"https:\/\/www.getastra.com\/reports\/vibe-coding-security-cost-study\">autonomous pentest agents at all four builds, and 137 confirmed vulnerabilities <\/a>came back.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One finding turned up in every app, which is a CORS misconfiguration. The builds looked fine from the outside, in the sense that they loaded, they logged users in, and they returned data on request. They were also telling browsers that other websites were welcome to read that data.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"977\" height=\"563\" src=\"\/cdn-cgi\/image\/quality=80,format=auto,onerror=redirect,metadata=none\/https:\/\/cdn-blog.getastra.com\/2026\/10\/ec780d64-image.png\" alt=\"Vulnerabilities in vibe coding apps\" class=\"wp-image-49856\" srcset=\"\/cdn-cgi\/image\/quality=80,format=auto,onerror=redirect,metadata=none\/https:\/\/storage.googleapis.com\/cdn-blog.getastra.com\/2026\/10\/ec780d64-image.png 977w, \/cdn-cgi\/image\/quality=80,format=auto,onerror=redirect,metadata=none\/https:\/\/storage.googleapis.com\/cdn-blog.getastra.com\/2026\/10\/ec780d64-image.png 399w\" sizes=\"auto, (max-width: 977px) 100vw, 977px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_is_CORS_and_what_counts_as_a_misconfiguration\"><\/span>What is CORS, and what counts as a misconfiguration?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">By default, your browser stops a script on one site from reading responses from another. That rule is the same-origin policy, and it is why a random page you open cannot quietly read your webmail in the next tab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CORS is how a server opts out of that rule for specific sites, by returning a header called <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Access-Control-Allow-Origin\" target=\"_blank\" rel=\"noopener\">Access-Control-Allow-Origin<\/a> that names who is allowed to read its responses. Used properly, it is how your frontend on <code>app.example.com <\/code>talks to your API on api[.]example[.]com.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A misconfiguration is when permission is handed out too widely, and it usually takes one of three forms. The first is a wildcard, where <code>Access-Control-Allow-Origin: * <\/code>tells the browser that every site on the internet qualifies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The second is a reflected origin, where the server reads whatever Origin the request arrived with and echoes it back as approved, which is a wildcard with extra steps. The third is loose subdomain matching, where a regex meant to allow <code>*.example.com <\/code>can be satisfied by an attacker who registers something like <code>notexample[.]com<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Any of these becomes dangerous once credentials are in play. If your API accepts cookies or auth headers and also tells the browser that the requesting origin is approved, you have handed an attacker&#8217;s page the ability to make authenticated requests as your logged-in user and read whatever comes back. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">OWASP files this under <a href=\"https:\/\/owasp.org\/API-Security\/editions\/2023\/en\/0xa8-security-misconfiguration\/\" target=\"_blank\" rel=\"noopener\">security misconfiguration<\/a> in its API Top 10, which is roughly where it belongs, since it is a setting rather than a bug.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Bad\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Credentials: true\n\n# Good\nAccess-Control-Allow-Origin: https:\/\/app.example.com\nAccess-Control-Allow-Credentials: true<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_we_found_across_the_four_apps\"><\/span>What we found across the four apps<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">All four tools received an identical prompt, with no advance notice, no vendor relationship, and no hint that anyone would be testing the output. Each of them built a working application, which is the part that makes this worth writing about. Nobody shipped something broken, and nobody would have caught this by using the app.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every finding went through the same pipeline before it counted. Two autonomous pentest agents worked the apps independently, one mapping the application and building attack chains, the other going in cold the way an unbriefed attacker would. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A validator agent then tried to reproduce every finding from scratch, and a human security engineer reviewed what survived, so anything nobody could reproduce was cut before analysis. 137 findings made it through that process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What makes this the most common finding in the study is that nobody wrote bad code to produce it. CORS is a configuration default, and the permissive setting is the one that makes development errors disappear fastest. Our prompt asked for a working API, said nothing about an origin allowlist, and so none of the four tools wrote one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_an_attacker_does_with_it\"><\/span>What an attacker does with it<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The attack is quieter than it sounds, which is part of why it ships. Your user is logged into your app in one tab, and in another tab they open a page the attacker controls, reached from an email, a comment, or an ad. Nothing about that page looks unusual to them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That page makes a request to your API in the background. Because the user has an active session, the browser attaches their cookies automatically. Your server checks the origin, decides it is allowed, and responds. The attacker&#8217;s script reads the response and sends it wherever it likes, with nothing brute-forced and no password changing hands, because the browser did exactly what your server told it was acceptable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">How bad this gets depends on what those endpoints return. Chained with <a href=\"https:\/\/www.getastra.com\/blog\/api-security\/broken-object-level-authorization-bola\/\">broken access control<\/a>, which was consistently the most severe failure in this study whenever it appeared, it stops being a configuration issue and becomes account-level data exposure. That combination is why we rated it the way we did rather than filing it as a hygiene problem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"How_to_fix_it\"><\/span>How to fix it<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">List your origins explicitly and never pair a wildcard with credentials. If you need to support more than one frontend, check the incoming origin against a hardcoded list and echo it back only on a match, and skip the clever regex, since most subdomain bypasses start with one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Limit your allowed methods and headers to what the frontend uses, and treat Origin as a browser-supplied hint rather than authentication, because anything outside a browser can set it to whatever it wants. The same thinking applies across the rest of your endpoints, which we covered in more depth in our guide to <a href=\"https:\/\/www.getastra.com\/blog\/api-security\/api-security-risks-and-how-to-mitigate-them\/\">API security risks<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are building with an AI tool, the fix happens in the prompt. Asking it to &#8220;set up CORS securely&#8221; gets you nothing, because that is an adjective and the tool will read it as a feature request. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tell it exactly what you want, which is to allow requests only from app.example.com, allow credentials, and reject everything else. Specific requirements separated the better builds from the worse ones across the whole study.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then check it yourself, which takes about thirty seconds. Send a request to your API with an Origin header you invented and look at what comes back in the response headers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>curl -I -H \"Origin: https:\/\/evil-test.example\" https:\/\/your-api.example.com\/api\/account<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the response echoes that origin back in <code>Access-Control-Allow-Origin<\/code>, or returns <code>* <\/code>alongside <code>Access-Control-Allow-Credentials: true<\/code>, you have the finding. Run it against every endpoint that returns user data, not only the one you remember configuring. That is one check out of the ten-minute version in the report.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Final_thoughts\"><\/span>Final thoughts<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">CORS is not an exotic vulnerability. It is a default that nobody changed, in four applications built by tools that did what they were asked to do. AI coding tools ship what you specify, and security tends to be the thing nobody specified.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you want this run against a real application instead of a demo build, that is what <a href=\"https:\/\/www.getastra.com\/autonomous-pentesting\">Astra&#8217;s autonomous pentest<\/a> does, using the validation pipeline described above. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"932\" src=\"\/cdn-cgi\/image\/quality=80,format=auto,onerror=redirect,metadata=none\/https:\/\/cdn-blog.getastra.com\/2026\/09\/879fc813-autonomous-astra-agents.png\" alt=\"Autonomous Astra agents\" class=\"wp-image-49133\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The other eight weak points, the full findings, and the hardened prompt are all in the <a href=\"https:\/\/www.getastra.com\/reports\/vibe-coding-security-cost-study\">free report<\/a>. There is also a ten-minute checklist in there for testing something you have already shipped.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Key Takeaways We gave four AI coding tools the same prompt and asked each one to build the same application. Emergent, Replit, Lovable, and Base44 all shipped something that ran, and none of them knew a pentest was coming. We then pointed our autonomous pentest agents at all four builds, and 137 confirmed vulnerabilities came &#8230; <a title=\"CORS Misconfiguration Was the #1 Mistake in Every App We Tested\" class=\"read-more\" href=\"https:\/\/www.getastra.com\/blog\/security-audit\/cors-misconfiguration\/\" aria-label=\"Read more about CORS Misconfiguration Was the #1 Mistake in Every App We Tested\">Read more<\/a><\/p>\n","protected":false},"author":100,"featured_media":49861,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[340],"tags":[],"class_list":["post-49859","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security-audit"],"_links":{"self":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/users\/100"}],"replies":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/comments?post=49859"}],"version-history":[{"count":1,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49859\/revisions"}],"predecessor-version":[{"id":49862,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/posts\/49859\/revisions\/49862"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/media\/49861"}],"wp:attachment":[{"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/media?parent=49859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/categories?post=49859"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.getastra.com\/blog\/wp-json\/wp\/v2\/tags?post=49859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}