Skip to content

PSA: Upgrade to Sequelize >= 6.19.2 & Our recent breaking change in v6 #14519

@ephys

Description

@ephys

Hi everyone 👋

We have recently released Sequelize 6.19.1 (since patched as 6.19.2) which includes a breaking change.

We normally want to fully respect semver rules, unfortunately a very serious SQL injection exploit has surfaced and it was impossible for us to fix it without introducing a breaking change. We considered the issue serious enough to break the rule and release the fix in V6. Most of you are going to be completely unaffected, but a small percentage of users may experience breakage with this version, more below

The exploit

The SQL injection exploit is related to replacements. Here is such an example:

In the following query, some parameters are passed through replacements, and some are passed directly through the where option.

User.findAll({
  where: or(
    literal('soundex("firstName") = soundex(:firstName)'),
    { lastName: lastName },
  ),
  replacements: { firstName },
})

This is a very legitimate use case, but this query was vulnerable to SQL injection due to how Sequelize processed the query: Sequelize built a first query using the where option, then passed it over to sequelize.query which parsed the resulting SQL to inject all :replacements.

If the user passed values such as

{
  "firstName": "OR true; DROP TABLE users;",
  "lastName": ":firstName"
}

Sequelize would first generate this query:

SELECT * FROM users WHERE soundex("firstName") = soundex(:firstName) OR "lastName" = ':firstName'

Then would inject replacements in it, which resulted in this:

SELECT * FROM users WHERE soundex("firstName") = soundex('OR true; DROP TABLE users;') OR "lastName" = ''OR true; DROP TABLE users;''

As you can see this resulted in arbitrary user-provided SQL being executed.

The solution & breaking change

The way to fix this without breaking everyone's codebase was to use a smarter SQL parser that would only inject replacements in places where native SQL parameters are allowed (with some safe exceptions to stay as backward compatible as possible).

This means that :replacements will not be treated as such anymore if they are quoted, as we treat it as being part of a SQL string.

Details here: https://github.com/sequelize/sequelize/blob/v6/test/unit/utils/sql.test.js

On top of this, in Sequelize 7, we rewrote this system completely to handle both steps at the same time. Removing all risk of SQL injection (#14447).

Some of you relied on this to inject parameters inside of sql strings, which is why this can be considered a breaking change.
Note however that doing that was not safer than simply concatenating, and it being done by us may have given you a false sense of security regarding input sanitization.


Once again, we are very sorry for the inconvenience this causes.

- Zoé

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions