Written by

Team Lead at InterSystems
Article Ash Sherzhanov · Jul 31, 2025 3m read

Avoiding SQL Injection in InterSystems IRIS: The Case for Secure Query Practices

SQL injection remains one of the most critical vulnerabilities in database-driven applications, allowing attackers to manipulate queries and potentially access or compromise sensitive data. In InterSystems IRIS, developers have access to both Dynamic SQL and Embedded SQL, each with distinct characteristics. Understanding how to use them securely is essential for preventing SQL injection.

The Problem: Dynamic SQL and SQL Injection

Dynamic SQL constructs queries as strings at runtime. While this offers flexibility, it also creates a vulnerability if user input is not handled correctly. For example:

Set query = "SELECT Name, Age FROM Patients WHERE Age > "_age
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)

If age is user-provided, concatenating it directly into the query string exposes the application to injection. An attacker might supply a malicious value such as 0; DROP TABLE Patients, with disastrous results.

The Solution: Parameterised Queries

Parameterised queries are the best defence against SQL injection. Rather than concatenating inputs into the query, user values are bound as parameters. Here is a secure approach using Dynamic SQL:

Set query = "SELECT Name, Age FROM Patients WHERE Age > ?"
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)
If status {
    Set result = statement.%Execute(age)
    While result.%Next() {
        Write "Name: ", result.Name, ", Age: ", result.Age, !
    }
}

Here, the ? placeholder ensures the age value is treated strictly as data rather than executable code, significantly reducing the risk of injection.

Embedded SQL: Built-in Safety

Embedded SQL integrates SQL directly into ObjectScript, inherently protecting against SQL injection. The host variable syntax (:variable) securely binds parameters at compile time:

&sql(SELECT Name, Age INTO :name, :age FROM Patients WHERE Age > :minAge)

With Embedded SQL, there is no mechanism to concatenate raw user input directly into the query, thereby preventing injection.

Comparing Embedded SQL and Dynamic SQL

Feature Embedded SQL Dynamic SQL
Security Safe from injection due to host variables Secure if parameterised; risky if not
Flexibility Limited (static queries only) Highly flexible for dynamic scenarios
Searchability Easy to locate in class definitions Harder to analyse; queries are in strings
Performance Compiled at class compile time Parsed and optimised at runtime

When to Use Dynamic SQL

Dynamic SQL is useful when query structures must be determined at runtime, for example when adding optional filters:

Set query = "SELECT Name, Age FROM Patients"
If includeGender {
    Set query = query_" WHERE Gender = ?"
}
Set statement = ##class(%SQL.Statement).%New()
Set status = statement.%Prepare(query)
If status {
    Set result = statement.%Execute("Male")
}

Always remember to use parameterisation (?) for these dynamically built queries to maintain security.

Conclusion

Dynamic SQL allows for flexible query building but demands responsible usage to avoid SQL injection risks. Parameterised queries address this risk effectively. Meanwhile, Embedded SQL comes with built-in safeguards, making it an excellent choice for static queries. By using these approaches appropriately, developers can build robust, secure applications with InterSystems IRIS.

Comments

Zion Amsalem · Mar 17

its so important! minimize the risk of SQL injection is always one of the most important considerations as a developer and this is kind of unique way to make things harder

0
Joel Solon · Mar 17

Ash is correct! I prefer to explain it slightly differently. I don't consider SQL injection as a "problem" with Dynamic SQL because you should simply never concatenate user input into a query (using IRIS or any other platform). Always use placeholders which eliminates the risk. And the other 2 SQL options (Embedded SQL and Class Queries) accept input via host variables like :minAge. All automatically sanitize the input.
One further clarification: years ago, it's true that Dynamic SQL was the only one of the 3 options that was compiled at runtime; the other two were compiled at class compile time. But since Runtime Plan Choice (v2021.2), queries with input parameters in the WHERE clause (as discussed in this post) are compiled at runtime, whether you use Dynamic, Embedded, or Class Queries.

0
Ali Nasser · Mar 17

An attacker might supply a malicious value such as 0; DROP TABLE Patients, with disastrous results.

Though I agree that you shouldn't concatenate user input into dynamic SQL, this classic SQL attack wouldn't work in IRIS as it doesn't allow you to run more than one command in a single execution.

You can try it yourself, you will see that when you %Prepare your query that has "0; DROP TABLE XYZ" then IRIS will throw an error that says:

ERROR #5540: SQLCODE: -25 Message:  Input (;) encountered after end of query^ SELECT ...

0