Sails API Security
Sails Security Posture — What Sails Gets Right and Wrong by Default
Sails.js is a popular MVC framework for Node.js that emphasizes convention over configuration. While it provides a solid foundation for building APIs, its security defaults reveal a common pattern in modern frameworks: developer convenience often trumps security hardening.
By default, Sails ships with several security features enabled through the csurf and helmet middleware, which protect against CSRF attacks and set secure HTTP headers. However, Sails also enables blueprint routes by default, which automatically generate RESTful endpoints for your models without requiring explicit controller actions. This convenience feature can expose your entire data model to unauthenticated access if you're not careful.
The framework's Waterline ORM provides a powerful abstraction layer but can introduce SQL injection risks if developers use string-based queries instead of the provided parameterized methods. Additionally, Sails' default policy system for authentication and authorization requires explicit configuration—without policies in place, any blueprint route becomes a potential attack vector.
Another critical default behavior: Sails automatically generates an /api endpoint that exposes your API documentation and model schemas. While useful for development, this can reveal sensitive implementation details to attackers if left enabled in production.
Top 5 Security Pitfalls in Sails — Real Misconfigurations Developers Make
Based on real-world assessments of Sails applications, here are the most common security misconfigurations that leave APIs vulnerable:
1. Unprotected Blueprint Routes
Sails' blueprint routes automatically create CRUD endpoints for your models. Without policies restricting access, attackers can read, create, update, or delete records through these endpoints. For example, a User model with blueprints enabled exposes GET /user, POST /user, PUT /user/:id, and DELETE /user/:id endpoints automatically.
// Dangerous default behavior
// Without policies, this exposes all user data
module.exports.blueprints = {
actions: true,
shortcuts: true,
rest: true
};2. Insecure Direct Object References (IDOR)
Sails' default find/update methods use simple ID parameters without ownership verification. An attacker can easily modify the ID parameter to access other users' data:
// Vulnerable to IDOR
// No check that userId matches authenticated user
module.exports = {
find: async (req, res) => {
const records = await User.find({ id: req.params.id });
return res.json(records);
}
};3. Inadequate Input Validation
While Sails provides basic validation through model attributes, developers often rely on client-side validation or skip server-side validation entirely. This leaves APIs vulnerable to SQL injection, XSS, and data integrity issues.
// Vulnerable to NoSQL injection
// Malicious input could manipulate the query
module.exports = {
find: async (req, res) => {
const query = req.query.q; // Could contain injection payload
const results = await User.find({ where: { name: query } });
return res.json(results);
}
};4. Exposed API Documentation
Sails automatically serves API documentation at /api and model schemas at /model endpoints. In production environments, this reveals your API structure, model relationships, and potentially sensitive field names to attackers.
5. Missing Rate Limiting
Sails doesn't include rate limiting by default. Without protection, APIs are vulnerable to brute force attacks, credential stuffing, and DoS attempts. The lack of rate limiting on authentication endpoints is particularly dangerous.
Security Hardening Checklist — Actionable Config/Code Changes
Securing your Sails API requires a combination of configuration changes and code-level protections. Here's a practical checklist to implement:
1. Disable or Restrict Blueprint Routes
Only enable blueprints where absolutely necessary, and always protect them with policies:
module.exports.blueprints = {
actions: true,
shortcuts: false, // Disable automatic shortcuts
rest: false, // Disable automatic REST endpoints
prefix: '/api/v1' // Version your API
};2. Implement Comprehensive Policies
Create policies for authentication, authorization, and data validation:
// api/policies/isAuthenticated.js
module.exports = async (req, res, next) => {
if (req.session.userId) {
return next();
}
return res.status(401).json({ error: 'Authentication required' });
};3. Add Input Validation and Sanitization
Use Sails' built-in validation and add custom validation logic:
// models/User.js
module.exports = {
attributes: {
email: {
type: 'string',
required: true,
unique: true,
isEmail: true
},
password: {
type: 'string',
required: true,
minLength: 8
}
}
};4. Implement Rate Limiting
Add rate limiting middleware to protect against abuse:
// config/http.js
const rateLimit = require('express-rate-limit');
module.exports.http = {
middleware: {
rateLimiter: (req, res, next) => {
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
limiter(req, res, next);
}
},
order: [
'cookieParser',
'session',
'rateLimiter', // Add rate limiter early
'bodyParser',
'compress',
'router',
'www',
'favicon',
'404'
]
};5. Secure API Documentation
Disable automatic API documentation in production or protect it with authentication:
// config/blueprints.js
module.exports.blueprints = {
actions: true,
shortcuts: false,
rest: false,
populate: false,
autoWatch: false,
mirror: false
};6. Add Security Headers
Configure security headers through Sails' middleware:
// config/security.js
module.exports.security = {
cors: {
allRoutes: true,
origin: 'https://yourdomain.com',
credentials: true
},
csp: {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'"],
'img-src': ["'self'", 'data:', 'https:']
}
};Frequently Asked Questions
How can I scan my Sails API for security vulnerabilities?
Does Sails have built-in protection against SQL injection?
find(), create(), and update() with object parameters rather than building SQL strings manually. Additionally, validate and sanitize all user inputs before they reach your database layer.What's the biggest security mistake developers make with Sails blueprints?
config/blueprints.js file and ensure blueprint routes are either disabled or protected by authentication and authorization policies before going to production.