SQL Server 2005: Deny Access to sp_prepexec - sql-server

I have a SQL Server 2005 database that is suffering from lock starvation because of some poorly behaving applications running cursors through sp_prepexec. I imagine that one of the applications running a cursor has "forgotten" to close it. I would like to deny access to sp_prepexec for specific users to prevent the lock starvation from occurring in the production database.
When I'm attempting the following syntax:
use master
deny execute on sp_prepexec to applicationUser
I have to use master because when I don't: Permissions on server scoped catalog views or system stored procedures or extended stored procedures can be granted only when the current database is master.
But the problem is that applicationUser is not a user in the master database. So I get the following error:
Cannot find the user 'applicationUser', because it does not exist or you do not have permission.
If I add that user to master it doesn't seem to prevent applicationUser from running sp_prepexec in the production database.
Can somebody point me in the right direction with this?

Could you deny the permissions to a role, rather than a specific user?
USE master
GO
DENY EXECUTE ON production.sp_prepexec TO public
GO

Related

Do I need to grant permission on the database if I already granted it to stored procedure?

I have a SQL Server database (let's call it DB1) with a few stored procedures, and these stored procedures are executed by my Web API (that will be consumed by my mobile app later).
These stored procedures get data from DB1 and from another database (let's call it DB2).
I created a SQL Server login to be used by the Web API with public and dbcreator server roles and only public database role in both DB1 and DB2.
Then I followed this guide and granted Execute permission on the stored procedures for the login.
But when I try to execute my Web API method that uses one of the procedures, I get an exception:
The SELECT permission was denied on the object 'APP_USERS', database 'DB2', schema 'dbo'.
So, do I have to grant the permissions on DB1 and DB2 for this to work, even if I already granted it on the stored procedure? Or am I just granting the wrong permission to the stored procedure?
Note: I used to grant permissions directly on the database for each login, because all applications consulting the database were internal of our enterprise (until now). But this mobile app will be public. I talked to a security expert who told me that this practice is insecure, and advised me to grant the permissions only on the stored procedures.
It looks like you've been introduced (whether you wanted to be or not!) to something called database permissions chaining. At a high level, you're allowed to have objects in your own database reference other objects and only have to grant permissions on the referencing object so long as both the referenced and referencing object are owned by the same database principal (i.e. user). For example, if I have a table that I own, I can write a stored proc doing whatever (say a SELECT) against the table and then grant execute on the proc to another user. When the other user goes to execute the proc, permission chaining kicks in and says "the proc and the table are owned by the same user - the execute permission is sufficient"
But! By default, the permissions chain is broken when the referenced object is in another database. Why? I can only speculate as to creators' intent, but imagine a multi-hosted database server and I'm an malicious actor. If I have my own database, I could write a proc that says select * from OtherDb.dbo.Users;, grant permissions on that proc and exfiltrate data from other users' databases.
There are a couple of ways around this:
You can enable cross db ownership chaining at the server level. I don't recommend this, but it is an easy button out of the problem you have.
You can grant permissions on the objects referenced in the procedure. This may be okay, depending on why you're gating data access through stored procedures (which, full disclosure, I like to do in general). This would be a simple grant select on dbo.APP_USERS to «some DB2 principal - a user or group»;. The downside here is that the principal to whom the permissions are granted can do any select on the table now, thereby bypassing the proc.
You can sign your stored procedures. This is a little more involved, but is the more secure option. It involves creating a certificate or asymmetric key in both databases, creating a user based on the same, granting permissions to that user, and finally calling add signature on the related procs. You'd think you're done, but you'll need to re-apply that signature any time someone changes the procedure definition. Why? Let's say that you sign the proc today but then I change it to do something unintended (either innocently or maliciously). If the signature persisted through an alter procedure, the original proc could be a Trojan horse.
Here is a rough sketch of the module signing dance.
use Db1;
create certificate ModuleSigningCert ...;
add signature to dbo.YourProc by certificate ModuleSigningCert;
use Db2;
-- import ModuleSigningCert - either by backup certificate/create certificate
-- you technically only need the public key portion
create user SigningUser from certificate ModuleSigningCert;
grant select on dbo.YourTable to SigningUser;
For what it's worth, I don't know that "database will be accessed by a public app" necessarily means "and now we need to do cross-database stuff". It may, but it may not. For instance, if the public app still accesses the database through an internal application server, you're not getting much security-wise with the multi-database setup.

Sql Server Agent job failing to execute stored procedure

I have a stored procedure that I can execute in SSMS with a non domain SQL Server user.
This stored procedure selects data from tables in one database (DB1) truncates and selects into a table in DB2.
The user has datareader,datawriter and dbowner for both databases.
Problem:
When I execute the stored procedure via SS Agent with execute as the user I get the following error
The server principal [user] is not able to access the database [DB1]
under the current security context.
Actions taken So far:
I have tried to resolve this so far by:
Turning on db chaining for both databases
Deleted the user from DB1 and added again
Checked using EXEC sp_change_users_login #Action=’Report’ to see if user orphaned. As this is a database that is a restore of a live one. However I added the user after the restore. The user was not listed as orphaned
A possible workaround if you don't want to have the owner be sa is to have the user be a member of msdb and grant the the SQLAgentOperatorRole in msdb. See if that works.
But to be honest, either use sa or a dedicated service account with enough permissions. It's better if the job runs under that context.

What level of SQL Server access is required to view, but not execute, stored procedures and their code?

I have been granted db_datareader access to our production SQL Server database, but they also granted me the denywrite permission, as a safety precaution to make sure I absolutely cannot break our services during the course of my investigations.
However, I am finding that I cannot see our stored procedures - the list appears empty.
We should have hundreds of stored procedures in our production environment, so I'm perplexed as to why they aren't showing up in the object explorer.
Our infrastructure manager granted me the rights, but he doesn't know anything about SQL Server, so management has asked me to assist with figuring out which SQL Server permissions I need, since I am the developer.
So I need to know what I'm missing here - I assumed db_datareader would let me view everything, including stored procedures and metadata, but apparently I was mistaken. :)
Admittedly, I'm not the most knowledgeable when it comes to permissions. However, I believe the minimum permission you need here is view definition. You can also grant this permission to a role if it makes more sense for your situation.
use MyDB
GO
GRANT VIEW DEFINITION to MyUser
The above will grant view_definition to MyUser for the MyDB database.
Changing to the following will grant view definition on any database:
use master
GO
GRANT VIEW ANY DEFINITION to MyUser
Sources:
https://msdn.microsoft.com/en-us/library/ms345443.aspx#Security
http://www.mssqltips.com/sqlservertip/1593/granting-view-definition-permission-to-a-user-or-role-in-sql-server/

How to disable SQL Server Management Studio for a user

Is there a way to prevent users from getting into SQL Server Management Studio so that they can't just edit table rows manually? They still need to access the tables by running my application.
You can use the DENY VIEW ANY DATABASE command for the particular user(s). This is a new feature available in SQL Server 2008.
It prevents the user from seeing the system catalog (sys.databases, sys.sysdatabases, etc.) and therefore makes the DB invisible to them in SQL Management Studio (SSMS).
Run this command from the Master Database:
DENY VIEW ANY DATABASE TO 'loginName'
The user is still able to access the database through your application. However, if they log in through SSMS, your database will not show up in the list of databases and if they open a query window, your database will not appear in the dropdown.
However, this is not fool-proof. If the user is smart enough to run the Query Command:
USE <YourDatabaseName>
Then they will see the database in the Query Analyzer.
Since this solution is taking you 90% there, I would give the database some obscure name not let the users know the name of the database.
You DO NOT need to worry about them having access to the tool. Simply make sure they do not know any of the SQL logins for the specific Databases that have read/write permissions, if they do, change the password. If they have access to the DB via Windows Authentication, make sure that they are in a datareader role. You can use roles to manage what the users can do in SQL.
You can use a trigger.
CREATE TRIGGER [TR_LOGON_APP]
ON ALL SERVER
FOR LOGON
AS
BEGIN
DECLARE #program_name nvarchar(128)
DECLARE #host_name nvarchar(128)
SELECT #program_name = program_name,
#host_name = host_name
FROM sys.dm_exec_sessions AS c
WHERE c.session_id = ##spid
IF ORIGINAL_LOGIN() IN('YOUR_APP_LOGIN_NAME')
AND #program_name LIKE '%Management%Studio%'
BEGIN
RAISERROR('This login is for application use only.',16,1)
ROLLBACK;
END
END;
https://www.sqlservercentral.com/Forums/1236514/How-to-prevent-user-login-to-SQL-Management-Studio-#bm1236562
I would suggest you lock down the database and give appropriate read-only (or other) rights to the user. That way the user can still use management studio to run select queries and such.
If you don't want the user to have any rights at all then you could do that as well.
If your application is running as a service/user account then only that account requires access to the database. The individual users' account do not require any access to the database and therefore they won't even have read access. Your app will be the gateway to the data.
If the users are running the application under their user accounts then grant them read-only permission. You can simply add them to the db_datareader role.
Hope this helps!
You can deny 'Users' access rights to the ssms.exe executable file, while granting the relevant users/administrators rights to it.
If your application only used stored procedures to modify the data, you could give the end users access to run the stored procs, but deny them access to modify the tables.
Don't let them know what the database login is.
If you can't restrict the login, use stored procedures exclusively for updates and disable any CREATE,DELETE,INSERT, or UPDATE permissions for that user.
An Application Role will allow you to secure database objects to your application instead of the logged on user.
I agree with Jon Erickson as a general rule
do not allow any users access to the tables, but only allow access through stored procs
do not allow general user accounts access to stored procs, but only the account your app runs under (whether it's an integrated login or SQL login)
Make well usage of Database Roles, if Users should only have SELECT (read) access assign them the db_datareader Role. Even if they login using SSMS they will can execute only SELECT statements.

Hide SQL database from Management Studio

How can you hide databases you do not have access rights to when logging into SQL Server 2005 / 2008?
Currently if a user connects, they see all the databases on the server, meaning they have to scan though the list to find their database.
After hours of trying to figure out how to create a user account which only has access to 1 DB, and can only see that DB. I think i figured it out!!!!
Create a user account ( make sure its not mapped to any Database, otherwise you will get the final error Msg 15110, Level 16, State 1 and note proposed solution)
USE [master]
GO
CREATE LOGIN [us4]
WITH PASSWORD=N'123',
DEFAULT_DATABASE=[master],
CHECK_EXPIRATION=OFF,
CHECK_POLICY=OFF
Right Click on the upper section of the SQL (SQLSERVER Name)>Properties>Permissions>Click on the user account, and select Deny to view databases.
use [master]
GO
DENY VIEW ANY DATABASE TO [us4]
Right Click on the newly created DB, Properties,Files, and change the Owner to the newly created account.(important note: ALTER ROLE [db_owner] ADD MEMBER [us4] does not work)
USE [dbname]
GO
EXEC dbo.sp_changedbowner #loginame = N'us4', #map = false
At this point, once the user logs in he will see the Master,tempdb and will also see the new DB which he is a DB Owner of..You may want to go to Tools>Option and enabled the option to hide system objects so that you don't show the master,tempdb,etc. You may also need SP1 if this option does not work
Msg 15110, Level 16, State 1, Line 1
The proposed new database owner is already a user or aliased in the database.
proposed solution to Msg 15110: to resolve above error simply delete the user from database security node and try again
Hope that helps...
Nikhil
This actually won't work the way that makes sense or that you might expect that it would.
You REVOKE VIEW ANY DATABASE from the public role, but then the user has to be the database owner of the database or it can't be seen, but it still can be accessed.
The problem is a Database Engine Security shortcoming and not likely to be fixed in the current or future release of SQL Server.
Erland Sommarskog opened the following connect item for this a while ago, and it recently was discussed on twitter and with Microsoft by the SQL MVP's.
Vote for the connect and help make it more of a priority for Microsoft to fix:
Connect Feedback
Basically the permissions are stored at the database level, so it would require enumerating each database to determine if the user has connect rights to display the database in the object explorer, which is an expensive task to perform and how the older EM used to do things.
The proposes solution is for this information to be maintained at the server level as well, which is a major change.
You would need to revoke the permission 'VIEW ANY DATABASE' from the role PUBLIC (SQL SERVER 2005 onwards)
Add user to DB as Db owner after removing VIEW ANY DATABASE rights
This will show only the database owned by the login in SSMS.
USE master; GO
DENY VIEW ANY DATABASE TO [loginname]; GO
USE [your db]; GO
DROP USER [loginname]; GO
USE master; GO
ALTER AUTHORIZATION ON DATABASE::[your db]TO [loginname]; GO
Note: this requires the login to exists already
There appears to be a server-side setting on MS SQL 2005 and 2008 to restrict the databases a user may see. I found the following text at sql-server-performance.com
In SQL Server 2005 it is possible with a new server side role that has been created. VIEW ANY DATABASE permission is a new, server-level permission. A login that is granted with this permission can see metadata that describes all databases, regardless of whether the login owns or can actually use a particular database. Please note By default, the VIEW ANY DATABASE permission is granted to the public role. Therefore, by default, every user that connects to an instance of SQL Server 2005 can see all databases in the instance.

Resources