SQL Server Extend dynamic SQL query with where part fetched from Stored Procedure
Typical things you can make SQL injection safe:


Values

DECLARE @UserSuppliedValue varchar(32) = 'foo';

DECLARE @sql nvarchar(max) = N'SELECT ... FROM dbo.bar
  WHERE value = @value;';
EXEC sys.sp_executesql @sql, N'@value', @UserSuppliedValue;

Entities, like table/column names

DECLARE @UserSuppliedTable  = N'dbo.bar',
        @UserSuppliedColumn = N'foo';

IF EXISTS -- make sure table exists
(
  SELECT 1 FROM sys.tables
    WHERE [object_id] = OBJECT_ID(@UserSuppliedTable)
)
AND EXISTS -- or make sure both exist
(
  SELECT 1 FROM sys.columns
    WHERE [object_id] = OBJECT_ID(@UserSuppliedTable)
      AND name = @UserSuppliedColumn
)
BEGIN
  DECLARE @sql nvarchar(max) = N'SELECT '
    + QUOTENAME(@UserSuppliedColumn) + N' FROM '
    + @UserSuppliedTable + N';';
  EXEC sys.sp_executesql @sql;
END



I listed both there for illustration but you would only need one or the other in most scenarios.

If you're trying to pass an entire WHERE clause to a dynamic SQL statement, there's little you can do to make that injection safe, except maybe try to execute it with PARSEONLY to make sure it's not garbage. That doesn't help prevent valid SQL injection commands but it does at least help prevent utter garbage.

DECLARE @where nvarchar(max) = N' WHERE junk;'

DECLARE @sql  nvarchar(max) = N'SELECT * FROM sys.objects ' + @where;
DECLARE @test nvarchar(max) = N'SET PARSEONLY ON;' + @sql;

DECLARE @hr int;
BEGIN TRY
  EXEC @hr = sys.sp_executesql @test;
  EXEC sys.sp_executesql @sql;
END TRY
BEGIN CATCH
  PRINT N'Statement was garbage.';
END CATCH;


If @where were the following...

DECLARE @where nvarchar(max) = N' WHERE 1=1; DROP TABLE dbo.bobby;';


...then this parsing trick doesn't work (since parsing just validates syntax, not object existence or permissions).

So you need to put proper logic into the stored procedure that produces @where and make sure there are appropriate controls, permissions, and auditing around that stored procedure, as well as reducing the permissions of the user/group executing the final @sql - this should not be sa, for example.

I've written a little about this topic:


Protecting Yourself from SQL Injection in SQL Server - Part 1
Protecting Yourself from SQL Injection in SQL Server - Part 2