fix(security): resolve a potential SQL injection via bypass through objects#4054
fix(security): resolve a potential SQL injection via bypass through objects#4054wellwelwel wants to merge 7 commits intosidorares:masterfrom
Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
If you like the solution, I would be happy to assign you as a maintainer, as was done with AWS SSL Profiles. I'm totally open to migrating to the mysqljs organization or what you think is best.
It will be a pleasure to contribute to sqlstring, regardless of the direction this PR takes. Also, from docs:
The proposed repository is: https://github.com/wellwelwel/sql-escaper. It has the same strictness as lru.min, with 100% coverage of real tests, passing in all sqlstring tests (and including new ones too, specially considered for MySQL2), 100% typed, and fully documented too. Please, feel free to review 💙 |
|
I have not really been in Node.js land for some time now and am open to delegating the modules to someone who is / the mysqljs org too. |
@dougwilson, although I'm not part of mysqljs organization, I'm open to helping in any way I can 🙋🏻♂️ In this specific case, I didn't simply think about creating a different project, I recognized that there is a really strong philosophy behind sqlstring and mysqljs/mysql that must be respected. |
|
It's finally ready 🎉 Now it uses an AST-based approach to:
New features:
For those concerned about this issue, you can use an overrides in your package.json: "dependencies": {
"mysql2": "^3.16.3"
},
"overrides": {
"sqlstring": "npm:sql-escaper"
}
Now, it will work normally for Tip The same works for mysqljs/mysql, but it requires at least Node.js v12. |
Important
Normally, this would be handled with much more care, but considering that it is a vulnerability that has been exploited since 2022 in a completely public manner, I also decided to resolve it publicly.
Fixes #1914, Fixes #4051, Fixes #2528, Fixes #3481.
Vulnerability Exploitation
Changes
This fix doesn't change MySQL2's behavior, nor does it introduce any kind of breaking change.
Unlike what's recommended in the two exploits (forcing the use of
trueinstringifyObjects), this doesn't fix the reported vulnerability, it just "hides" it.I researched several scenarios where it's actually necessary to convert values derived from objects, and there are the three safe/expected situations:
❗️ For SELECT, conventional INSERT, DELETE, etc., they will always cause a potential SQL injection. For example:
The replacement of sqlstring (and why)
Mainly motivated by this comment: mysqljs/sqlstring#49 (comment), sqlstring must be compatible with Node.js v0.6. This limits the natural improvement of new language features, such as the addition of
BigInt(v10.4) andUint8Array(v0.10).Security + Breaking Change Conflicts
I read the discussion around #1914 and evaluated the ideas:
stringifyObjectsentirely, create an alternative feature such assqlstring.SET, etc.I thought carefully about the issue and the time that has passed without resolution (~3 years), probably due to the challenge of negotiating a breaking change that affects so many users and packages simultaneously.
The solution
Instead of introducing a breaking change, I just handled the parameters properly, for example:
RegExp, supports comments and understands when key values are used inside a string 🔥Then, in short:
This way,
SETorKEY UPDATEnormally provide the functionality, whileSELECT, commonINSERT, etc., return the same behavior whenstringifyObjectsis enabled, for example[Object ...]. Unlike a breaking change, these queries should no longer process the object values in any case, after all, that is where the vulnerability lies.Long term support and pros
It will enable us to move forward with fixing issues #2528, #3481, and #1176, including support for
BigInt(mysqljs/sqlstring#49).⚡️ Performance
Not only the language resource point, but also performance limitations, where SQL Escaper outperforms sqlstring up to 40%:
Considerations
I still consider that this problem should indeed be solved on the sqlstring side, but unfortunately I was unable to contribute due to the limitations of the environment (Node.js v0.6):
String.prototype.search(), for example, requires version 0.10 😅I believe that MySQL2 has evolved differently from mysqljs/mysql. Even if we ignore the security issue, I still believe that MySQL2 has a lot to gain from the level of performance that new approaches can offer.
SQL Escaper is also compatible with mysqljs/mysql as usual, but requires at least Node.js v12.
Tip
With this idea, for example, we can discuss different approaches, such as removing it entirely, etc., with the project already safely and available in the meantime.