User login information not pulling active users - sql-server

Need some help on this.
I have a job that pulls user logins once a day and inserts into a table. The purpose of this is to gather user info to see what accounts can be dropped after a while.
Insert into [User_Login_Audit]
Select
login_name, max (login_time) as last_login_time,
last_successful_logon, (select ##servername) as server_instance, getdate()
from
sys.dm_exec_sessions
group by
login_name, last_successful_logon;
I am using the query below to gather the user information using registered servers.
SELECT
[Login_name],
MAX([last_login_time]) AS Last_login_date,
[server_instance],
DATEDIFF(day, getdate(), max([last_login_time])) Days
FROM
[Benefitfocus_DBA].[dbo].[User_Login_Audit]
WHERE
Login_name NOT IN ('NT AUTHORITY\SYSTEM', 'sa','')
AND last_successful_logon IS NOT NULL
GROUP BY
Login_name, server_instance
I've noticed that the top query pulls all information from sys.dm_exec_sessions. Some of the logins it records have been removed from security on that instance. I need only the users that are Active and / or present on the instance. Can someone suggest a way to either modify the Insert query to pull only users currently in security on the instance or a way on the second query to sort which users are active on the instance?
I would appreciate any insight into this.

Take a look at sys.server_principals and see if it will work for you. You would do a join on the 'name' column in that table from the login_name that you currently have in your query. I'm not sure if you are deleting the logins or disabling them, but that system view could tell you if either condition was true.
The big caveat that I know of is that if a user has access to the server through an Active Directory group, you will find the login_name of the user in your current query but it will not match up to anything in the system view because the relevant entry there will be for the AD group. There are some ways to handle that, but not knowing if that is even relevant in your system I will not go into detail here.

After trying a few things I found that adding the following in the first collection script limited it to the current users:
where login_name in (SELECT name FROM sys.database_principals where type<>'R')

Related

Block access to column without changing query

I have a Microsoft SQL Server with data that needs to be protected (certain sensitive columns of some tables) and an application that queries that database like this:
SELECT BoringColumn, SensitiveColumn FROM Table
I have the following restrictions:
I have multiple users (3-4) each with different columns visible or not.
In this example SensitiveColumn should not be accessible.
I can not directly update the queries that the application sends
What did I try already:
I tried to use SQL Servers Dynamic Data Masking feature. However it's not granular enough, you can just turn it on or off per user but not just for some columns. And its can leak data in queries, the link above explains that as well.
I know I can just deny the user SELECT on Table.SensitiveColumn.
However then any existing query asking for the table just breaks with permission errors.
What other options do I have left?
Ideally I would like something that replaces the query on the serverside and executes something like this:
SELECT BoringColumn, 'N/A' as SensitiveColumn FROM Table
I think I found a possible solution:
Change the table structure - Rename the SensitiveColumn to a different name, and add a computed column with the old name of the SensitiveColumn, that will show results based on current_user.
CREATE TABLE tblTest
(
boringColumn int,
SensitiveBase varchar(10), -- no user should have direct access to this column!
SensitiveColumn as
case current_user
when 'Trusted login' then SensitiveBase
else 'N/A'
end
)
The one thing I'm not sure about is if you can deny access to the SensitiveBase column but grant it to the SensitiveColumn.
I'll leave you to test it yourself.
If that can't be done, you can simply grant select permissions on the SensitiveBase column only to trusted login and deny them for everyone else.

SQL Server 2016 - How to get last logged in date for user?

How can I get the last logged in date for certain user.
I googled and stumbled upon this query
SELECT name, accdate FROM sys.syslogins
But the accdate column seems to be deprecated, and does not update.
I have another clue
SELECT login_name, max(login_time) as last_logged_in
FROM sys.dm_exec_sessions GROUP BY login_name
But it shows me results only for system users, not the ones which I created with this query
CREATE USER test1 FOR LOGIN test1 WITH DEFAULT_SCHEMA = 'test1'
The question is, how to make custom created users appear in sys.dm_exec_sessions, or what's the alternative to that solution?
Use sys.dm_exec_sessions system view That
shows information about all active user connections and internal
tasks. This information includes client version, client program name,
client login time, login user, current session setting, and more.
Here’s a little script hopes help you out!
SELECT login_name [Login] , MAX(login_time) AS [Last Login Time]
FROM sys.dm_exec_sessions
GROUP BY login_name;
UPDATE
And About New logins, You Must Login with them firstly for getting a record into sys.dm_exec_sessions
so use the next code for creating a login:-
CREATE LOGIN NewAdminName WITH PASSWORD = 'ABCDa#12'
GO
CREATE USER [NewAdminName] FOR LOGIN [NewAdminName]
EXEC sp_addrolemember N'db_owner', N'NewAdminName'
Now Login by:-
User Name = NewAdminName
Password: ABCDa#12
After Logging successfully, the information of this login is stored into sys.dm_exec_sessions
select DISTINCT login_time, host_name, program_name, login_name from
(SELECT sys.dm_exec_sessions.*,
RANK() OVER(PARTITION BY login_name ORDER BY login_time DESC) as rnk
FROM sys.dm_exec_sessions) l
where l.rnk = 1
ORDER BY l.login_time DESC
By default the last login date isn't recorded by SQL Server :'(
There's a variety of ways you can record it yourself, e.g.
Extended Events session to record logins (e.g. see this article), then you query that to get what you want. There's a "Connection Tracking" template that probably records the info you need.
Triggers (ugh, not recommended)
SQL Server Audit feature (which like Extended Events is super flexible but hard to get your head around when all you want is the last login date).
A SQL Agent Job that periodically looks at the current logins by querying sys.dm_exec_sessions system view. I wouldn't recommend this since it's highly possible you'll miss some logins, plus it puts a higher load than using the extended events infrastructure that SQL provides.

Dynamic Row Level Security In a SQL Server Database Using Extended Properties

We have a requirement to provide customer access to a staging database so that they can extract their data into their own servers, but every table contains all customers data. All of the tables have a 'CustomerID' column. Customers should only see rows where the customerID is the same as theirs.
I am not looking for suggestions to create separate databases or views for each customer as both suggestions are high maintenance and low efficiency.
My solution has to work with:
100GB database
400 Tables
Updates every 30 minutes from the core transaction database
Quarterly schema changes (Application is in continuous Development).
Can anyone give me a definitive answer as to why the following method is not secure or will not work?:
I've set up a database user for each customer, with their customerID as an extended property.
I've created a view of every table that dynamically selects * from the table where the customerID column is the same as the extended property CustomerID of the logged in user. The code looks like this and appears to work well:
CREATE VIEW [CustomerAccessDatabase].[vw_Sales]
AS SELECT * FROM [CustomerAccessDatabase].[Sales]
WHERE [Sales].[CustomerID]=
(SELECT CONVERT(INT,p.value) AS [Value]
FROM sys.extended_properties
JOIN sys.sysusers ON extended_properties.major_id=sysusers.[uid]
AND extended_properties.name = 'CustomerID'
AND sysusers.[SID]=(SELECT suser_sid())
);
GO
To provide access to the views I've created a generic database role 'Customer_Access_Role'. This role has access granted to all of the table views, but access to the database tables themselves is denied.
To prevent users from changing their own customerID I've denied access to the extended properties like so:
USE [master];
GO
DENY EXEC ON sys.sp_addextendedproperty to [public];
GO
DENY EXEC ON sys.sp_dropextendedproperty to [public];
GO
DENY EXEC ON sys.sp_updateextendedproperty to [public];
GO
The end result is that I only need one database, and one set of permissions.
To add a new customer all I would need to do is create a new user with their customerID as an extended attribute and add them to the Customer_Access_Role. Thats it!
I am going to reiterate what everyone is stating already and sum it up.
You are making your job harder than it has to be.
Create a View, that is just their data and then give them Security access to that View.
Alternatively, extract all their data out of the "Core" database and into their own and give them the necessary access to that data.

How to tell if SQL Server user can view every table in database

I have an application that lets users browse data in a SQL database, and I'd like to warn the user if they don't have rights to see every table. I'm getting a list of tables by doing a SELECT on sys.objects, but if the user doesn't have SELECT rights on a table, it doesn't show up in that query, so there's no way I can see to know what I don't know.
I don't even need to know what in particular I'm missing, just if there's anything the user can't view. Anybody know how to accomplish this?
CLARIFICATION:
I'm creating a tool that can be run against any SQL Server, so I'm not looking to set up permissions to see every object - I just want to know if the current user can. If they don't have rights, that's fine - I just want to make sure they know that, since the results they get won't reflect the entire database.
I'm not exactly sure if this is in line with any security concerns of yours, but my idea would be to create a user called SELECT_ALL that has select rights to all tables, create a connection with SELECT_ALL and query the sys.objects table, pass the list of tables (or just a count(*) if you want to simply know if the user has complete access or anything less than complete), close the connection, then repeat with the user's credentials and compare.
You need to mask the sys.objects query with a user with different permissions that can see the lot. The best way is EXECUTE AS OWNER in a view or udf. This enumerates all objects (if dbo owns everything) thus allowing you to find out what is missing (which is masked by metadata visibility)
CREATE VIEW dbo.mymask
WITH EXECUTE AS OWNER --the magic
AS
SELECT * FROM sys.objects
GO
SELECT
*
FROM
dbo.mymask v
LEFT JOIN
sys.objects o2 ON v.object_id = o.object_id
If you use roles, or want to do it in one then something like
CREATE VIEW dbo.mymask
WITH EXECUTE AS OWNER --the magic
AS
SELECT ...
FROM
sys.objects O
JOIN
sys.database_permissions DP ON ... --premissions
JOIN
sys.database_principals R ON DP. = R --role
etc to role members etc
WHERE
xxx.name = ORIGINAL_LOGIN()
GO

Triggers in sql server 2008 management studio

I am trying to set up the trigger in a way that when the administrator (not users) make any changes to the database, all the changed data with the administrator name and time gets saved in the audit table (already created) that has all the possible fields.
I have created triggers on each table for any sort of updates in those tables. The trigger saves the information in audittable. However, i want to restrict this action for administrators only. Like I only want to keep the record of changes made by adminsitrators with their name, time and changes made by them(I have a separate table for adminsitrator names, username, pw and all that).
Can someone please help me with that.
Thanks
To get the user you may use:
server level (login)
select system_user , suser_sname() , suser_sid()
db level (db user)
select session_user , current_user , user , user_name() , user_id()
Than and check that this user is admin or not in that additional table.
You can try one of these two functions, depending on what you define as "administrator".
SELECT IS_MEMBER('dbo'), IS_SRVROLEMEMBER('sysadmin')
The IS_MEMBER function evaluates the database role and the IS_SRVROLEMEMBER evaluates the server role. So, if you want to know if the user is a database admin, you would use IS_MEMBER. These will work for user-defined roles as well as built-in roles.
UPDATE:
Here's an example of the trigger that would add data to the audit table when a server administrator inserts data to the table.
CREATE TRIGGER trg_InfoUpdates ON tblCustomerInfo
FOR INSERT AS
IF IS_SRVROLEMEMBER('sysadmin') = 1
BEGIN
INSERT INTO tblAuditLog (CustomerID)
SELECT CustomerID
FROM inserted
END
;

Resources