Fix Shadow API Exposure in LoopBack
Shadow APIs in LoopBack 4 are a silent killer, often born from the 'convention over configuration' trap. When developers use the CLI to scaffold CRUD controllers, they inadvertently expose internal-only methods and sensitive model properties to the public. If you aren't explicitly pruning your endpoints and hardening your models, you're leaking the blueprint of your internal infrastructure to any script kiddie with a fuzzer.
The Vulnerable Pattern
@model() export class User extends Entity { @property({id: true}) id: number; @property() email: string; @property() passwordHash: string; // EXPOSED @property() internalAdminNote: string; // EXPOSED }export class UserController { constructor(@repository(UserRepository) public userRepository: UserRepository) {}
@get(‘/users’) async find(): Promise<User[]> { return this.userRepository.find(); // Returns every field in the DB including hashes }
@patch(‘/users/{id}’) async updateById(@param.path.number(‘id’) id: number, @requestBody() user: User): Promise{ await this.userRepository.updateById(id, user); // Allows mass assignment/overwriting of admin notes } }
The Secure Implementation
To eliminate Shadow API exposure, you must implement a multi-layered defense. 1. Model Hardening: Use the 'hidden: true' metadata in your Model definition to ensure sensitive fields are stripped during JSON serialization. 2. Field Filtering: In your Controller, never return the raw result of a repository find; use the 'fields' filter to explicitly define an allow-list of public attributes. 3. Schema Pruning: Use 'getModelSchemaRef' with the 'exclude' option in your requestBody decorators to prevent mass-assignment vulnerabilities where attackers overwrite internal flags. 4. Explicit Routing: Delete any scaffolded CRUD methods that aren't strictly required for your business logic to reduce the attack surface.
@model({settings: {strict: true}}) export class User extends Entity { @property({id: true}) id: number; @property({required: true}) email: string; @property({hidden: true}) passwordHash: string; // PROTECTED @property({hidden: true}) internalAdminNote: string; // PROTECTED }export class UserController { constructor(@repository(UserRepository) public userRepository: UserRepository) {}
@get(‘/users’) @authenticate(‘jwt’) async find(): Promise<Partial
[]> { return this.userRepository.find({ fields: {id: true, email: true} // STRICT ALLOW-LIST }); }
@patch(‘/users/{id}’) @authorize({allowedRoles: [‘admin’]}) async updateById(@param.path.number(‘id’) id: number, @requestBody({content: {‘application/json’: {schema: getModelSchemaRef(User, {partial: true, exclude: [‘id’, ‘internalAdminNote’]})}}}) user: User): Promise{ await this.userRepository.updateById(id, user); } }
Your LoopBack API
might be exposed to Shadow API Exposure
74% of LoopBack apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.