Question

I've to implement the following requirement:

Access to SQL Server instance shall be allowed only from a C# application. Users shall not be able to access any database (even those in which they have access) via SQLCMD, SSMS. Access using SSMS shall be allowed only for logins that are sysadmin, serveradmin. Connection from one specific host machine shall be denied. Connections from a specific C# application shall be allowed.

Is it possible, secure and safe to implement this via logon trigger? If yes, how can I implement it in a reliable manner.

In order to apply the logon trigger, is it necessary to be done via SSMS, only once to be activated. In case something goes wrong how can I disable it?

No correct solution

OTHER TIPS

You can try to do this...

CREATE TRIGGER CSharpOnly ON ALL SERVER FOR LOGON
AS
BEGIN
     IF APP_NAME() NOT IN (N'Your C# app_name() according to SQL Server')
     BEGIN
          RAISERROR('Please use sea sharp.', 16, 1);
          ROLLBACK;
     END
END

...but, since application name and other connection properties are easily spoofed, it is very easy for your users to bypass them - they can just tell SSMS, for example, that it should present itself as the same APP_NAME(). So, it all depends on your definition of "in a reliable manner."

When things go south

In order to fix a bad trigger, you can bypass logon triggers entirely by using the Dedicated Administrator Connection (DAC) to connect and modify / disable the trigger. You can also keep your initial connection established when you create / enable the trigger - existing connections won't go through the trigger again unless they get disconnected.

Maybe this would be a better approach than a Logon Trigger. Instead, create the Logins and correlating Database Users for your domain users who should have access to the database via your C# app. Set the permissions on the Database Users appropriately. Disable their Logins on the server. Create a SQL Login and Database User dedicated for your C# application that has the highest level permissions any domain user would theoretically need.

In your C# app, use the dedicated SQL Login account to connect to your database. Use Windows Authentication to get the current domain user running the application. Whenever you make a database call, prefix the SQL code being executed with EXECUTE AS USER = followed by the Database User that corresponds to that domain user (the names should theoretically match exactly). For example EXECUTE AS USER = 'JD'; SELECT SomeField FROM SomeTable. Then the permissions you setup on your SQL instance will be applied for that database call to your dedicated SQL Database User, and you get your security enforcement and no domain user will be able to connect to your SQL instance any other way.

I also think this helps reduce the chances of a spoofing issue like Aaron Bertrand pointed out, via obscurity, since a domain user would need to know the name of the dedicated SQL Login (and password for that matter) to be able to connect to the server. And the only way they'd be able to do that is if they had the source code to your app. But even then, if you implement secure connection strings in your application (depending on the database framework you're using) theoretically the password should still be secured.

Downsides to this are it may be confusing to developers who are inexperienced with SQL Server on how the security permissions are being enforced, and if you want to use AD Groups for your Database Users then you need to implement an additional call in your C# code to look up the AD Groups that correspond to the current domain user running the app, before you EXECUTE AS that AD group's correlating Database User. There's probably gotta be other reasons this isn't necessarily the best practice from a security model perspective or a code maintainability perspective, but seems like a nifty solution to your problem anyway.

I think you could do something using the SQL 2017 feature "Always Encrypted with secure enclaves" which allows clients with the right certificate to access the encrypted data, presumably you'd then elevate the privileges / use an application role to access the database while keeping all ordinary users DENYied from using tables or SPs. I've never worked with this feature though & am not very strong at encryption - so this is only half an answer, maybe someone else can add the other (more difficult) half ?

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top