Security
Security in Portway is layered: tokens decide who gets in, scopes and environments decide what they can reach, and network rules decide where requests may go. This page walks through each layer in turn, from authentication down to a pre-deployment checklist you can run before going live.
Note
This page must be read as a sensible starting point; rather than a policy to follow. It is worth aligning them with your organisation's security policies before exposing Portway to production traffic.
Authentication
All API requests require a Bearer token:
Authorization: Bearer your-token-hereTokens are generated using cryptographically secure random values and stored encrypted on disk. Each token is bound to a username for audit trail purposes.
First-run token
On first run, Portway generates an initial token and writes it to tokens/YOUR_SERVER_NAME.txt:
{
"Username": "SERVER-NAME",
"Token": "base64-encoded-secure-token",
"AllowedScopes": "*",
"AllowedEnvironments": "*",
"ExpiresAt": "Never",
"CreatedAt": "2024-01-01 00:00:00"
}Caution
This file is highly sensitive: it carries a token with full scope and environment access. Remove it from disk immediately after recording the token somewhere secure. Use the Web UI to manage all subsequent tokens.
Authorization
Scope control
Restrict a token to specific endpoints using the AllowedScopes field:
| Pattern | Access |
|---|---|
* |
All endpoints |
Products,Orders |
Named endpoints only |
Product* |
All endpoints matching the prefix |
Company/Employees |
Specific namespaced endpoint |
Company/* |
All endpoints in a namespace |
GET:Products |
Single endpoint, single method |
Environment control
Restrict a token to specific environments using AllowedEnvironments:
| Pattern | Access |
|---|---|
* |
All environments |
prod |
Single environment |
dev,test |
Named environments |
dev* |
All environments matching the prefix |
Endpoint-level restrictions
Individual endpoints enforce their own environment and visibility constraints:
{
"DatabaseObjectName": "SensitiveData",
"AllowedEnvironments": ["prod"],
"IsPrivate": true,
"AllowedMethods": ["GET"]
}Both token-level and endpoint-level restrictions must pass for a request to succeed. See Environments, access control for the full matrix.
Network security
IP restrictions
Configure allowed hosts and blocked IP ranges in environments/network-access-policy.json:
{
"allowedHosts": [
"localhost",
"127.0.0.1",
"your-internal-server.local"
],
"blockedIpRanges": [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
]
}Security headers
Portway adds these headers to all responses automatically:
| Header | Value |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Strict-Transport-Security |
max-age=31536000 |
Referrer-Policy |
strict-origin-when-cross-origin |
Content-Security-Policy |
default-src 'self'; object-src 'none'; ... |
Secrets management
Automatic encryption
Portway encrypts plaintext secrets in settings.json files on next startup. Connection strings and authentication values written in plaintext become PWENC:... format. The original value is no longer stored.
Automatic encryption applies only to per-environment settings.json files and the MCP configuration store (mcp.db). It does not rewrite appsettings.json, so values placed there (such as WebUi:AdminApiKey) stay in plaintext.
Web UI admin key
Never store a real admin key in appsettings.json. The shipped file intentionally contains the placeholder INSECURE-CHANGE-ME-admin-api-key, which Portway rejects in production: Web UI authentication is disabled and an error is logged until a real key is provided.
Supply the key through the environment instead:
Generate a strong key (32+ characters; shorter keys log a warning at startup):
With Azure Key Vault configured (KEYVAULT_URI), the key can also be served from the vault through the standard ASP.NET Core configuration pipeline. Environment variables and Key Vault values override anything in appsettings.json.
The Settings page in the Web UI shows the current key strength (not set / placeholder / weak / strong) under Security Posture.
Azure Key Vault
Store connection strings and server names in Azure Key Vault instead of settings.json:
Create secrets named by environment:
{environment}-ConnectionString{environment}-ServerName{environment}-Headers(JSON string)
Portway fetches these at startup and uses them identically to file-based configuration.
SQL Server permissions
When using Windows Authentication (NTLM) via IIS, configure the database account with minimum required permissions:
USE [master];
GO
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'DOMAIN\USER_NAME')
BEGIN
EXEC ('CREATE LOGIN [DOMAIN\USER_NAME] FROM WINDOWS;');
END
GO
USE [YourDatabase];
GO
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'DOMAIN\USER_NAME')
BEGIN
CREATE USER [DOMAIN\USER_NAME] FOR LOGIN [DOMAIN\USER_NAME];
END
GO
ALTER ROLE [db_datareader] ADD MEMBER [DOMAIN\USER_NAME]; -- Read tables/views
ALTER ROLE [db_datawriter] ADD MEMBER [DOMAIN\USER_NAME]; -- Write (insert/update/delete)
GRANT EXECUTE TO [DOMAIN\USER_NAME]; -- Stored procedures
GRANT VIEW DEFINITION TO [DOMAIN\USER_NAME]; -- Schema metadata
GOGrant only the roles your endpoints require. A read-only deployment needs only db_datareader.
Logging and auditing
Security events are logged at Warning or Debug level:
[DBG] Invalid token: {masked}
[WRN] IP {IP} has exceeded rate limit, blocking for {period}Enable request traffic logging to capture headers and bodies for security analysis:
{
"RequestTrafficLogging": {
"Enabled": true,
"CaptureHeaders": true,
"IncludeRequestBodies": true
}
}See Monitoring for traffic logging configuration details.
Pre-deployment checklist
- HTTPS binding configured in IIS
- IIS Application Pool using minimum-privilege identity
-
WebUi__AdminApiKeyset via environment variable or Key Vault (32+ random characters, never inappsettings.json) - Azure Key Vault configured (if applicable)
- Initial token file removed from disk
- Tokens created with specific scopes and environments
- Rate limiting configured
- Firewall rules reviewed
- Security headers verified with a response inspection tool
Incident response: compromised token
- Open the Web UI and navigate to Access Tokens
- Click Rotate Token on the affected entry to invalidate it and generate a replacement
- Update all applications using the compromised token with the new value
- Enable traffic logging if not already active to monitor for continued unauthorized activity:
json
{ "RequestTrafficLogging": { "Enabled": true, "CaptureHeaders": true } } - Document the incident for your security audit trail
INFO
Manage all tokens in the Web UI under Tokens. The Tokens page lists all active (non-revoked, non-expired) tokens with their scope and environment restrictions.
WARNING
Token revocation is permanent. A revoked token cannot be reactivated. Create a new token for any affected user or system.
Portway