Skip to main content

Access Control Scanner

The Access Control scanner identifies authentication vulnerabilities, authorization bypasses, and permission issues that could allow unauthorized access to PHI.

HIPAA Reference

§164.312(a)(1) - Access control

Implement technical policies and procedures for electronic information systems that maintain electronic protected health information to allow access only to those persons or software programs that have been granted access rights.

§164.312(d) - Person or entity authentication

Implement procedures to verify that a person or entity seeking access to electronic protected health information is the one claimed.

What It Detects

Missing Authentication

// ❌ CRITICAL: No authentication on PHI endpoint
app.get('/api/patients/:id', async (req, res) => {
const patient = await db.patients.findById(req.params.id);
res.json(patient);
});

// ✓ Protected endpoint
app.get('/api/patients/:id', authenticate, async (req, res) => {
const patient = await db.patients.findById(req.params.id);
res.json(patient);
});

Hardcoded Credentials

// ❌ CRITICAL: Hardcoded credentials
const dbPassword = "super_secret_123";
const apiKey = "sk_live_abc123xyz";

// ✓ Environment variables
const dbPassword = process.env.DB_PASSWORD;
const apiKey = process.env.API_KEY;

Overly Permissive CORS

// ❌ HIGH: Allows any origin
app.use(cors({ origin: '*' }));

// ❌ MEDIUM: Reflects origin without validation
app.use(cors({ origin: true }));

// ✓ Explicit allowed origins
app.use(cors({
origin: ['https://app.healthcare.com', 'https://admin.healthcare.com']
}));

Hardcoded Roles/Permissions

// ❌ MEDIUM: Hardcoded role check
if (user.email === 'admin@company.com') {
// Grant admin access
}

// ✓ Role-based check
if (user.roles.includes('admin')) {
// Grant admin access
}

Missing Authorization

// ❌ HIGH: No ownership check
app.get('/api/patients/:id/records', authenticate, async (req, res) => {
const records = await db.records.find({ patientId: req.params.id });
res.json(records);
});

// ✓ With authorization
app.get('/api/patients/:id/records', authenticate, authorize, async (req, res) => {
// authorize middleware checks if user can access this patient
const records = await db.records.find({ patientId: req.params.id });
res.json(records);
});

Insecure Session Management

// ❌ MEDIUM: Session stored in localStorage
localStorage.setItem('authToken', token);

// ✓ HttpOnly cookie
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});

Detection Rules

IssueSeverityDescription
Unprotected PHI endpointCRITICALNo auth middleware on PHI routes
Hardcoded credentialsCRITICALSecrets in source code
cors({ origin: '*' })HIGHAny origin allowed
Missing authorizationHIGHAuth without authz
Hardcoded role checksMEDIUMEmail/name-based permissions
localStorage authMEDIUMToken in localStorage
Disabled auth checkHIGH// TODO: add auth patterns

Configuration

{
"scanners": {
"access": {
"authMiddleware": ["authenticate", "requireAuth", "isAuthenticated"],
"authzMiddleware": ["authorize", "checkPermission", "requireRole"],
"protectedPaths": [
"/api/patients",
"/api/records",
"/api/prescriptions"
],
"allowedCorsOrigins": [
"https://app.healthcare.com"
]
}
}
}

Remediation

Implement Authentication Middleware

// middleware/authenticate.ts
import jwt from 'jsonwebtoken';

export async function authenticate(req, res, next) {
const token = req.cookies.session || req.headers.authorization?.split(' ')[1];

if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}

try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = await db.users.findById(payload.userId);
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}

Add Authorization Layer

// middleware/authorize.ts
export function authorize(permission: string) {
return async (req, res, next) => {
const user = req.user;
const resource = req.params.id;

const allowed = await checkPermission(user, permission, resource);

if (!allowed) {
return res.status(403).json({ error: 'Access denied' });
}

next();
};
}

// Usage
app.get('/api/patients/:id',
authenticate,
authorize('patient:read'),
getPatient
);

Secure Credential Management

// Use environment variables
const config = {
database: {
host: process.env.DB_HOST,
password: process.env.DB_PASSWORD
},
jwt: {
secret: process.env.JWT_SECRET
}
};

// Validate required secrets at startup
const required = ['DB_PASSWORD', 'JWT_SECRET', 'ENCRYPTION_KEY'];
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}

Configure CORS Properly

import cors from 'cors';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];

app.use(cors({
origin: (origin, callback) => {
// Allow requests with no origin (mobile apps, Postman)
if (!origin) return callback(null, true);

if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));

Role-Based Access Control

// Define permissions
const permissions = {
'patient:read': ['doctor', 'nurse', 'admin'],
'patient:write': ['doctor', 'admin'],
'patient:delete': ['admin'],
'prescription:create': ['doctor']
};

function checkPermission(user: User, permission: string): boolean {
const allowedRoles = permissions[permission] || [];
return user.roles.some(role => allowedRoles.includes(role));
}

See Also