Why we may need to stop exposing half our application to the Internet and hoping Cloudflare saves us

I’ve been building software for a long time, and one thing I’ve learned is that software developers are exceptionally good at creating security problems for our future selves.

We build an API.

Then somebody needs a slightly different version of the API.

Then the mobile app needs one.

Then marketing needs an endpoint.

Then Dave creates /api/v2-temp/ because changing the existing endpoint might break something.

Dave leaves the company.

Nobody knows whether v2-temp is still being used, so nobody deletes it.

Three years later it’s still sitting there happily accepting requests from the Internet.

This was already a questionable way of building systems.

AI is about to make it considerably more questionable.

Because the attacker looking at /api/v2-temp/ is increasingly unlikely to be some bloke manually typing things into Burp Suite at 2am.

It can be software.

Software that doesn’t get tired, doesn’t get bored, remembers everything it discovers and can reason about what to try next.

And that’s the part I think we’re underestimating.


AI Doesn’t Need to Become a Super Hacker

When people talk about AI and cybersecurity, the conversation tends to become ridiculous quite quickly.

You get visions of some digital superintelligence breaking AES encryption while simultaneously shutting down the Pentagon.

That’s not the threat I’m particularly worried about.

I’m worried about something much more boring.

An endlessly patient junior penetration tester.

Give a modern AI agent access to a website and increasingly it can do something remarkably similar to what a human tester would do.

Open the site.

Create an account.

Click around.

Inspect the JavaScript.

Watch the network requests.

Work out how authentication works.

Discover the API.

Change some parameters.

Observe the response.

Form a hypothesis.

Try something else.

Repeat.

Except it can potentially do this against thousands of applications.

At 3am.

On Christmas Day.

While we’re asleep after confidently telling ourselves that the WAF has it covered.

That’s the shift.

AI doesn’t necessarily make individual exploits dramatically more powerful.

It makes curiosity cheap.

And curiosity is surprisingly dangerous.


Imagine an AI Walking Through Your Application

Imagine our agent creates an account on an e-commerce site.

It buys something.

It visits its order history.

The browser requests:

GET /api/orders/58382

The agent notices the number.

A human penetration tester immediately knows what they’re going to try next.

So does the AI.

GET /api/orders/58383

Fine.

Try 58381.

Try another endpoint.

Maybe:

/api/invoices/58383

Interesting.

Different response.

Now investigate why.

Maybe nothing happens.

Hopefully nothing happens.

But perhaps six hours later it discovers that one obscure endpoint checks whether you’re authenticated but doesn’t actually check whether the invoice belongs to you.

Congratulations.

You’ve invented a data breach.

The vulnerability wasn’t particularly clever.

The clever bit was having something willing to explore the application indefinitely until it found it.


We’ve Built Enormous Front Doors

Look at the architecture of many modern applications.

It roughly resembles this:

Internet
   |
   v
Cloudflare / CDN / WAF
   |
   v
Web Application
   |
   v
Absolutely Bloody Enormous API
   |
   +---- Users
   +---- Orders
   +---- Search
   +---- Files
   +---- Reports
   +---- Payments
   +---- Admin
   +---- Legacy API
   +---- Mobile API
   +---- GraphQL
   +---- Something nobody remembers
   |
   v
Internal Services
   |
   v
Database containing everything we care about

And our security strategy is often essentially:

Put a very clever bouncer in front of it.

WAF.

Rate limiting.

Bot detection.

Authentication.

IP reputation.

All useful.

But perhaps we’re asking the wrong question.

Instead of asking:

How do we build a better guard for this enormous entrance?

Maybe we should ask:

Why is the entrance so bloody enormous?


Make the Hole Smaller

This is the idea I keep coming back to.

If AI makes exploring applications dramatically cheaper, perhaps applications should expose dramatically less territory to explore.

Instead of exposing our backend architecture to clients, expose a tiny set of intentional capabilities.

For example:

login
searchProducts
viewProduct
createOrder
viewOrder
cancelOrder
updateProfile

