How to set DENY permissions on a data user - sql-server

For a .net application we can store database connectionstrings like so
<connectionstrings config="cnn.config" />
I am trying to get as little as permissions as possible but there always seems to be a different way. To get info.
I am using the settings because they are more secure for one and two I have other people working on my application.
So I've set
USE database_name;
GRANT EXECUTE TO [security_account];
So the user can execute commands that's fine.
Then I've got db_reader and db_writer so they can read and write and this seems like a perfect marriage.
Still bad news the user can login and see the tables and store procedures definitions but not alter them, however, they can still see them.
How can I set the permissions to where the user can read, write. execute, and that is it PERIOD!?

Being able to see the definition of tables, stored procedures, etc is governed by the VIEW DEFINITION permission. So you can do:
DENY VIEW DEFINITION TO [yourUser];
And that user won't be able to see the definition for any object in the database. This also includes the ability to see other users.

If you want to stop the user from viewing the sp definition you need to specify the WITH ENCRYPTION option in the sp.
Adding a user to the db_datareader and db_datawriter role and granting EXECUTE will limit the user to reading writing and executing but they will still be able to see the sp definition by using the sp_helptext stored procedure and sys.sql_modules catalog view. See here for more information on sp and funciton encryption.
exec sp_addrolemember #rolename = 'db_datareader', #membername = 'testUser'
exec sp_addrolemember #rolename = 'db_datawriter', #membername = 'testUser'
GRANT EXECUTE TO testUser;
You can create an sp the WITH ENCRYPTION option by adding it before the AS keyword like this. See the WITH ENCRYPTION section of the CREATE PROCEDURE definition here
CREATE PROCEDURE HumanResources.uspEncryptThis
WITH ENCRYPTION
AS
SET NOCOUNT ON;
SELECT BusinessEntityID, JobTitle, NationalIDNumber, VacationHours, SickLeaveHours
FROM HumanResources.Employee;
GO
You can also alter functions by adding it after the returns keyword.
ALTER FUNCTION dbo.getHash ( #inputString VARCHAR(20) )
RETURNS VARBINARY(8000) WITH ENCRYPTION

Related

How can someone see all rows in sys.sysprocesses and not be SA?

The only user that can see the entire sys.sysprocesses is the SA. Is there a role I can place a user (or any other way) that a user can be made a member of so the user can see the entire sys.sysprocesses -all the rows for all users not just the processes for the user executing the select.
I connect to many SQL Server instances with a dbo account. I need to know if someone is connected to the instance. I cannot get SA privileges.
You can use code signing to accomplish what you're looking to do. Here's the code:
CREATE LOGIN [normalUser] WITH password = 'f00bar!23'
CREATE USER [normalUser]
GO
CREATE CERTIFICATE [codeSigningCert] WITH SUBJECT = 'Certificate for code signing', EXPIRY_DATE = '2099-01-01'
CREATE LOGIN [codeSigningLogin] FROM CERTIFICATE [codeSigningCert]
GRANT VIEW SERVER STATE TO [codeSigningLogin]
go
CREATE PROCEDURE dbo.processesProc
AS
BEGIN
SELECT *
FROM sys.sysprocesses AS s
END
GO
GRANT EXECUTE ON dbo.processesProc TO [normalUser]
GO
EXECUTE AS LOGIN = 'normalUser'
GO
EXEC dbo.processesProc
GO
REVERT
GO
ADD SIGNATURE TO processesProc BY CERTIFICATE [codeSigningCert]
GO
GRANT EXECUTE ON dbo.processesProc TO [normalUser]
GO
EXECUTE AS LOGIN = 'normalUser'
GO
EXEC dbo.processesProc
GO
REVERT
GO
By way of explanation, I'm creating an unprivileged login/user and a stored procedure for them to run. Without signing the procedure, the select exhibits the normal behavior (that is, it only displays the current process. But once I add a signature to the procedure using a certificate that has an associated login with the proper permissions, the result set blossoms.
Note: I created the procedure in master because I'm lazy. You can create the procedure anywhere you want so long as the certificate exists in the database you're creating the procedure in (so you can add the signature). Lastly, one gotacha with code signing is that the signature is lost when the procedure is modified. This makes sense as the signature is an attestation of the contents of the proc at the time of signing. If the body is changed, it will need to be re-signed.

Stored procedure to act differently based on user role

I'm trying to figure out if there is a way to achieve the converse of this:
can a SQL Server stored proc execute with higher permission than its caller?
I want to create a stored procedure which does one thing if the user is in a role, but a fallback option if they're not.
My first attempt tried to query the current user's roles, based on this:
How to query current user's roles
I tried to query what role a user was in, and decide what to do based on that. But if you set "mydomain\Domain Users" to a role (for example), users who belong to Domain Users aren't listed in the sys.database_role_members view. So users who were supposed to have permissions don't.
From here https://msdn.microsoft.com/en-us/library/ee677633.aspx
IS_ROLEMEMBER always returns 0 when a Windows group is used as the database
principal argument, and this Windows group is a member of another Windows
group which is, in turn, a member of the specified database role.
My next attempt works like this. Create a stored procedure with the actual permissions, and then a wrapper around it which calls the with lower permissions, and if that fails, perform the fallback action:
CREATE PROCEDURE [internal_myproc]
AS
BEGIN
-- do something here
END
GO
GRANT EXECUTE ON [internal_myproc] TO [Role1] AS [dbo]
GO
CREATE PROCEDURE [myproc]
AS
BEGIN
BEGIN TRY
EXEC [internal_myproc]
END TRY
BEGIN CATCH
-- perform fallback action
END CATCH
END
GO
GRANT EXECUTE ON [internal_myproc] TO [Role1] AS [dbo]
GO
GRANT EXECUTE ON [internal_myproc] TO [Role2] AS [dbo]
GO
GRANT EXECUTE ON [internal_myproc] TO [Role3] AS [dbo]
GO
The problem with this is that [Role1] and [Role2] both succeed in executing [internal_myproc] via [myproc]. If you take the code out of the stored procedure, it behaves the way it should, but because it's hidden inside a stored procedure, it gets implicit permissions to execute other stored procedures. I've experimented with "WITH EXECUTE AS" stuff, but it doesn't seem to change anything.
I also tried "IF HAS_PERMS_BY_NAME('internal_myproc', 'OBJECT', 'EXECUTE') = 1", suggested here MS SQL Server: Check to see if a user can execute a stored procedure , but that seems to not work in certain situations I haven't understood yet.
Is there a way to NOT grant those implicit permissions, to do a permission check inside a stored procedure? Or something equivalent?

Read-only clause for a query

I am wanting to allow users, via an ASP page, to run a query on my database, however I want this query to be restricted to read-only.
I will try to detect as much as I can in the ASP itself (detect words drop, update, delete etc.) and not run the query in these scenarios, but, is there a way I can just append a flag to the query when my ASP submits it to have it execute as read-only regardless of its content?
I realise that, if this is possible, update/delete/drop statements will error - this is fine.
I also want to avoid setting the whole DB or even table as read-only.
I also don't want to replicate my database in any form.
Thankyou for your help.
Dan
Your query should be dynamic. Main problem - sql injection.
I recommend this way:
create database role [DYNAMIC_SQL]. Give required select permissions on tables to [DYNAMIC_SQL] role.
create database user without login [dynamic_sql_proxy]. Map with [DYNAMIC_SQL] role.
create procedure [dynamic_sql__call] with hardcoded execution rights as [dynamic_sql_proxy]. Pass your custom query to this procedure.
Scripts:
(1)
USE [your_db]
GO
CREATE ROLE [DYNAMIC_SQL]
GO
GRANT SELECT ON [dbo].[your_table] TO [DYNAMIC_SQL] AS [dbo]
***
(2)
CREATE USER [dynamic_sql_proxy] WITHOUT LOGIN WITH DEFAULT_SCHEMA = [dbo]
ALTER ROLE [DYNAMIC_SQL] ADD MEMBER [dynamic_sql_proxy]
(3)
CREATE PROCEDURE [dbo].[dynamic_sql__call]
#err_code INT OUTPUT
,#err_msg NVARCHAR(4000) OUTPUT
,#sql NVARCHAR(MAX)
WITH EXECUTE AS 'dynamic_sql_proxy'
AS
BEGIN
SET #err_code = 0;
SET #err_msg = SPACE(0);
BEGIN TRY
EXEC(#sql);
END TRY
BEGIN CATCH
PRINT 'Please handle your error';
END CATCH;
RETURN #err_code;
END;
Thankyou Allan S. Hansen, I used the username/password method to good effect.

Difference EXECUTE AS targets

I am not quite clear about the difference between different EXECUTE AS targets in SQL Server : CALLER, SELF and OWNER, notably between the last two.
My understanding is that CALLER is the one who Execute/Call the procedure.
SELF is the specified user is the person creating or altering the module
OWNER is the current owner of the module
Could you explain and give some example who is the person creating/modifying and the owner of the module. Is 'module' here the stored procedure/function or the session or the database ? Including an example with SELF user will be great.
Being put very simple, SELF impersonates you as a database user who actually executed create / alter procedure the last time. It doesn't always have to be the schema owner, as you can imagine, it can be any person with permissions sufficient to create / modify given objects.
The OWNER mode impersonates you as the owner of the schema the procedure / function belongs to.
If you want to dig a little deeper (and is this case, there is always some room to dig in), below is a (relatively) simple example that can demonstrate you how things can work here. There are some shortcuts and implications that are specific to SQL Server which I omit deliberately because otherwise it would be too much to write. You can always read the documentation, though.
use master;
go
if db_id('TestDB') is not null
drop database TestDB;
go
create database TestDB;
go
use TestDB;
go
-- Just for the sake of example, so that everyone can create procs
grant create procedure to public;
go
-- Schema owner
create user [SomeUser] without login;
go
create schema [s1] authorization [SomeUser];
go
-- An ordinary user
create user [AnotherUser] without login;
go
grant execute on schema::s1 to AnotherUser as [SomeUser];
go
-- Database administrator
create user [DBA] without login;
go
alter role [db_owner] add member [DBA];
go
-- Although it's SomeUser that owns the schema, DBA creates objects in it
execute as user = 'DBA';
go
create procedure s1.SpCaller
as
select user_name() as [s1_caller];
return;
go
create procedure s1.SpSelf
with execute as self as
select user_name() as [s1_self];
return;
go
create procedure s1.SpOwner
with execute as owner as
select user_name() as [s1_owner];
return;
go
revert;
go
-- You can play with actual impersonation and look at results
execute as user = 'AnotherUser';
go
exec s1.SpCaller;
go
exec s1.SpSelf;
go
exec s1.SpOwner;
go
revert;
go

Determining the current security checks being made (SQL Server)

One thing that I've always hated more than just about anything in MS SQL Server is the way that security works. The security context constantly switches if you look at the server funny and it's often very hard (for me anyway) to predict or debug.
In dealing with an issue today, I though, "I wish I could just add a line to my code that would display the security context that SQL Server is using when this code runs." Does such a command exist? For example, SELECT security_context()
To be a little clearer... if I'm in a stored procedure and am therefor subject to the security context of the owner of the SP then I'd like to see that. If I'm in code that was called by sp_executesql and it's causing the security to be under the context of the SQL Server service account, then I would want to see that.
At least then I might be able to figure out why SQL Server thinks that I shouldn't have access to something.
Thanks!
EXAMPLE
-- Set up
CREATE USER Test_User WITHOUT LOGIN
CREATE TABLE Test_Security_Context (my_id INT)
INSERT INTO Test_Security_Context VALUES (1)
DENY SELECT ON Test_Security_Context TO Test_User
GO
CREATE PROCEDURE Test_Security_Context_SP
AS
SELECT SUSER_SNAME()
SELECT * FROM Test_Security_Context -- This will return ok
EXEC('SELECT SUSER_SNAME(); SELECT * FROM Test_Security_Context') -- SUSER_SNAME() will match above but select fails
GO
GRANT EXECUTE ON Test_Security_Context_SP TO Test_User
GO
-- Switch to the new user
SETUSER 'Test_User'
GO
-- Do the test
EXEC Test_Security_Context_SP
GO
-- Clean up
SETUSER
DROP PROCEDURE Test_Security_Context_SP
DROP TABLE Test_Security_Context
DROP USER Test_User
GO
Yes, there is such a pair of views that represents your current security context, considering all the details like EXECUTE AS or code signing:
sys.login_token for the server wide context
sys.user_token for the current database context
Every single access you get is ultimately derived from a row in the return of these results. Note that some access are implicit from hard coded role membership (like db_datareader database role or sysadmin server role).
Other that that:
ownership chaining is not related to security context: you are not under the 'context' of the SP owner. Ownership chaining simply states that access checks are skipped for objects owned by the same owner as current object (SP, View).
sp_executesql does not change the security context in any way
Not sure if this is what you mean by security context, but you can retrieve the user associated with your session like:
select SYSTEM_USER
This works for both a SQL Server login or a WIndows login. It even works inside stored procedures with execute as owner. For example,
create procedure dbo.Test
with execute as owner
as
select SYSTEM_USER
go
exec dbo.Test
select SYSTEM_USER
Prints:
sa
MyMachine\MyName
If you're looking for the Windows account that SQL Server is using to do things on your behalf, you could try to run whoami from the command like:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
EXEC master..xp_cmdshell 'whoami'
For me, that returns nt authority\network service.
I think you want to use CURRENT_USER to see the current security context. Here's an example:
SELECT CURRENT_USER AS 'Current User Name';
GO
EXECUTE AS LOGIN = 'junk'
GO
SELECT CURRENT_USER AS 'Current User Name';
GO
REVERT
SELECT CURRENT_USER AS 'Current User Name';
GO
with output (note: I'm admin on my SQL Server for this)
Current User Name
------------------
dbo
(1 row(s) affected)
Current User Name
------------------
Junk
(1 row(s) affected)
Current User Name
------------------
dbo
(1 row(s) affected)

Resources