Security & Module Risk

Unsafe Direct Database Writes in Custom Modules

Custom code that writes raw SQL to core tables skips validation, indexing, and cache, and can open injection. Here is how to find it and the right approach.

Jason Schuman · June 27, 2026

Direct database writes bypass everything Magento does for you

Magento provides models, resource models, and service contracts for writing data, and they exist for good reasons. Custom modules that skip them and write raw SQL directly to the database bypass all of that protection.

A direct write to a core table can look like a shortcut and behave like a landmine. It skips validation, events, indexing, and cache invalidation, and it can open a path to SQL injection.

This article covers what direct writes skip, the specific risks they create, how to find them in a codebase, and what the correct approach looks like.

What a direct write skips

When you save a product through Magento's normal path, a lot happens beyond the row changing. Validation runs, events fire, the relevant indexes are marked for update, and cached content is invalidated.

A direct SQL write to the same table does none of that. It changes the row and nothing else, leaving indexes stale, caches serving old content, and any logic that should have run on the change simply skipped.

A direct write to a core table changes the data and nothing else. The reindex that should have followed, the cache that should have cleared, and the validation that should have run all silently do not happen.

The data-integrity risk

Core tables are related, and Magento's write layer keeps those relationships consistent. Writing directly to one table without the others is how catalog, inventory, and sales data drift apart.

An integration that updates stock by writing straight to the inventory table, for example, can set a quantity without updating stock status or triggering the reindex that would reconcile the two. The number changes, but the derived data does not follow.

This is a frequent root cause of wrong stock, incorrect prices, and orders that reference inconsistent data. The direct write saved a step and created a discrepancy.

The SQL injection risk

Raw SQL built by concatenating variables into a query string is a classic injection vector. If any of those values comes from user input, an attacker can manipulate the query.

Magento's database layer supports bound parameters precisely to prevent this. Custom code that builds queries by string concatenation, rather than binding parameters, exposes the store to injection on exactly the inputs it fails to escape.

This turns a data shortcut into a security vulnerability. A direct write is not only an integrity risk; done carelessly, it is an attack surface.

The upgrade-coupling risk

Direct writes hard-code assumptions about the database structure. They name tables and columns directly, which couples the custom code to a schema that Magento controls and changes.

When an upgrade changes that structure, the direct write breaks, often silently, because nothing checks that the table still looks the way the code assumed. Code that went through the service contracts would have been insulated from the change.

This is why direct writes age badly. They work until the schema moves, and then they fail in ways that are hard to connect back to an upgrade.

Finding direct writes in the code

Direct writes are searchable. They tend to use a recognizable set of methods to get a raw connection and execute SQL.

Grep the custom modules for the patterns: calls that fetch a connection and run query, insert, or update against core table names. A resource connection used to write to sales_order, quote, or the catalog tables is the strongest signal.

grep -rn "getConnection()" app/code | grep -iE "->query\(|->insert\(|->update\(|->delete\("

Every hit is a candidate to review. Not all raw queries are wrong, but every one writing to a core table deserves a close look.

What the correct approach looks like

The right way to change data is through Magento's own layers. Repositories and service contracts handle entities like products, orders, and customers while maintaining the relationships and firing the events.

For a module's own custom tables, resource models with declarative schema are the standard, and they keep the code decoupled from raw SQL. When a query genuinely must be raw, bound parameters rather than string concatenation are non-negotiable.

These approaches are more code than a direct write, and that is the trade. They cost a little more to write and save the integrity, security, and upgrade problems the shortcut creates.

Bulk operations are the tempting case

The strongest temptation to write raw SQL is bulk work. Updating a hundred thousand products through the normal save path is slow, and a direct bulk update is dramatically faster.

The speed is real, and so is the cost. A direct bulk update skips the indexing and cache invalidation that the change requires, so the fast write leaves the storefront showing stale data until a separate reindex catches up.

Magento provides bulk-friendly APIs and import paths that keep the relationships intact for exactly this reason. When raw bulk writes are genuinely necessary, they must be paired with the explicit reindex and cache invalidation they skipped, or the store is fast and wrong.

Shortcuts that cost later

A direct database write is a small saving now and a standing risk afterward. It skips the work Magento does to keep data consistent, secure, and upgrade-safe, and it does so invisibly.

Knowing where your custom modules write directly to the database, and whether those writes touch core tables or user input, turns a hidden risk into a reviewable list. Finding unsafe direct writes is a core part of a security and module-quality review.