Short answer
No security layer should be trusted on its own. If one fails or is misconfigured, the next one must still block the way. For a small internal tool, that means identity before anything can be reached, a service that re-checks the session on every request, and data with its own permissions. The most useful principle is to fail closed: if configuration is missing, the service serves nothing. A visible error is preferable to a tool left open without anyone knowing.
Key points
- No security layer should be trusted on its own: if one fails or is misconfigured, the next one must still block the way.
- A small internal tool does not need a VPN: a one-time code sent by email and a list of authorised people are enough to handle entry.
- The service should check the session on every request, keep its own list of permitted email addresses and fail closed when configuration is missing.
- Office systems can be published through an outbound connection to the network edge, without opening any router ports and always with identity in front.
- Documentation, logs and conversations with AI assistants should show a secret's name, never its value.
Identity before anything can be reached
A small team does not need passwords that have to be remembered and can be leaked. A one-time code sent by email and a policy listing the authorised people are enough to handle entry to a private tool, with no VPN and nothing to install.
Behind that door, the service itself should check the session token on every request: algorithm, issuer, audience, expiry, email and signature. It should also keep its own list of permitted email addresses, so that if the access policy is widened by mistake, the tool stays closed.
The same error for every failure
A forged token, an expired one and one with no email should all receive the same response, without explaining which check failed. Saying ‘the token has expired’ helps an attacker refine their approach; saying ‘sign in again’ is enough for the person using the tool.
If the service does not have its verification parameters configured, it must respond that access is not configured and serve nothing, even if the token is valid. An incomplete deployment must never be an open deployment.
Tunnels, not open ports
Whatever lives in the office (a database, a machine that reads documents) can be published through an outbound connection to the network edge, without opening any port on the router. Identity always comes first, even for a quick test.
For public forms, a bot check without annoying captchas, a honeypot field and storing the IP address only as a hash reduce the noise without asking anything of the person filling them in. And a strict content security policy in the browser stops a third-party script from slipping into the page.
Secrets by name only
In documentation, in conversations with AI assistants and in logs, the name of a secret should appear, never its value. The value lives in the service’s secrets manager and in a local file that version control ignores.
One deploy key per repository, a guard that checks every commit for anything that looks like a secret, and a short list of actions that require human approval (deploying, changing a secret, touching the access configuration or DNS) complete a hygiene routine that costs little and prevents incidents.
Frequently asked questions
How can I secure access to a small internal tool without setting up a VPN?
Put identity in front of everything: a one-time code sent by email and a policy listing the authorised people, with no passwords and nothing to install. Behind that, the service itself should check the session token on every request (algorithm, issuer, audience, expiry, email and signature) and keep its own list of permitted email addresses. That way, if the access policy is widened by mistake, the tool stays closed.
What does it mean for an internal tool to ‘fail closed’?
It means that if configuration is missing, the service serves nothing. If its verification parameters are not configured, it must respond that access is not configured, even if the token is valid. A visible error is preferable to a tool left open without anyone knowing: an incomplete deployment must never be an open deployment.
What error message should an internal tool show when an access check fails?
The same one for every failure, without explaining which check failed. A forged token, an expired token and a token with no email should all receive the same response. Saying ‘the token has expired’ helps an attacker refine their approach, whereas ‘sign in again’ is enough for the person using the tool.
What basic measures stop an internal tool's keys and secrets from leaking?
Documentation, logs and conversations with AI assistants should show a secret's name, never its value, which lives in the service's secrets manager and in a local file ignored by version control. Add one deploy key per repository and a guard that checks every commit for anything that looks like a secret. A short list of actions that require human approval, such as deploying, changing a secret or touching the access configuration or DNS, completes a hygiene routine that costs little and prevents incidents.