In short
- External means the tester starts on the internet with no access. Internal means they start inside your network, with a stolen laptop or VPN login. A team with no office network rarely needs the classic internal kind.
- On Supabase, Firebase or Vercel, the external surface is mostly the app and its backend: the public key in the JavaScript, database policies and rules, storage buckets, API routes and forgotten subdomains.
- On a VPS, Docker publishes container ports on every address by default, and that traffic skips ufw rules. A database can face the internet while the firewall says the port is closed.
- You can see much of what a tester sees on your own domain in minutes: certificate transparency logs, your DNS records, your JavaScript, and a port scan of your own server from another network.
- Scope every domain, backend project and server, sign a written authorization, read your providers' testing rules, and test the app layer again on every deploy.
On this page
- What an external pentest is
- External vs internal pentest
- What an external pentest covers on a modern app
- How a tester finds what you expose, and how to look first
- Typical external findings in apps built with AI
- Fixing what the outside view shows
- When does an internal pentest make sense?
- How to scope and prepare an external pentest
- How often to run an external pentest
- What Keybrum tests from the outside
- Questions
- Sources
What an external pentest is
An external penetration test, or external pentest, is an authorized attack on what your company exposes to the internet, run by someone who starts with no access. NIST defines external security testing as testing conducted from outside the organization's security perimeter. The tester starts where a stranger starts: your domain name, and whatever can be found from it.
The classic version targets an office: public IP ranges, a firewall, a VPN gateway, a mail server. For a team that ships a web app on Supabase, Firebase, Vercel or a small VPS and has no office network, the app, its backend and its server are close to the whole attack surface.
Most external tests are black box: a list of domains, no accounts, no code. That misses everything behind the login, so add test accounts (grey box), as the web app pentest guide explains. Handing over the code too makes it a white box test.
External vs internal pentest
External pentest: the tester starts on the internet with no access and attacks what anyone can reach: domains, apps, APIs, backend services and open ports. It answers one question: what can a stranger do to us today?
Internal pentest: the tester starts inside your network, as if an attacker already had a foothold: a compromised laptop, a stolen VPN login, someone in the building. This starting point is called assume breach, and it answers a different question: once someone is in, how far can they go?
| External pentest | Internal pentest | |
|---|---|---|
| Starting point | The internet, with no account | Inside the network: a staff laptop, a VPN account, an office port |
| Targets | Domains, the web app, APIs, the backend, storage, DNS and email records, server ports | Workstations, file shares, internal servers, Active Directory, network devices |
| Typical findings | Tables open to the public key, secrets in the JavaScript, a served .env file, dangling subdomains, an exposed database or admin console | Weak or reused passwords, unpatched internal servers, a path from a normal account to domain admin |
| Who needs it | Anyone with something on the internet, including every SaaS app | Companies with an office network, a VPN, Active Directory or servers on a private network |
What an external pentest covers on a modern app
The external surface of a typical app built with Lovable, Bolt, v0, Cursor or Claude Code:
| Asset | What the tester checks |
|---|---|
| Subdomains | Every name under your domain, from certificate transparency logs and DNS: staging, admin, a preview build wired to the production database |
| The web app | The HTML and JavaScript every visitor downloads: keys, backend URLs, table names, routes, source maps, security headers |
| The backend as a service | Supabase tables, functions and storage reached with the public key, where row level security decides (Supabase anon key); Firestore, Realtime Database and Storage, where security rules decide (Firebase security rules) |
| APIs | Your routes, Server Actions, AI endpoints and webhooks: what answers without a session, or answers the wrong user |
| Storage | Public buckets holding private files, or listing their contents to anyone (Supabase storage security) |
| Exposed files | A served .env file, .git folder or backup archive (exposed .env files) |
| DNS and email records | CNAME records pointing at deleted services, and SPF and DMARC records, which tell mail servers what to do with mail forging your domain |
| The origin server | On a VPS: every port that answers from the internet (databases, Redis, the Docker API, admin consoles, dev servers) and whether the server answers directly, around your CDN |
Two pieces are easy to forget. Your Supabase database and its pooler accept connections from any IP address until you set Network Restrictions in the database settings; until then, only the password stands in the way. On Vercel, every deployment gets its own URL: Standard Protection, on all plans, protects every domain except production ones, so turn it on before a preview with real data lands in a chat.
How a tester finds what you expose, and how to look first
A tester starts with discovery, or reconnaissance: listing everything that is yours before attacking any of it. Most of it is public, so you can look first. Run these checks against your own domain and your own server only.
- 1
Search certificate transparency logs for your domain
Every certificate a public authority issues goes into Certificate Transparency logs, which are append-only: entries are never deleted or changed. Every hostname that ever had its own certificate is there, even last year's staging site. Search
%.yourdomain.comon crt.sh, a free search over those logs. A wildcard certificate (*.yourdomain.com) hides the names under it. - 2
List your DNS records
Export the zone from your DNS provider. Every
Arecord is a server, everyCNAMEa service you depend on: is each one still yours, and still in use? - 3
Look at what your JavaScript gives away
In your browser's developer tools, search the loaded scripts for
supabase.co,firebase,apiKeyandsk_. Whatever you find, a stranger finds too. - 4
Check which ports your server answers on
If you run a VPS, scan your own server's public IP from another network, such as your laptop on a mobile connection; a scan from the server itself skips the path outside traffic takes. Expect 22, 80 and 443. Anything else needs a reason.
# Your own domain only. Replace yourdomain.com.
dig +short yourdomain.com A
dig +short www.yourdomain.com CNAME
dig +short yourdomain.com MX
dig +short yourdomain.com TXT # SPF lives here
dig +short _dmarc.yourdomain.com TXT # your DMARC policy, if anyCNAME that points at a Heroku app, a GitHub Pages site or a storage bucket you deleted is a candidate for subdomain takeover. An empty _dmarc answer means you publish no DMARC policy.# From a machine outside your server's network, against your own server's IP only.
# -p- checks every TCP port, 1 to 65535. It can take several minutes.
nmap -p- YOUR_SERVER_IP
# On the server itself: which services listen, and on which address.
sudo ss -tlnp
docker ps --format "table {{.Names}}\t{{.Ports}}"ss, a service on 0.0.0.0, * or [::] listens on every address, one on 127.0.0.1 only on the server itself. In docker ps, 0.0.0.0:5432->5432/tcp means Postgres is published on every address.Typical external findings in apps built with AI
Two public studies give a sense of scale. In October 2025, Escape's research team reported analyzing more than 5,600 publicly available apps built on vibe coding platforms and finding more than 2,000 vulnerabilities, over 400 exposed secrets and 175 instances of personal data, including medical records and IBANs. Earlier that year, the researcher behind CVE-2025-48757 found 170 of 1,645 Lovable projects, about one in ten, exposing data through row level security settings, from their home pages alone. Lovable disputes the CVE: it says each customer is responsible for their own app's data.
- Tables open to the public key. A Supabase table with RLS off, or Firebase rules that let anyone read. The key is meant to be public; the policy is what failed.
- Secret keys in the JavaScript. An OpenAI, Stripe or Supabase service role key that reached the bundle through a
NEXT_PUBLIC_orVITE_variable (API keys exposed in the frontend). - Dangling subdomains. Whoever claims the deleted service a
CNAMEpoints at serves their content on your subdomain; OWASP lists phishing, malicious content and stolen session cookies among the possible attacks. - A database or the Docker API open to the internet. Postgres, MySQL, MongoDB or Redis published by a container is open to password guessing and to the next flaw in that software. An unsecured Docker API on port 2375 is worse: Docker's documentation warns that remote non-root users can gain root access on the host.
- Dev servers, admin consoles and the origin: a Vite or Next.js dev server left on 5173 or 3000, pgAdmin or Portainer reachable by IP, or the server behind your CDN answering on its own address.
Fixing what the outside view shows
Most external findings on a small stack have short fixes. Start with the server: its holes are the hardest to notice from inside.
# Before: published on every address of the server, and ufw does not filter it
services:
db:
image: postgres:17
ports:
- "5432:5432"
# After: reachable only from the server itself
services:
db:
image: postgres:17
ports:
- "127.0.0.1:5432:5432"- Bind internal services to localhost, as above. Firewall rules for published ports belong in the
DOCKER-USERchain, Docker's documentation says, since ufw never sees that traffic. - Close the origin: behind a CDN, accept web traffic only from the CDN's address ranges, and remove DNS records that reveal the server.
- Delete dangling DNS records, which is OWASP's remedy for subdomain takeover, and keep checking.
- Publish SPF and DMARC records for every domain you own, including domains that send no mail, and add the security headers the app lacks.
- Fix the app layer at the source: policies from the Supabase RLS guide, rules from the Firebase guide above, and a new key for any secret that reached the browser.
When does an internal pentest make sense?
When there is an inside to attack: an office network, a VPN to internal tools, Active Directory controlling staff accounts and machines, or servers on a private network. Book one too when a customer, an auditor or a standard requires it; if you take card payments on your own systems, read PCI DSS penetration testing.
A fully remote team on Supabase, Vercel and GitHub has no office network. Its inside is a set of accounts and tokens, and the cloud-only equivalent of an internal test is a cloud configuration review: someone reads your setup from the inside and asks what one stolen login or leaked token could reach.
- Who is an owner or admin on your backend, Vercel, GitHub, your DNS provider and your registrar, and does each account use multi-factor authentication?
- Where do the service role key, the Stripe secret and the database password live: CI, hosting variables, laptops, an old
.envin Git history? - Are policies and rules right on every table and collection, not only the ones the app calls? Reading them all is what a white box test does.
How to scope and prepare an external pentest
- List what is in scope: every domain and subdomain, the backend projects behind them (Supabase project ref, Firebase project ID), API hosts and the public IP of every server. The certificate transparency search catches forgotten names.
- Say what is out of scope: providers you use but do not run, such as payments, email or auth. You cannot authorize an attack on them.
- Sign a written authorization naming the targets, the dates, the tester's source IP addresses and what they may write or delete. Some testers and tools also ask you to prove you control each domain, for example with a DNS TXT record.
- Read your providers' rules. Supabase, AWS and Google Cloud let you test your own resources without asking first, within their policies; Supabase treats testing outside its policy as testing against Supabase. Vercel allows tests of your own app on Pro and Enterprise plans, not Hobby, and never against the platform.
- Create test accounts: two per role, with data you can lose.
- Back up and name a contact: take a fresh database backup, tell your team the dates, and give the tester someone to call if something breaks.
- Choose staging or production. Production shows the real DNS, keys and open ports. Staging is safer for tests that write data, but only speaks for production if it runs the same build, rules and server setup.
- Ask for proof: each finding should come with the request that worked. A list of open ports with CVE numbers and no proof is a scan (pentest vs vulnerability scan).
How often to run an external pentest
The outside of an app changes fast: a new subdomain, a moved server, or a coding agent adding a table or rewriting a route in one deploy. A yearly test describes one day of that.
- A full external test before a big launch or an enterprise deal, and after major changes: a new domain, a server move, a new backend.
- The app layer on every deploy: tables and rules, keys in the bundle, exposed files and API routes change with the code, so they need automated attacks on each release.
- DNS whenever it changes: remove the record the day you shut a service down.
The penetration testing methodology guide walks through the phases of a full test, and penetration testing cost covers what to budget for one.
What Keybrum tests from the outside
Keybrum runs the external checks in this guide as attacks on apps built with AI tools. It starts from your app's address, as a stranger would, and attacks nothing until you prove you own the app with a DNS record, a tag in your page or a file on your site.
- The app and its backend: more than 100 attacks on Supabase tables, RPC, storage and realtime, Firebase rules, secrets in the bundle, exposed
.envfiles, security headers, unprotected AI endpoints, unsigned Stripe webhooks, and Next.js and React CVEs proved by behaviour. With two test accounts, it sends user B after user A's data. - Your DNS: it reads the
CNAMErecords of your domain and common subdomains, and reports a takeover risk only when the provider's own page says the resource is unclaimed, without claiming it. - Your server: the addresses your DNS points to, plus any you add, each probed only after Keybrum confirms it serves your app. It sends each database port a first message without a password, asks the Docker and etcd APIs for their version, requests the home page of ports apps keep internal, and checks whether the server answers around your CDN.
- Shared platforms: on Vercel, Netlify, Lovable and similar hosts, the servers are the platform's, so Keybrum leaves their ports alone and says so in the report.
Your coding agent reads each finding and its fix through Keybrum's MCP server. Office networks, VPNs and Active Directory are outside what Keybrum attacks: for those, book a human internal test.
Questions
What is the difference between an internal and an external pentest?
An external pentest starts on the internet with no access and attacks what anyone can reach: domains, apps, APIs and open ports. An internal pentest starts inside your network, as if an attacker already controlled a laptop or a VPN account, and measures how far they can move. Most SaaS companies need the external kind first.
Does a fully remote startup need an internal pentest?
Usually not in the classic sense, because there is no office network or Active Directory to attack. The useful equivalent is a cloud configuration review: who has admin access to your backend, hosting, GitHub and DNS, where secret keys live, and what one stolen token could reach.
Is an external pentest the same as a vulnerability scan?
No. A scan lists open ports and software versions that might be vulnerable. An external pentest tries each lead and proves which ones are real, with the request that worked.
Does an external pentest include the web application?
Not always. Some external network tests focus on ports and services and only glance at the apps they find; Intruder, for one, recommends choosing a provider that includes web application testing of publicly accessible apps. For a SaaS product, the app and its backend are most of the surface, so name them in the scope.
Can I run an external pentest on an app hosted on Vercel or Supabase?
On your own app, yes, within each provider's rules. Supabase lets customers test their own project without prior approval for the services its policy lists. Vercel allows it on Pro and Enterprise plans, not Hobby, and never against the platform itself.
How often should you do an external pentest?
Run a full test before big launches and after major changes, such as a new domain, a server move or a new backend. Test the app layer on every deploy, since each release can reopen a hole.
Sources
- 1.Glossary: external security testing, NIST CSRC
- 2.Glossary: internal security testing, NIST CSRC
- 3.Packet filtering and firewalls (Docker and ufw), Docker Docs
- 4.Docker with iptables (the DOCKER-USER chain), Docker Docs
- 5.Port publishing and mapping, Docker Docs
- 6.Configure remote access for Docker daemon, Docker Docs
- 7.Networking in Compose, Docker Docs
- 8.Test for Subdomain Takeover (WSTG-CONF-10), OWASP
- 9.How Certificate Transparency works, Certificate Transparency
- 10.Network Restrictions, Supabase Docs
- 11.Penetration testing and security testing policy, Supabase Docs
- 12.Penetration testing on Vercel, Vercel
- 13.Deployment Protection, Vercel Docs
- 14.Penetration Testing policy, Amazon Web Services
- 15.Google Cloud security FAQ: penetration testing, Google Cloud
- 16.RFC 7489: Domain-based Message Authentication, Reporting, and Conformance (DMARC), IETF
- 17.Port specification and scan order, Nmap Reference Guide
- 18.Methodology: how we discovered over 2k high-impact vulnerabilities in apps built with vibe coding platforms, Escape
- 19.Statement on CVE-2025-48757, Matt Palmer
- 20.CVE-2025-48757, NIST National Vulnerability Database
- 21.What is an external pentest and how is it carried out?, Intruder