В журнале событий «Безопасность» на контроллере домена Windows 2003 я вижу несколько записей о событии 540 - «Успешный вход в сеть» с интервалом в несколько минут в течение дня.
Обязательно ли первый из них для конкретного пользователя в определенный день, когда пользователь вошел в систему на своей машине?
Если нет (или даже если так), есть ли другой (лучший?) Способ узнать, в какое время пользователь вошел в систему утром?
Если вы знаете, на какой машине сейчас находится ваш пользователь, попробуйте пслогедон из пакета Sysinternals.
Если вы ищете интерактивный вход в систему на данном клиентском компьютере, вам нужно искать длительное событие на этом клиентском компьютере. Пользователь, который входит в систему, например, с кэшированными учетными данными, не будет создавать записи в журнале событий контроллера домена. Даже если вы не ищете входа в систему с кэшированными учетными данными, настройка клиентских компьютеров на аудит событий входа в систему и просмотр журнала событий клиентского компьютера даст вам наиболее точную и удобную информацию.
В объекте пользователя AD есть атрибут, который называется Last-Logon-Timestamp. Он обновляется каждый раз, когда пользователь входит в систему, но реплицируется не чаще, чем каждые 14 дней, так как предназначен для поиска мертвых учетных записей. Его можно использовать как более точный счетчик, если вы хотите опросить каждый DC в домене для получения этой информации. Исходя из этого, вы можете отслеживать, когда пользователи аутентифицировались в домене всякий раз, когда это происходит, а не только утром.
Вы могли бы в сценарии входа в систему создать строку, которая записывает метку времени где-нибудь в файл?
Что-то вроде?
чистое время >> \ server \ logonlogs \% username% .txt
Ответ на первый вопрос из вашего заголовка «Как узнать, когда пользователь домена вошел в систему», зависит от того, на какой платформе он входит. Для Windows 2000 / XP / 2003 идентификатор события 528 с типом входа 2 покажет вам интерактивный вход в систему с локальной или доменной учетной записи. LogParser - отличный инструмент для анализа журналов событий с большого количества машин и поддерживает большое количество выходных данных. Так, например, вы можете использовать следующее для запроса журнала безопасности на удаленном компьютере и вывода в файл с разделителями табуляции:
c:>logparser.exe "select TimeGenerated, SID from \\wksname\Security where EventID = 528" -i EVT -resolveSIDs:ON -q:ON -headers:off -o:TSV >> c:\UserLogons.txt
Запрос событий из журналов безопасности в Windows Vista / 2008/7 немного отличается тем, что изменился формат файла журнала, а также идентификаторы событий. Идентификатор события 4624 с типом входа 2 покажет успешный интерактивный вход. Мы можем использовать wevtutil для запроса похожих данных и вывода их в формате XML:
c:>wevtutil qe Security /q:"*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and Task=12544 and (EventID=4624)] and EventData[Data[@Name='LogonType']='2']]" /e:Events > c:\UserLogons.xml
Что касается появления идентификатора события 540 в журналах событий безопасности на контроллере домена:
Событие 540 регистрируется по нескольким причинам. Так, например, вы можете увидеть событие с идентификатором 540 с типом входа 3, когда к общему ресурсу обращается служба сервера. Вот типы входа в систему для этого идентификатора события, предоставленные Microsoft:
2 Интерактивный Пользователь вошел в систему на этом компьютере с консоли.
3 Сеть Пользователь или компьютер вошел на этот компьютер из сети.
4 партии Пакетный тип входа в систему используется серверами пакетной обработки, где процессы могут выполняться от имени пользователя без прямого вмешательства пользователя.
5 Сервис Служба была запущена диспетчером управления службами.
7 Разблокировать Эта рабочая станция была разблокирована.
8 Сеть Очистить текст Пользователь вошел в сеть. Пароль пользователя был передан в пакет аутентификации в нехешированной форме. Встроенная проверка подлинности упаковывает все учетные данные хэша перед их отправкой по сети. Учетные данные не передаются по сети в виде открытого текста (также называемого открытым текстом).
9 NewCredentials Вызывающий клонировал свой текущий токен и указал новые учетные данные для исходящих подключений. Новый сеанс входа в систему имеет тот же локальный идентификатор, но использует другие учетные данные для других сетевых подключений.
10 RemoteInteractive Пользователь вошел в систему на этом компьютере удаленно с помощью служб терминалов или подключения к удаленному рабочему столу.
11 CachedInteractive Пользователь вошел в систему на этом компьютере с сетевыми учетными данными, которые хранились локально на компьютере. Не удалось связаться с контроллером домена для проверки учетных данных.
Хорошей охоты.
Извиняюсь за длинный пост, но это то, что я использую. Возможно, вы могли бы немного упростить это:
' **********************************************************************
' AuditUsers
' ==========
'
' UserAccountControl
' SCRIPT 0x0001 1
' ACCOUNTDISABLE 0x0002 2
' HOMEDIR_REQUIRED 0x0008 8
' LOCKOUT 0x0010 16
' PASSWD_NOTREQD 0x0020 32
' PASSWD_CANT_CHANGE 0x0040 64
' ENCRYPTED_TEXT_PWD_ALLOWED 0x0080 128
' TEMP_DUPLICATE_ACCOUNT 0x0100 256
' NORMAL_ACCOUNT 0x0200 512
' INTERDOMAIN_TRUST_ACCOUNT 0x0800 2048
' WORKSTATION_TRUST_ACCOUNT 0x1000 4096
' SERVER_TRUST_ACCOUNT 0x2000 8192
' DONT_EXPIRE_PASSWORD 0x10000 65536
' MNS_LOGON_ACCOUNT 0x20000 131072
' SMARTCARD_REQUIRED 0x40000 262144
' TRUSTED_FOR_DELEGATION 0x80000 524288
' NOT_DELEGATED 0x100000 1048576
' USE_DES_KEY_ONLY 0x200000 2097152
' DONT_REQ_PREAUTH 0x400000 4194304
' PASSWORD_EXPIRED 0x800000 8388608
' TRUSTED_TO_AUTH_FOR_DELEGATION 0x1000000 16777216
'
' objUser.get("userAccountControl")
' **********************************************************************
option explicit
' *** Global constants
const HKEY_CLASSES_ROOT = &H80000000
const HKEY_CURRENT_USER = &H80000001
const HKEY_LOCAL_MACHINE = &H80000002
const HKEY_USERS = &H80000003
const HKEY_CURRENT_CONFIG = &H80000005
const REG_SZ = 1
const REG_EXPAND_SZ = 2
const REG_BINARY = 3
const REG_DWORD = 4
const REG_MULTI_SZ = 7
' *** User account status flags
const SCRIPT = &H0001
const ACCOUNTDISABLE = &H0002
const HOMEDIR_REQUIRED = &H0008
const LOCKOUT = &H0010
const PASSWD_NOTREQD = &H0020
const PASSWD_CANT_CHANGE = &H0040
const ENCRYPTED_TEXT_PWD_ALLOWED = &H0080
const TEMP_DUPLICATE_ACCOUNT = &H0100
const NORMAL_ACCOUNT = &H0200
const INTERDOMAIN_TRUST_ACCOUNT = &H0800
const WORKSTATION_TRUST_ACCOUNT = &H1000
const SERVER_TRUST_ACCOUNT = &H2000
const DONT_EXPIRE_PASSWORD = &H10000
const MNS_LOGON_ACCOUNT = &H20000
const SMARTCARD_REQUIRED = &H40000
const TRUSTED_FOR_DELEGATION = &H80000
const NOT_DELEGATED = &H100000
const USE_DES_KEY_ONLY = &H200000
const DONT_REQ_PREAUTH = &H400000
const PASSWORD_EXPIRED = &H800000
const TRUSTED_TO_AUTH_FOR_DELEGATION = &H1000000
dim wsh_shell, wsh_env, domain_name, server_name
dim initial_ou, computer, last_logon, i
dim users(4, 1000) ' 0 = username, 1 = display_name, 2 = is_disabled, 3 = lastlogon_date, 4 = group membership
dim num_users
const MAX_USERS = 1000
wscript.echo "Audit users started at " & formatdatetime(now(), 0)
' *** Get the domain name
set wsh_shell = Wscript.CreateObject("Wscript.Shell")
set wsh_env = wsh_shell.Environment("PROCESS")
domain_name = wsh_env("USERDNSDOMAIN")
server_name = wsh_env("COMPUTERNAME")
set wsh_env = nothing
set wsh_shell = nothing
' *** Open the Computers container
domain_name = split(domain_name, ".")
initial_ou = "LDAP://DC=" & domain_name(0)
for i = 1 to ubound(domain_name)
initial_ou = initial_ou & ",DC=" & domain_name(i)
next
wscript.echo "Checking domain " & initial_ou
' *** Find all users
set initial_ou = GetObject(initial_ou)
num_users = 0
FindAllUsers initial_ou
' *** Post the data
for i = 0 to num_users-1
wscript.echo users(0, i) & "," & users(1, i) & "," & users(2, i) & "," & users(3, i) & "," & users(4, i)
next
' *** All done
wscript.echo "Audit users finished at " & formatdatetime(now(), 0)
set initial_ou = nothing
wscript.quit 0
' **********************************************************************
' FindAllUsers
' ------------
' **********************************************************************
sub FindAllUsers(fau_OU)
dim ou_name, user, user_dn, display_name, lastlogon_date
dim ldap_user, group_array, i
ou_name = fau_OU.distinguishedName
' *** First list users in this OU
for each user in fau_OU
if lcase(user.class) = "user" then
user_dn = "LDAP://CN=" & user.displayName & "," & ou_name
' *** Check we haven't found too many users
if num_users >= MAX_USERS then
wscript.echo "WARNING: exceeded maximum number of users - " & cstr(MAX_USERS)
exit for
end if
' *** New user
users(0, num_users) = lcase(user.samAccountName)
' *** Get the display name; error trap this because it can fail
users(1, num_users) = ""
on error resume next
err = 0
display_name = user.get("displayName")
if err = 0 then users(1, num_users) = display_name
on error goto 0
' *** Get the enabled/disabled status
users(2, num_users) = user.get("UserAccountControl") and ACCOUNTDISABLE
if users(2, num_users) = 0 then
users(2, num_users) = "0"
else
users(2, num_users) = "1"
end if
' *** Get the last logon date; this may fail so trap errors
lastlogon_date = 0
on error resume next
set lastlogon_date = user.get("lastLogon")
if err = 0 then
if not isempty(lastlogon_date) then
lastlogon_date = LongTimeToDate(lastlogon_date)
if lastlogon_date < 0 then lastlogon_date = 0
end if
end if
on error goto 0
users(3, num_users) = formatdatetime(lastlogon_date, 0)
' *** Get the group membership
users(4, num_users) = ""
on error resume next
err = 0
set ldap_user = GetObject(user_dn)
if err = 0 then
on error goto 0
group_array = ldap_user.MemberOf
if not isempty(group_array) then
if TypeName(group_array) = "String" then
users(4, num_users) = group_array
else
for i = lbound(group_array) to ubound(group_array)
if users(4, num_users) <> "" then users(4, num_users) = users(4, num_users) & ";"
users(4, num_users) = users(4, num_users) & TrimGroupName(group_array(i))
next
end if
end if
set ldap_user = nothing
end if
on error goto 0
' *** Finished with this user
num_users = num_users + 1
end if
next
' *** Now recurse into subcontainers
for each user in fau_OU
if lcase(user.class) = "organizationalunit" or lcase(user.class) = "container" then
FindAllUsers user
end if
next
' *** All done
end sub
' **********************************************************************
' TrimGroupName
' -------------
' Turn the distinguished name into a simply group name
' **********************************************************************
function TrimGroupName(tgn_FullName)
dim group_name, len_group, c
TrimGroupName = ""
group_name = ""
len_group = len(tgn_FullName)
if len_group < 4 then exit function
for i = 4 to len_group
c = mid(tgn_FullName, i, 1)
if c = "," then exit for
group_name = group_name + c
next
group_name = lcase(group_name)
TrimGroupName = group_name
end function
' **********************************************************************
' LongTimeToDate
' --------------
' Convert the ADSI longint timestamp to a VBScript format date
' **********************************************************************
function LongTimeToDate(lt_Time)
dim ltdate
ltdate = lt_Time.HighPart * (2^32) + lt_Time.LowPart
ltdate = ltdate / (60 * 10000000)
ltdate = ltdate / 1440
ltdate = ltdate + #1/1/1601#
LongTimeToDate = ltdate
end function
JR
Не имеет отношения к вашему вопросу, но почему бы не отфильтровать эти события, вам не наплевать, когда у вас есть такой уровень возвратов событий. Ошибки с серьезностью 3 или выше, человек, не теряйте время!
Если вы не хотите копаться в журналах безопасности, вы также можете попробовать инструменты блокировки и управления учетной записью. (ссылка на сайт). Это добавляет дополнительную страницу свойств к сведениям об учетной записи в ADUC, включая «время последнего входа в систему».