server principal not able to access database on second call - sql-server

I am using SQL Server 2017. I am in the role of sa for the server in question. I have two databases that are used in an ETL process. The ETL is coded in one database, and the raw imported tables are located in the staging database. All ETL is handled in SQL stored procedures that follow a pattern. The first step in each ETL SP is a call to a diagnostics table in the staging database.
My current ETL job is a wrapper around two of these ETL sps; the wrapper itself contains only code that accesses the main db.
The first SP can be called and successfully selects the data from the staging db, however, the second SP that has identical code up to the point of failure with the first, fails on accessing the diagnostics table and tells me
The server principal "sa" is not able to access the database "staging" under the current security context.
The problem stays if I comment out the first SP call, so something must be different in the definition of the two SPs, but I cannot spot it.
There are plenty of SPs that use the diagnostics staging table, so it is not a general problem (as stated in answers to similar questions that suggest changing security options in the staging database), but must be related to the new SP somehow.
Any suggestions?

There are three things to check/do.
First of all, the login associated with the user in database DB1 must also be associated with a user in DB2. This provides the login with a security context in database DB2. The sa login will map to dbo in both databases, so this should already be fine.
Second, the security context of the code being executed in DB1 must be "trustworthy". In other words, when the user context goes from DB1 back up to the server level and then down into DB2 via the cross-database call, the new user context has to trust the original login. There are two ways to do this, the quick and dirty and opens-up-possible-security-holes way, and the more complicated but safer way:
Quick and not entirely safe: alter database DB1 set trustworthy on.
Safe: Use signed modules
Third, in the general case you should check that the owner of DB1 and the owner of DB2 are the same (otherwise you can't cross database ownership chain): select owner_sid from sys.databases where name in ('DB1', 'DB2') But as with the first point, as a sysadmin you can take ownership of anything.

As too often happens, I failed to recognize a subtle difference between the two stored procedures: They both call a logging stored procedure, but this logging procedure has two variants, one with prefix sp_, and another one with the prefix usp. (Someone reacted to the Microsoft warning not to use sp_ as prefix.) The old one had an 'execute as owner' inside, which caused the error.
Replacing the function call with the new version fixed the error.
Sometimes the error is on the other side of the screen.

Related

Trying to understand stored procedure behavior

I'm tired of searching for this, but I couldn't find anything.
I have three databases in SQL Server and although all stored procedures are in the Main database, they work with tables from the other databases.
My question is: if you have the query
select name
from SecondDatabase.dbo.SomeTable
where id = 56
and this query is stored in the main database, will it run in the main database and go all the way to the second database and returns the data, or will it run in the second database and you have the select result directly?
(hope you understand my question)
I think you are misunderstanding the difference between a Database and an Instance.
An instance is the software running the SQL service. Each instance can have multiple databases. For example, there is a master database and a tempdb database for each instance of SQL Server, these are system databases. You can create any number of user databases. All these databases will be handled by the same SQL Server instance (on the same machine).
A particular client session is connected first to an instance and then to a particular database, thats why you include which database you will connect to by default on connection strings (or by login). When you write select name from SecondDatabase.dbo.SomeTable, you are telling the SQL service to retrieve data from the SecondDatabase, even if your session is linked to any other database. The engine will then use your login credential to match a user of the other database (since users go by database and logins by instance) to validate if it has enough privileges to query that table, before searching for the data.
A complete different story would be trying to access data from another instance (machine), in which you will need a linked server, a openrowset or such.
use FirstDatabase
select name
from SecondDatabase.dbo.SomeTable
where id = 56
Question:
will it run in the main database and go all the way to the second
database and returns the data, or will it run in the second database
and you have the select result directly?
Your first assumption is correct:
This query will run in a first database, it will use context and all settings (ANSI, query optimizer and statistic related) of the first database but will get data from a table of the second database.
Just an example from a life: if database have to stay in an old compatibility mode, but new T-SQL features need occasionally to be used, query can switch context to tempdb (which normally set to the latest compatibility level) and run queries referencing data from any other database where access is granted. Usage of those new features will not raise exception
The (now edited) query above will always execute on SecondDatabase.dbo.SomeTable even if the active database context was another database and even if the active user had a different default schema. This is because the object SomeTable is qualified with the schema and the schema owner.
Test to illustrate that the following still returns the executed results (assuming the objects exist and the active user context has access to them)
USE [OtherDatabaseSchema]
GO
SELECT TOP 10 *
FROM [SecondDatabase].[dbo].[SomeTable]

SQL Server security - stored procedures

I'm trying to implement security on our applications (which consist mainly of websites using SQL Server stored procedures).
Microsoft seem to suggest that best practice is to wrap all data functionality in stored procedures which we have done. Our structure is something like:
SQL Server database - [data1]
SQL Server database - [webSPs]
PHP / Symfony web apps
All the data resides in tables in [data1] and on the same server is another 'database' which just contains the stored procedures used by the web apps.
The SQL Server has a login 'webapp' which, as a user on [webSPs], has permissions just to run the stored procedures in [webSPs].
However, as these stored procedures read, write, append data on the [data1] tables, this user has to be a member of db_datawriter and db_datareader database roles in [data1].
This all works fine, but it seems there is a hole in the security here, as its then possible for a user accessing [webSPs] to not only run the stored procedures but write to any underlying table - is it possible to give permission that say something like:
you can read/write to tables on this database but only through SPs - not directly
From what I've read, I think it would work as I hoped if there wasn't the second database which I guess is breaking ownership chaining.
Thank you in advance.
Yes, but it's frankly a pain to implement. You can use module signing. Essentially, you:
Create a certificate in your WebSps database.
Backup and restore the certificate to the data1 database
Create a user from the certificate
Grant whatever read and write permissions to the certificate-based user in data1
Sign any stored procedure you want to use cross-database with the certificate by calling the add signature syntax
Keep in mind that every time that you change the procedure (either via alter or drop and create), the signature gets lost. So you'll be in a constant cycle of re-signing. You can read more about the process of module signing here

Determine Security Differences Between Two SQL Servers Database Users

We're running SQL Server 2012 / .Net Framework 4.5.1
We have an application that does the following:
Extract all table data from a source database using an instance of .Net's SqlBulkCopy.
Delete all data in a target database using regular SQL statements.
Deploy the data from the source database to the target database using an instance of .Net's SqlBulkCopy.
The third step is successful when the SQL connection uses my Active Directory account, but fails with the following error message when using a SQL Server account created for this purpose: Cannot find the object "[SchemaName].[TableName]" because it does not exist or you do not have permissions.
Interestingly, the process runs through about a dozen tables before hitting one that causes this error. Manual verification proves that a) The table exists on the target, b) The problem user can select from the table, and c) the problem user can manually insert into the table with the standard INSERT INTO [SchemaName].[TableName] ([Columns]) VALUES ([Values]) format. BCP also works for that user, but using SqlBulkCopy from a .Net application fails for the same user.
Our DBA (A pretty seasoned guy, so far as I can tell, actually) says that the database permissions on the target database are IDENTICAL between the two users, but reality would seem to suggest this is not the case.
Googling the problem shows that the user should have the db_owner or db_ddladmin roles. The user actually belongs to both.
Anyway, solving the local problem is of secondary concern, since I can get done what I need done with my AD account. What I'd really like to know is whether there is a baked-in way to compare the differences in permissions between two users. If not, can this be done with a T-SQL query of some kind?
Thanks, guys and gals!
Here's my permissions script that I use. It's generally the approach that everyone uses, unless they have a schema compare product via Visual Studio, Red Gate, etc. http://www.csvreader.com/posts/permissions_list.php
Are you specifying the schema on the destination table with SqlBulkCopy? Is it possible that you're running into a user owned schema instance?
It's also been my experience that SqlBulkCopy only requires select and insert on the destination table. BCP requires the escalated permissions that you described, which is another benefit of SqlBulkCopy.

