Bind values as parameters in the local wallet database queries - #2109
Open
eastagiletracker wants to merge 1 commit into
Open
Bind values as parameters in the local wallet database queries#2109eastagiletracker wants to merge 1 commit into
eastagiletracker wants to merge 1 commit into
Conversation
Search values, contact fields and api supplied transaction ids were interpolated straight into the statements run against the local sqlite database, so a quote character broke the query and a crafted value could read other tables or run extra statements. The statement builders that took values are now constants holding placeholders, and every caller supplied value is bound through the params path that query() already supports. update() and delete() take the values for their where clause the same way.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR proposes binding caller-supplied values as SQL parameters in the local wallet database layer, so that a search term or a contact field can no longer change the statement that runs against the wallet's sqlite file. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/275. You can sign in with your GitHub ID to claim ownership of the project.
What is happening today
Most statements in
src/api/db/statements/*are built by interpolating the caller's value into the SQL string — for exampleselect * from Contacts where name like "${value}%" or address like "${value}%"— andDatabaseManager.update()does the same for column values, viaflattenObjectToSetClausewrapping strings in single quotes.Those values arrive straight from the UI.
SearchContactspasses what the user typed intosearchContactsByValue→databaseManager.getContactsByValue; the send-funds and receive-funds address lists do the same throughgetWalletAddressesByValue; transaction search reachessearchTransactionsForValue; the contact and wallet sidepanels pass their form fields toupdateContactById/updateWalletById; andupdateTxByTxIduses a transaction id that came back from the explorer API.Three things follow from that, in increasing order of seriousness. A value containing a quote character produces a syntax error, so searching for
Jean "Ace" Doeor saving a contact namedSam O'Neilrejects the query and the search results or the save silently do nothing. A crafted search value can select from another table, and the result rows are rendered in the list — the search path returns rows throughdb.prepare, so aunion selectreaches the seed and private key columns ofWalletsandAddresses. And an update reachesdb.runwithout params, which runs every statement in the string, so a contact field is enough to execute a second statement of the attacker's choosing.Reproduction on master
Against
masterat 2a3b646, with onlytests/unit/api/db/database-manager.unit.test.jsfrom this branch added andsrc/untouched,yarn run test:unitreports:The two that matter most:
getContactsByValue('" union select id, name, seed, description from Wallets --')comes back with the wallet's mnemonic seed in the contact result set, andupdateContactById(1, { name: "Mallory'; delete from Wallets; --" })leaves theWalletstable empty.The change
The
SELECTentries that took a value are now constants holding?placeholders instead of functions that interpolate one, and every caller-supplied value is bound through theparamspath thatDatabaseManager.query()already implements and documents.update()anddelete()take the values for their where clause the same way, through a new optionalwhereParams, andflattenObjectToSetClausenow emitscol=?and lets the values bind. Nothing new was added to the dependency list —sqlstring-sqliteis already declared but not imported anywhere, and binding through the existingquery()path makes it unnecessary.DatabaseManageris the only consumer ofSTATEMENTSin the repo, so no other caller changes shape.LIKEsemantics are preserved exactly: the%wildcards are appended to the bound value, in the same prefix or substring position each statement used before.Verification
tests/unit/api/db/database-manager.unit.test.jsadds 30 assertions covering every query method the change touches: the 6 above fail without thesrc/half of this branch and pass with it, and the other 24 pass on both trees — they are the controls that the legitimate behaviour did not move (prefix search still matches, date filters still filter, updates and deletes still hit exactly one row,getTxAddressIds,getAddressName,getBalanceByAddress,getPrivKeyFromAddressand the theme and db-version updates all still return what they did before).On this branch the full unit suite is green —
Tests: 34 passed, 34 total, the 4 that existed plus these 30 — run with node 14 to match.github/workflows/test.yml.yarn run lintbehaves the same before and after: it stops at the sameTypeError: Cannot read property 'range' of nullinside eslint'stemplate-curly-spacingrule that master already hits on multi-line template literals, which is untouched by this change.How this was managed
We track this work on a live board built from your repo's own issues and pull requests: this change is the story Bind SQL parameters in the local wallet database queries, on the board at https://eastagiletracker.com/projects/275.
If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com