[{"data":1,"prerenderedAt":352},["ShallowReactive",2],{"page:\u002Fsecurity":3},{"id":4,"title":5,"accent":6,"body":7,"coming":339,"description":340,"extension":341,"headline":342,"intro":343,"meta":344,"navigation":345,"path":346,"repos":347,"seo":348,"stats":349,"statsDetail":6,"statsNote":6,"stem":350,"__hash__":351},"content\u002Fsecurity.md","Security",null,{"type":8,"value":9,"toc":332},"minimark",[10,33,45,57,77,82,91,207,233,237,244,248,251,313,320,329],[11,12,16,30],"claim",{"emoji":13,"figure":14,"title":15},"🧱","3","Sandboxes Around PHP",[17,18,19,20,24,25,29],"p",{},"PHP runs as WebAssembly, so it can reach the outside only through the functions the host hands it.\nThe WebAssembly runs inside a V8 isolate. The isolate runs inside Cloudflare's process sandbox,\nwhich ",[21,22,23],"pitch",{},"uses Linux namespaces and seccomp to prohibit all access to the filesystem and network",",\nin Cloudflare's own words. There is no web server, PHP-FPM pool, SSH daemon or inbound socket in the\nsite's trust boundary, and the interpreter's ",[26,27,28],"code",{},"posix_spawnp"," aborts, so PHP has no process to start.",[17,31,32],{},"Cloudflare's security model page describes the outer layers. The Drupflare side is read from the\nshipping interpreter's glue and its list of imports.",[11,34,38],{"emoji":35,"figure":36,"title":37},"🗺️","12","Attack Classes Removed",[17,39,40,41,44],{},"We mapped 24 published vulnerabilities and incident classes, from Drupalgeddon to PHP-FPM, Shellshock,\ncontainer escapes and PHP2Shell. ",[21,42,43],{},"Twelve need a component a Drupflare site does not have",", four\nmore are contained by the sandbox, and eight work as they do on any host. The verdicts are inferred\nfrom the architecture, and the map, with advisories and sources, is in the docs.",[11,46,50],{"emoji":47,"figure":48,"title":49},"⏱️","\u003C24h","Cloudflare's Stated V8 Patch Gap",[17,51,52,53,56],{},"Cloudflare states that ",[21,54,55],{},"the Workers patch gap is under 24 hours",": the time from a published V8\nfix to its deployment across Workers. That covers the platform runtime. Drupal core and the PHP\ninterpreter are patched through Drupflare releases, which is a separate and slower path.",[11,58,62],{"emoji":59,"figure":60,"title":61},"⏪","30","Days of Point-in-Time Recovery",[17,63,64,65,68,69,76],{},"A deployed site is a SQLite-backed Durable Object, and ",[21,66,67],{},"it can be restored to any point in the\npast 30 days",", with an undo bookmark for the restore. An export dump leaves the platform for the\ncase where the object itself is gone. ",[70,71,75],"a",{"href":72,"rel":73},"https:\u002F\u002Fdocs.drupflare.com\u002Foperating\u002Frecovery.html",[74],"nofollow","Recovery"," says\nwhich of the four primitives answers which failure.",[78,79,81],"h2",{"id":80},"what-it-removes","🧭 What It Removes",[17,83,84,85,90],{},"Each row is an attack that needs something a Drupflare site does not contain. The full map has all\n24 rows, with advisories, dates and sources, on the\n",[70,86,89],{"href":87,"rel":88},"https:\u002F\u002Fdocs.drupflare.com\u002Fsecurity\u002Findex.html",[74],"security page of the docs",".",[92,93,94,110],"table",{},[95,96,97],"thead",{},[98,99,100,104,107],"tr",{},[101,102,103],"th",{},"Attack",[101,105,106],{},"Needs",[101,108,109],{},"On Drupflare",[111,112,113,125,135,146,156,167,178,193],"tbody",{},[98,114,115,119,122],{},[116,117,118],"td",{},"PHP-FPM behind nginx, CVE-2019-11043",[116,120,121],{},"A FastCGI process manager and a web server",[116,123,124],{},"Neither exists",[98,126,127,130,133],{},[116,128,129],{},"PHP-CGI on Windows, CVE-2024-4577",[116,131,132],{},"The CGI interface on Windows with Apache",[116,134,124],{},[98,136,137,140,143],{},[116,138,139],{},"Shellshock, CVE-2014-6271",[116,141,142],{},"Bash reached through CGI",[116,144,145],{},"No shell and no CGI",[98,147,148,151,154],{},[116,149,150],{},"Apache path traversal, CVE-2021-41773",[116,152,153],{},"Apache 2.4.49 and a filesystem to traverse",[116,155,124],{},[98,157,158,161,164],{},[116,159,160],{},"ImageMagick delegates, CVE-2016-3714",[116,162,163],{},"A shell for the delegate to call",[116,165,166],{},"No shell, and image work runs in JS",[98,168,169,172,175],{},[116,170,171],{},"Container and kernel escapes",[116,173,174],{},"A process on a shared Linux kernel",[116,176,177],{},"That kernel belongs to the platform",[98,179,180,183,190],{},[116,181,182],{},"PHP SoapServer use-after-free, CVE-2026-7261",[116,184,185,186,189],{},"The ",[26,187,188],{},"soap"," extension",[116,191,192],{},"Not compiled into the interpreter",[98,194,195,198,204],{},[116,196,197],{},"PHP2Shell, CVE-2026-109618 and CVE-2026-109619",[116,199,200,201],{},"A request startup and shutdown on every request, and an enforced ",[26,202,203],{},"memory_limit",[116,205,206],{},"Neither exists: PHP runs inside one retained request on the system allocator",[17,208,209,210,212,213,212,216,212,219,212,222,212,225,228,229,232],{},"The interpreter has a short extension list: ",[26,211,188],{},", ",[26,214,215],{},"phar",[26,217,218],{},"intl",[26,220,221],{},"gd",[26,223,224],{},"openssl",[26,226,227],{},"exif"," and\n",[26,230,231],{},"sqlite"," are not in it. An extension that is not compiled in has no bug for anyone to reach.",[78,234,236],{"id":235},"where-the-evidence-comes-from","🔎 Where the Evidence Comes From",[17,238,239,240,243],{},"Every claim on the ",[70,241,89],{"href":87,"rel":242},[74]," says how\nwe know it: read from a source, measured by a named test, or inferred from the architecture. Every\nrow ends as removed, contained or open, and a claim we could not establish counts as open. The sources\nare Cloudflare's security documentation, the PHP and Drupal advisories and NVD records. The docs also\ncover what the architecture leaves in place, such as application bugs and shared platform risk, so a\nreviewer can weigh both sides.",[78,245,247],{"id":246},"hardened-past-the-sandbox","🔐 Hardened Past the Sandbox",[17,249,250],{},"The sandbox stops most attacks before they start. The rest of the hardening assumes an attacker\nalready has a foothold in PHP and closes the ways to dig in or reach further. We rate 19 attack\nclasses before and after: 4 move from open to removed, 13 to contained, and 2 stay open.",[252,253,254,262,268,278,295,301,307],"ul",{},[255,256,257,261],"li",{},[258,259,260],"strong",{},"Your owner token can't be read from the database."," Only its SHA-256 is kept, in storage SQL\ncan't reach, and recovery rotates the token.",[255,263,264,267],{},[258,265,266],{},"Planted code gets caught."," Each module file and the interpreter's saved memory image are checked\nagainst sealed SHA-256 records before they run. A mismatch boots the site from the pack instead.",[255,269,270,273,274,277],{},[258,271,272],{},"Owner actions leave a trail."," Every call is written to a hash-chained log that\n",[26,275,276],{},"drangler audit --verify"," recomputes.",[255,279,280,283,284,212,287,290,291,294],{},[258,281,282],{},"Pages don't leak between visitors."," A page whose response varies by ",[26,285,286],{},"Accept",[26,288,289],{},"Origin"," or\n",[26,292,293],{},"User-Agent"," is never stored, and the security headers a module sets are replayed on every cache tier.",[255,296,297,300],{},[258,298,299],{},"PHP stays inside the box."," It can't open a raw socket or call a JavaScript global, and the\noutbound guard refuses private addresses in every spelling. A tenant can't switch it off.",[255,302,303,306],{},[258,304,305],{},"Installs are checked."," drupal.org archives are verified against their published digests before\nthey install, and a Content-Security-Policy baseline guards against framing (report-only by default\non paid sites).",[255,308,309,312],{},[258,310,311],{},"PHP2Shell is patched."," The interpreter runs PHP 8.5.12, which carries the fix.",[17,314,315,316,319],{},"Three things aren't covered yet, and the docs list them: a Drupal admin can remove the\nContent-Security-Policy baseline through the ",[26,317,318],{},"RESPONSE_HEADERS"," setting, a planted row on a read\nreplica lane isn't checked, and Packagist installs aren't digest-verified.",[17,321,322,323,328],{},"To report a vulnerability, use the private reporting link in the\n",[70,324,327],{"href":325,"rel":326},"https:\u002F\u002Fgithub.com\u002Fdrupflare\u002Fworker\u002Fsecurity\u002Fpolicy",[74],"security policy"," and not a public issue.",[330,331],"deploy-button",{},{"title":333,"searchDepth":334,"depth":334,"links":335},"",2,[336,337,338],{"id":80,"depth":334,"text":81},{"id":235,"depth":334,"text":236},{"id":246,"depth":334,"text":247},[],"What running Drupal in WebAssembly on Cloudflare Workers removes from the attack surface, and the evidence for each claim.","md","What Changes When Drupal Has No Server","A bug in Drupal still compromises a Drupal site. What changes is what the attacker gets next. On a conventional host that is a shell on an operating system. Here it is a PHP interpreter inside three sandboxes, with no operating system to explore.",{},true,"\u002Fsecurity",[],{"title":5,"description":340},[],"security","3yvKEA42aTx7IxlQCp8l3O3rSgqyO6SVMT7s2xX9DLs",1793683736804]