That’s it.

Behind that interface there might be:

  • 40 microservices;
  • six databases;
  • three queues;
  • an ancient .NET service nobody wants to touch;
  • something written in Node that everyone denies responsibility for;
  • and a SQL Server called PROD-SQL-OLD-DO-NOT-DELETE.

Fine.

The Internet doesn’t need to know.

The browser doesn’t need to know.

And the AI wandering around our application definitely doesn’t need to know.


Secure by Vocabulary

I’ve started thinking about this as secure by vocabulary.

Give the untrusted world a very small language with which it can communicate with your application.

Every word has a specific meaning.

Every operation is intentional.

Every parameter has a known shape.

Everything else gets rejected.

So instead of:

/api/users/{id}
/api/orders/query
/api/files/{path}
/api/reports/run
/graphql

we expose concepts such as:

viewMyOrder
cancelMyOrder
downloadMyInvoice
changeMyAddress

There’s an important philosophical difference here.

We’re no longer exposing how the system works.

We’re exposing what the user is permitted to do.

That gives an attacker considerably less vocabulary with which to ask dangerous questions.


Authentication Has Been Doing Far Too Much Emotional Labour

We also need to stop treating authentication like some magical security force field.

A JWT does not mean:

This person may now roam freely through the kingdom.

It means:

We have some confidence about who this person is.

That’s all.

Every important request still needs to answer:

WHO are you?

WHAT are you trying to do?

WHICH resource are you trying to do it to?

DO you actually own/have access to that resource?

ARE you allowed to perform this action?

DOES this operation make sense right now?

That sounds obvious.

And yet authorization bugs continue to exist because somewhere deep inside an application somebody eventually writes:

if (user != null)
{
    return invoice;
}

Future incident report:

“The application correctly verified that the attacker was, in fact, a person.”

Excellent.


Capability-Based Security Starts Looking Very Attractive

This leads naturally toward capability-based authorization.

Instead of:

Authenticated
     |
     v
    API

think:

Identity
   |
   v
Session
   |
   v
Specific Capabilities

Perhaps the session can perform:

profile.update:self

order.read:58382

order.cancel:58382

invoice.download:58382

The implementation isn’t the important bit.

The principle is.

Access to the application should not imply broad authority within the application.

And this has another useful property.

If somebody steals a session, what have they stolen?

Ideally:

A small collection of narrowly scoped capabilities.

Not:

The keys to Mordor.


The AI Attacker Doesn’t Need to Look Like an Attacker

This is where things become particularly interesting.

A lot of security technology is built around detecting obviously malicious behaviour.

Someone fires 10,000 requests at the server.

Block them.

Someone sends SQL injection payloads.

Block them.

Known malicious IP?

Block it.

Impossible user agent?

Block it.

Fine.

But an AI doesn’t have to behave like that.

It can send one request.

Wait.

Click another page.

Wait.

Create another perfectly valid request.

Keep the cookies.

Execute JavaScript.

Obtain the CSRF token.

Use the real browser.

Take lunch.

It doesn’t actually need lunch, obviously, but perhaps it waits anyway because our behavioural detection system thinks humans become hungry around noon.

The attack can look remarkably normal.

Which means individual requests become less useful as a security signal.

Behaviour becomes the signal.


One Innocent Request at a Time

Consider this sequence.

Login



View order



View another order



Change an identifier



Receive 403



Change another identifier



Receive 404



Try invoice endpoint



Change account context



Repeat

Every HTTP request could be completely valid.

There’s no SQL injection.

No shell command.

No obviously malicious payload.

But step back and look at the session.

Someone is mapping the application.

That means our security systems increasingly need to understand sequences rather than individual requests.

Who is doing this?

What resources are they touching?

How quickly?

In what order?

How often are authorization boundaries being tested?

What changed after the last failed request?

That’s a much richer security signal than:

IP 82.x.x.x sent 73 requests.

Wonderful.

So did everyone else.


Fight AI With AI — But Don’t Be Stupid About It

