SQL Security
A database usually holds a company’s most valuable data. These practices keep it safe from both attackers and accidents.
On this page · 5 min · Advanced
On this page
- In simple words
- Learning objectives
- Explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Advanced
- Updated
- Oct 27, 2026
Related tutorials
In simple words#
Office staff get keys only to the rooms they need. Logins, users and roles give each app or person exactly the database permissions they need — no more.
- Login / user
- Server identity / database identity.
- Role
- A group of permissions.
- Least privilege
- Grant the minimum needed.
Learning objectives#
- Distinguish logins, users and roles
- Apply least privilege with roles and GRANT
- Prevent SQL injection
- Write safe dynamic SQL
Explanation#
A login authenticates to the server; a user maps that login into a database. Permissions are best granted to roles, and users are added to roles.
Least privilege: applications should get only what they need — often EXECUTE on stored procedures or SELECT/INSERT on specific schemas — never sysadmin or db_owner.
SQL injection happens when user input is concatenated into SQL text. Parameterised queries and procedures send values separately, so input is never executed as code.
When dynamic SQL is unavoidable, use sp_executesql with parameters and QUOTENAME for identifiers such as column names.
Code example: A least-privilege app account and safe dynamic SQL#
CREATE LOGIN shop_app WITH PASSWORD = 'Use-A-Long-Random-Secret-1!';
CREATE USER shop_app FOR LOGIN shop_app;
CREATE ROLE app_role;
GRANT SELECT, INSERT, UPDATE ON SCHEMA::dbo TO app_role;
GRANT EXECUTE ON SCHEMA::dbo TO app_role;
ALTER ROLE app_role ADD MEMBER shop_app;
-- Safe dynamic sort column + parameterised value
DECLARE @sortColumn SYSNAME = N'Price', @category NVARCHAR(30) = N'Furniture';
IF @sortColumn NOT IN (N'Name', N'Price') THROW 50000, 'Invalid sort column', 1;
DECLARE @sql NVARCHAR(MAX) =
N'SELECT Name, Price FROM dbo.Products WHERE Category = @cat ORDER BY ' + QUOTENAME(@sortColumn) + N';';
EXEC sp_executesql @sql, N'@cat NVARCHAR(30)', @cat = @category;
Name Price Chair 4500.00 Desk 12000.00
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The app gets its own login and database user.
- Permissions go to a role, and the user joins the role — easy to audit and change.
- The sort column is checked against an allow-list and wrapped with QUOTENAME.
- The category value is passed as a parameter to sp_executesql, so it can never become SQL code.
Real-world use case#
A data breach investigation is far less severe when the compromised app account could only execute a few procedures instead of reading every table.
Common mistakes#
If the app is compromised, the attacker controls the whole server.
'… WHERE Name = ''' + @name + '''' lets input like x' OR '1'='1 change the query. Always parameterise.
Best practices#
- Grant permissions to roles, not individuals
- Give apps the minimum permissions they need
- Parameterise every query; use sp_executesql for dynamic SQL
- Encrypt connections and back up regularly; consider Always Encrypted for sensitive columns
Remember this#
Least privilege for every login.
Apps use their own user, never sa.
Parameters in apps prevent SQL injection.
Practice exercise#
Your task: Grant a reporting role read-only access to a reporting schema.
Show solution
CREATE ROLE reporting_reader;
GRANT SELECT ON SCHEMA::reporting TO reporting_reader;
Mini quiz#
What is the main defence against SQL injection?
Want more? Practise all SQL Server MCQs · SQL Server interview questions
My notes#
Saved in this browser.