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?
You can use middleBrick to scan your Sails API endpoints without any configuration or credentials. Simply provide the base URL of your API, and middleBrick will test for common vulnerabilities like authentication bypass, IDOR, input validation issues, and data exposure. The scan takes 5-15 seconds and returns a security score with prioritized findings. For continuous monitoring, the Pro plan lets you integrate middleBrick into your CI/CD pipeline to automatically scan staging APIs before deployment.
Does Sails have built-in protection against SQL injection?
Sails' Waterline ORM provides parameterized queries that protect against SQL injection when used correctly. However, developers often bypass the ORM and use raw queries or string concatenation, which reintroduces injection vulnerabilities. Always use the provided query methods like 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?
The most common mistake is leaving blueprint routes enabled without authentication policies in production. Developers enable blueprints during development for convenience, then forget to disable them or add proper access controls before deployment. This exposes all CRUD operations on your models to anyone who can access your API. Always review your config/blueprints.js file and ensure blueprint routes are either disabled or protected by authentication and authorization policies before going to production.