The obvious conclusion is that defenders should use AI too.

I agree.

With one very important qualification.

I don’t want an LLM deciding whether somebody gets access to my production database.

I don’t particularly want:

GPT thinks Neil probably looks trustworthy.

as an authorization policy.

LLMs are probabilistic systems.

Authorization should be boring.

Boring is good.

identity = valid

capability = order.read

resource = 58382

owner = current_user

ALLOW

Beautiful.

Predictable.

Testable.

Auditable.

AI belongs alongside that system.

It watches.

It correlates.

It notices unusual behaviour.

It says:

This account has attempted 19 operations against resources it doesn’t own and changed its navigation pattern immediately after each authorization failure.

Now that’s useful.

The AI is an investigator.

It isn’t the judge.


The Security Gateway I’d Like to See

If I were designing an application specifically for this threat model, the Internet-facing architecture might look something like this:

                    INTERNET
                       |
                       v
               +----------------+
               | Edge Protection|
               +-------+--------+
                       |
                       v
               +----------------+
               | Identity       |
               +-------+--------+
                       |
                       v
               +----------------+
               | Strict Schema  |
               | Validation     |
               +-------+--------+
                       |
                       v
               +----------------+
               | Capability     |
               | Authorization  |
               +-------+--------+
                       |
                       v
               +----------------+
               | Behaviour /    |
               | Risk Analysis  |
               +-------+--------+
                       |
                ALLOW / DENY
                       |
                       v
              Application Broker
                       |
                TRUST BOUNDARY
                       |
           +-----------+-----------+
           |           |           |
           v           v           v
        Service     Service     Service
                       |
                       v
                      Data

The critical component isn’t the AI.

It’s that tiny waist in the architecture.

The Internet gets a deliberately constrained interface.

Everything interesting sits behind it.


Don’t Solve Bad Architecture With A Bigger AI

There is another possible future.

And knowing this industry, we’ll probably try it first.

Attacker AI
     |
     v
£400,000 Enterprise Security AI
     |
     v
7,000 endpoints nobody understands

Then everyone attends a webinar explaining why we need the £700,000 version.

I’d rather fix the architecture.

Reduce the exposed interface.

Remove endpoints.

Remove permissions.

Remove unnecessary information.

Move functionality behind explicit trust boundaries.

Then use AI to watch the small interface that remains.

Sometimes the best cybersecurity product is still the delete key.


The Browser Should Know Less

We’ve also become surprisingly comfortable shipping enormous amounts of information about our applications directly to strangers.

Open DevTools on a modern web application.

You’ll often find:

API routes.

Object structures.

Feature flags.

Source maps.

Service names.

Internal identifiers.

Sometimes comments.

Occasionally comments written by a developer having what appears to have been a very difficult Tuesday.

Some of this is unavoidable.

A lot isn’t.

We should increasingly ask:

Why does the browser need to know this?

Because the browser isn’t ours.

The user controls it.

The attacker controls it.

The AI controls it.

Treat it accordingly.


Tiny Leaks Become Maps

This matters even more when AI can correlate information.

An error reveals a service name.

A JavaScript bundle reveals an endpoint.

A source map reveals an internal object.

A response header reveals infrastructure.

A forgotten documentation page reveals an old API.

None of those things necessarily compromises the system.

But together they start drawing a map.

Humans can do this.

AI can do it relentlessly.

The difference between:

“Here are 400 mildly interesting observations”

and:

“Here is how this application is probably architected”

is exactly the kind of transformation LLMs are good at.

Information leakage therefore becomes more valuable.


Rate Limiting Needs to Grow Up

We also need to move beyond:

100 requests / minute / IP

That’s useful against idiots and broken scripts.

An intelligent attacker can count.

A better security model looks at:

identity
+
session
+
device
+
capability
+
resource
+
behaviour
+
history

Three failed authorization attempts against unrelated resources might matter more than 500 legitimate requests.

Context matters.

Intent matters.

Sequence matters.


Then There’s the Slightly Awkward Problem: Good AI

