Mitigation Strategy: Prepared Statements and Query Builder Preference
-
Description:
- Prioritize the Query Builder: Developers should primarily use Diesel's query builder methods (e.g.,
.filter()
,.select()
,.insert()
,.update()
,.delete()
) for constructing database queries. These methods automatically generate parameterized SQL queries (prepared statements). - Minimize
sql_query
: Thesql_query
function, which allows raw SQL execution, should be used only when absolutely necessary and with extreme caution. - Safe Parameter Binding (for
sql_query
): Ifsql_query
is unavoidable:- Never interpolate user-provided data directly into the SQL string.
- Use Diesel's
bind
method to pass parameters separately. Specify the correctdiesel::sql_types
for each parameter. - Example (Correct):
let user_id = get_user_input(); // Assume this gets user input let results = sql_query("SELECT * FROM users WHERE id = ?") .bind::<diesel::sql_types::Integer, _>(user_id) .load::<User>(&mut conn)?;
- Example (Incorrect):
let user_id = get_user_input(); let results = sql_query(format!("SELECT * FROM users WHERE id = {}", user_id)) .load::<User>(&mut conn)?;
- Prioritize the Query Builder: Developers should primarily use Diesel's query builder methods (e.g.,
-
Threats Mitigated:
- SQL Injection (Severity: Critical): The primary threat. Attackers can inject malicious SQL code to bypass authentication, steal data, modify data, or even execute arbitrary commands on the database server.
-
Impact:
- SQL Injection: Risk reduced from Critical to Very Low (assuming proper implementation).
-
Currently Implemented:
- Example: Query builder is used for 95% of queries.
sql_query
is used insrc/legacy_reports.rs
.
- Example: Query builder is used for 95% of queries.
-
Missing Implementation:
- Example: The
src/legacy_reports.rs
code needs review and refactoring to use the query builder or safer parameter binding.
- Example: The
Mitigation Strategy: Schema Validation and Type Safety (Diesel-Specific Aspects)
-
Description:
- Diesel Migrations: All database schema changes must be managed through Diesel's migration system. Developers should use
diesel migration generate <migration_name>
to create new migrations anddiesel migration run
to apply them. schema.rs
Generation: After each migration, developers must rundiesel print-schema > src/schema.rs
to update theschema.rs
file. This file should be committed to version control. The build process should fail ifschema.rs
is outdated.- Enum Mapping (if applicable): If the database uses enums, ensure they are correctly mapped to Rust enums using Diesel's features (e.g.,
#[derive(DbEnum)]
). Document the mapping clearly.
- Diesel Migrations: All database schema changes must be managed through Diesel's migration system. Developers should use
-
Threats Mitigated:
- Data Corruption (Severity: High): Mismatches between the application's expected schema and the actual schema can lead to incorrect data being written.
- Application Errors/Crashes (Severity: Medium): Schema mismatches can cause runtime errors.
-
Impact:
- Data Corruption: Risk significantly reduced.
- Application Errors/Crashes: Risk reduced.
-
Currently Implemented:
- Example: Diesel migrations are used consistently.
schema.rs
is generated and committed.
- Example: Diesel migrations are used consistently.
-
Missing Implementation:
- Example: Enum mapping is not explicitly documented.
Mitigation Strategy: Over-Fetching and Under-Fetching Prevention (Diesel-Specific)
-
Description:
- Selective
SELECT
Statements: Use the.select()
method in Diesel's query builder to explicitly specify the columns to retrieve. AvoidSELECT *
unless all columns are genuinely required. - Targeted Structs: Define Rust structs that represent only the data needed for a specific operation. Use these structs with
.select()
to avoid fetching unnecessary data. - Association Management: Carefully manage database associations (e.g.,
belongs_to
,has_many
). Use eager loading (e.g.,.load()
,.get_results()
) or lazy loading (e.g.,.load_iter()
) appropriately. - Pagination: For queries that might return large result sets, implement pagination using Diesel's
.limit()
and.offset()
methods.
- Selective
-
Threats Mitigated:
- Data Exposure (Severity: Medium): Over-fetching can inadvertently expose sensitive data.
- Performance Degradation (Severity: Medium): Fetching unnecessary data increases database load.
- Application Errors (Severity: Low): Under-fetching can lead to errors.
-
Impact:
- Data Exposure: Risk reduced.
- Performance Degradation: Risk reduced.
- Application Errors: Risk reduced.
-
Currently Implemented:
- Example:
.select()
is used in some queries. Pagination is implemented for some list views.
- Example:
-
Missing Implementation:
- Example:
SELECT *
is still used in many queries. Pagination is missing for several large data sets.
- Example:
Mitigation Strategy: Transaction Management (Diesel-Specific)
-
Description:
- Explicit Transactions: Use
connection.transaction(|| { ... })
orconnection.build_transaction().run(|| { ... })
to wrap database operations that must be executed atomically. - Isolation Levels: When using
build_transaction()
, explicitly set the desired isolation level using methods like.read_committed()
,.repeatable_read()
, or.serializable()
. Understand the implications of each level and document the choice. - Error Handling within Transactions: Diesel's
transaction
method automatically handles rollbacks on error, ensure that custom error types implement the necessary traits for proper rollback.
- Explicit Transactions: Use
-
Threats Mitigated:
- Data Inconsistency (Severity: High): Incorrect transaction handling can lead to partial updates.
- Race Conditions (Severity: High): Concurrent transactions without proper isolation can lead to race conditions.
-
Impact:
- Data Inconsistency: Risk significantly reduced.
- Race Conditions: Risk reduced.
-
Currently Implemented:
- Example:
connection.transaction()
is used for some multi-step operations.
- Example:
-
Missing Implementation:
- Example: Isolation levels are not explicitly considered or documented.