How to fetch data from a different sql server database without having access?

I have two databases A and B in same SQL Server instance.. I need to write a trigger -- After update of a table in database B it will fetch data from few tables of database A and then insert data in some table of database B .. The issue is the user who will be accessing the database B does not have access to database A .. If I write a trigger with 'sa' account, will it work when the user inserts some data in database B? Also let me know the scenario what would I have to do if database A is in a different SQL Server?
It can work, but you have to do a few things to get there. Here's the easiest way (though not necessarily the best):
Set the owners of both Databases to 'sa'.
Turn on CROSS-DATABASE chaining for both databases.
Turn on TRUSTWORTHY for the source database (B).
Edit the Trigger and add WITH EXECUTE AS OWNER before the FOR clause.
Note that while this works, it has significant security considerations (particularly #2 and #3). Here is a link that explains this and some other methods and some of the security issues: http://msdn.microsoft.com/en-us/library/ms188304.aspx

Role for read-only access to execute SP/get result set, without indirectly modifying db?

For example
if a SQL Server user account is given
only the DataReader role and ability to execute one stored
procedure that modifies data in some
way, should the execution of that
stored procedure by that user cause
the modification to occur?
Overall, I want to give one user only read ability to the entire database including use of SQL syntax, Views and to execute any store procedures that return result sets. But I don't want any side effects to cause changes in the database. Therefore in the aforementioned stored procedure example, the attempt would ideally error out to satisfy my requirement, and all similar scenarios where a side-effect might cause a change. I want to ensure my database is protected against those.
Is this doable simply at the role level?
Product: SQL Server 2005 and up
Sure you can do this. Simply create a database role at the database level, and grant that role read on the tables and execute on only the stored procedures you want (i.e. the ones that read). Then, add the desired user(s) to your database role.
However, all things considering, if you are using stored procedures to read data, do so completely and do not grant read on tables for users of any level. Drive all data access through stored procedures (and views).
EDIT: Just noticed you said SQL 2005 "and up." If you are using SQL Server 2008, look at application roles instead of the traditional database roles.

Resources