There’s another reason simply blocking AI won’t work.

AI agents are going to become legitimate users of our applications.

They will:

book hotels;

manage calendars;

purchase products;

administer infrastructure;

submit expenses;

manage advertising;

interact with SaaS platforms;

and perform thousands of other tasks.

So we’ll have:

Humans

Trusted internal agents

Customer agents

Partner agents

Unknown agents

Malicious agents

You can’t solve that with:

if (robot)
    block();

The question becomes the same question we should have been asking about humans all along:

What is this identity allowed to do?

Which takes us straight back to capabilities.

Give an agent exactly the authority necessary to complete its task.

Nothing more.


The Security Product I Suspect Is Missing

The more I think about this, the more I wonder whether there’s a layer missing from modern application infrastructure.

Something somewhere between:

a WAF,

an API gateway,

identity management,

zero-trust infrastructure,

and behavioural security.

Call it an Application Security Gateway for lack of a better term.

Its job wouldn’t simply be:

Detect nasty HTTP.

Its job would be:

Define the tiny vocabulary through which the untrusted world is permitted to interact with this application.

Something like:

Internet
    |
    v
+----------------------------------+
| APPLICATION SECURITY GATEWAY     |
|                                  |
| Identity                         |
| Session                          |
| Schema validation                |
| Capability enforcement           |
| Resource authorization           |
| Behaviour modelling              |
| Risk scoring                     |
| Agent identification             |
| Security telemetry               |
+----------------+-----------------+
                 |
          VERY SMALL INTERFACE
                 |
                 v
+----------------------------------+
| PRIVATE APPLICATION ENVIRONMENT  |
+----------------------------------+

A WAF asks:

Does this request look malicious?

This asks:

Why is this client even allowed to ask that question?

I think that’s a fundamentally stronger security model.


If I Were Auditing an Application Tomorrow

I’d start asking some slightly uncomfortable questions.

How many public endpoints do we actually have?

How many do we genuinely need?

How many old versions still work?

What can an authenticated user enumerate?

How much architecture can I reconstruct from the frontend?

Does every object access verify ownership?

Could one stolen session access everything that user has ever touched?

Can the application detect slow reconnaissance?

Can internal services be reached in unexpected ways?

Do our security systems understand behaviour or just HTTP requests?

And perhaps most importantly:

If I gave a capable AI agent a valid account and told it to explore this application continuously for 30 days, how confident am I that it would find nothing?

I suspect that question would make quite a few engineering teams uncomfortable.

It should.


The Economics Have Changed

We’ve always had security vulnerabilities.

But there was an invisible defence protecting many applications:

attacker time.

Some obscure vulnerability might theoretically exist.

But someone had to find it.

Someone had to understand the application.

Someone had to spend hours investigating strange behaviour.

Someone had to care enough to continue.

AI attacks that economic assumption.

Machines don’t get bored.

They don’t need sleep.

They don’t care that your API naming convention makes absolutely no sense.

They don’t mind reading 600 JavaScript files.

They don’t mind trying again tomorrow.

And they can remember every tiny thing they learn.

Complexity used to provide a tiny amount of accidental camouflage.

Increasingly, complexity may simply provide the AI with more places to look.


Make the Hole Smaller

So my conclusion isn’t particularly glamorous.

We don’t necessarily need some revolutionary new cybersecurity philosophy.

We need to become much more serious about several very old ones:

Least privilege.

Defence in depth.

Zero trust.

Explicit authorization.

Isolation.

Attack-surface reduction.

But I’d add another:

Secure by vocabulary.

Expose the smallest possible language through which an untrusted system can communicate with your application.

Make every operation intentional.

Make every permission explicit.

Assume anything public will eventually be understood.

And use AI to watch how those capabilities are being exercised.

Because the attacker of the near future doesn’t necessarily need to smash through the front door.

We have already given them a visitor’s badge, a map and 247 API endpoints to investigate.

Maybe the clever move isn’t building a bigger guard.

Maybe we should start by making the bloody door smaller.