Safeguard
Application Security•

How do you prevent SQL injection in Dart and Flutter apps?

Local SQLite and Dart server code are both open to SQL injection. Use placeholders and argument lists, allow lists for names, and test with hostile input.

Safeguard Security Team
4 min read

Prevent SQL injection in Dart and Flutter by passing user input as arguments, never by joining strings into SQL. Packages like sqflite accept question mark placeholders with a separate argument list. Use that everywhere, and validate anything that cannot be bound, such as column names, against an allow list.

Does a mobile app really have SQL injection?

Yes, in a narrower way. A Flutter app that keeps data in a local SQLite file can be attacked through any input it feeds into a query: a search box, a deep link, a shared file, a push notification payload. The damage is usually limited to that user's local database, but it can corrupt records, bypass in-app checks, or read data the screen should not show. Dart on the server, with a database driver, has the full classic risk.

What does the safe pattern look like?

The sqflite documentation's own examples use placeholders. Statements such as an insert or update write the SQL with question marks and pass the values as a separate list. The unsafe versions in the same examples, where values are written straight into the SQL text, are fine only because the values are constants.

Here is a constructed example for a notes app.

// Unsafe: user text becomes SQL
db.rawQuery("SELECT * FROM notes WHERE title LIKE '%" + term + "%'");

// Safe: the value travels separately
db.rawQuery("SELECT * FROM notes WHERE title LIKE ?", ["%" + term + "%"]);

In the safe form, a quote inside term stays data. The percent signs are part of the bound value, so the user's own percent or underscore characters still act as wildcards. Escape them if that matters to you.

The OWASP SQL injection prevention guidance ranks parameterized queries first, and says bind variables cannot be used for table or column names, so those need an allow list or redesign.

Where do people slip?

  • Building ORDER BY clauses from a sort option the user picked.
  • Building IN lists by joining a list of values. Generate one placeholder per value instead.
  • Using the helper methods correctly in one file and raw strings in another.
  • Trusting data from a deep link or server response because it is not typed by the user.

What about the Dart server side?

Dart on the server uses database drivers whose APIs also accept parameters separately from the SQL text. The rule is identical: never build the statement from request data. If you use a query builder or an ORM, review its raw escape hatches the same way you would in any language, since those are where injection returns.

How to check or apply this

  1. Search the project for raw query, execute and rawDelete calls and read each one.
  2. Replace any string concatenation or interpolation with placeholders and an argument list.
  3. For sort and column choices, map user options to a fixed set of known strings.
  4. For IN lists, build the right number of placeholders and pass the values.
  5. Treat deep links, shared files and push payloads as untrusted input.
  6. Write tests that send quotes, semicolons and long strings to every search and filter path.
  7. On the server side, apply database least privilege so one bug reaches little.
  8. Keep database packages updated, and scan your dependencies for known issues.

Safeguard offers SAST code scanning and dependency scanning that can flag risky query construction and vulnerable packages in a project.

Quick check

Can I use a placeholder for a table name?

No. OWASP notes bind variables do not work for table or column names. Choose from a fixed list in your code.

Is local SQLite injection worth fixing?

Yes. It can corrupt or expose data in the app and often teaches habits that carry over to server code.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.