Для эффективного поддержания безопасности системы мною был разработан сценарий SetLocalPass.js, с помощью которого можно контролировать регулярность смены паролей для локальных учетных записей, в частности тех, которые являются членами группы "Администраторы".
Синтаксис сценария SetLocalPass.js
При помощи сценария SetLocalPass.js можно изменять пароль определенной учетной записи на одном или нескольких компьютерах на заданный пароль либо на пароль, сгенерированный случайным образом. В качестве целевого объекта можно указать либо отдельный компьютер, либо текстовый файл, содержащий список имен компьютеров. Синтаксис командной строки запуска сценария выглядит следующим образом:
[cscript] SetLocalPass.js
[@]
/pwd: |
/random: [/hidepwd]
Сценарий SetLocalPass.js должен запускаться через сервер CScript, поэтому ключевое слово cscript в начале команды запуска необходимо указывать только в тех случаях, когда cscript не является сервером сценариев, используемым в системе по умолчанию.
Первый параметр представляет собой имя компьютера или, если параметру предшествует символ @, текстовый файл, содержащий список имен компьютеров (по одному имени в строке).
Параметром также является учетная запись пользователя (например, Administrator). Сценарий требует указания имени учетной записи, поскольку в ряде случаев администраторы, руководствуясь требованиями безопасности, переименовывают учетную запись Administrator. Кроме того, использование имени учетной записи делает возможным применение данного сценария не только для англоязычных версий операционной системы.
С помощью ключа /pwd задается новый пароль для выбранной учетной записи. Если в пароле используются специальные командные символы, такие как (, ), <, >, ^, &, |, заключайте его в двойные кавычки. Следует отметить, что в силу ограничений обработчика командной строки Windows Script Host (WSH), сам пароль не может содержать символов двойных кавычек.
Ключ /random: предписывает сценарию использовать для формирования пароля генератор случайных чисел. Все, что нужно в данном случае - это указать требуемую длину пароля (например, /random:14). В результате будет сгенерирован пароль в виде случайной последовательности букв и цифр; если же требуется задать более сложный пароль, то можно соответствующим образом отредактировать значение переменной в начале сценария для возможности использования дополнительных символов. Как это сделать, будет описано ниже.
В выходных данных, выводимых сценарием SetLocalPass.js по умолчанию, отображается и новый пароль. Если вывод пароля на экран нежелателен, то при запуске сценария в командной строке можно воспользоваться ключом /hidepwd. На Рисунке 1 показан пример выходных данных сценария, когда ключ /hidepwd не используется.
Поскольку SetLocalPass.js выводит всю информацию в окно командной строки, то запускать его нужно через сервер CScript. Для дальнейшего просмотра результатов можно организовать перенаправление выходных данных в текстовый файл. В частности, данный сценарий можно запускать и просматривать его результаты по расписанию, для чего соответствующее задание помещается в планировщик задач, таким образом, можно будет контролировать успешность процесса изменения паролей. Например, командная строка запуска задачи, выполняемой по расписанию, может выглядеть следующим образом:
cmd /c cscript
C:ScriptsSetLocalPass.js
@C:ScriptsComputers.txt
Administrator /random:14
> C:ScriptsSetLocalPass.log
В результате выполнения данной команды будет предпринята попытка установки пароля локальной учетной записи "Администратор" для каждого из компьютеров, имена которых содержатся в файле C:ScriptsComputers.txt. Новый пароль длиной 14 символов будет сгенерирован случайным образом. Выходные данные сценария будут сохранены в файле C:Scripts SetLocalPass.log. Если к вашей инфраструктуре предъявляются повышенные требования безопасности, то для предотвращения отображения нового пароля в выходных данных можно использовать ключ /hidepwd. Обратите внимание на то, что строка запуска начинается с команды cmd /c, поскольку для организации перенаправления с помощью символа > требуется явный вызов cmd.exe.
Разумеется, запуск сценария SetLocalPass.js должен осуществляться от имени учетной записи пользователя, являющейся членом локальной группы "Администраторы" на каждом из удаленных компьютеров, к которым производится подключение.
Внутренняя структура SetLocalPass.js
В начале сценария объявляются две глобальные переменные: SCRIPT_NAME, которая используется функцией usage и CHARSET, содержащая перечень символов, которые будет использовать функция randompwd при генерировании случайного пароля. Как работает функция randompwd, будет описано ниже. Далее вызывается функция main.
Функция main. В функции main, приведенной в Листинге 1, сначала объявляются ее локальные переменные, после чего выполняется обработка ключей командной строки запуска сценария. Для упрощения доступа к объекту WScript. Arguments используется переменная arg. Если обнаруживается, что в командной строке присутствует меньше двух неименованных аргументов (перед которыми отсутствует символ "/"), если нет именованных аргументов (начинающихся с символа "/") либо, если в строке указан аргумент "/?", то в этих случаях функция main вызывает функцию usage (обозначенную на Листинге 1 меткой вызова A), которая выводит на экран краткое сообщение об использовании сценария и завершает его выполнение.
Далее функция main вызывает функцию scripthost, определяющую имя сервера, через который выполняется данный сценарий. Если имя сервера сценариев не cscript.exe, то функция main выводит сообщение об ошибке и прекращает выполнение сценария с ненулевым кодом завершения.
Затем производится анализ первого символа первого именованного аргумента командной строки. Для этого в функции main используется метод charAt. Если обнаруживается, что первым символом данного аргумента является "@", функция проверяет, не является ли этот символ единственным в имени аргумента (т.е. не состоит ли имя аргумента из одного символа @). Если это так, то функция завершает выполнение сценария с ненулевым кодом завершения. В противном случае из main вызывается функция filetoarray, которая считывает содержимое файла и заносит его в переменную computers, что иллюстрирует фрагмент кода, обозначенный меткой B. Краткое описание работы функции filetoarray будет приведено ниже.
Если имя первого неименованного аргумента командной строки начинается не с символа @, то функция main использует этот аргумент для создания массива, состоящего из единственного элемента, как показано во фрагменте, обозначенном меткой C. Таким образом, функция main просто перебирает массив имен компьютеров, при этом не имеет значения, из скольких элементов состоит массив - из одного или из нескольких.
Далее функция main проверяет, есть ли хотя бы один элемент в массиве computers. Если свойство length массива имеет нулевое значение (т.е. массив не содержит ни одного элемента), функция выводит сообщение об ошибке и завершает работу сценария с ненулевым кодом возврата.
Затем функция считывает из командной строки имя учетной записи. Это имя является вторым неименованным аргументом командной строки. В этот момент в переменной computers находится массив имен компьютеров, а в переменной account - имя учетной записи.
Следующая задача функции main состоит в том, чтобы определить, есть ли в командной строке ключ /pwd (см. фрагмент с меткой D). Если ключ /pwd присутствует, но не содержит никакого аргумента, функция выводит сообщение об ошибке и завершает работу сценария с ненулевым кодом возврата.
Если ключ /pwd отсутствует, выполняется проверка, нет ли вместо него ключа /random. В функции main для преобразования аргумента ключа /random в целое число используется функция parseInt. Если результат этого преобразования оказывается меньше единицы, или не является числом (что определяется функцией isNaN), функция main выводит сообщение об ошибке и завершает работу сценария с ненулевым кодом возврата. Если аргумент ключа /random имеет корректное значение, функция main вызывает функцию randompwd, с помощью которой генерируется случайный пароль, количество символов которого определяется аргументом ключа /random.
В этот момент для дальнейшей работы функции main необходимо, чтобы переменная pwd имела какое-либо значение. Если эта переменная все еще не содержит никакого значения (т.е. содержит null), следовательно, в командной строке отсутствует аргумент ключа /pwd или ключа /random, и в этом случае функция main выводит сообщение об ошибке и завершает работу сценария с ненулевым кодом возврата.
Далее функция main отображает имя учетной записи пользователя, после которого следует возврат каретки. Затем проверяется, есть ли в командной строке ключ /hidepwd, и если он отсутствует, функция main отображает новый пароль на экране, после чего выполняется возврат каретки.
Последняя задача функции main заключается в том, чтобы последовательно перебрать все элементы массива имен компьютеров и для каждого из них попытаться установить пароль для заданной учетной записи. Как показано во фрагменте, обозначенном меткой E, внутри цикла for вызывается функция GetObject, с помощью которой для компьютера с заданным именем формируется ссылка на учетную запись пользователя. Для задания пароля функция GetObject обращается к провайдеру ADSI WinNT и вызывает метод SetPassword. Блок try... catch обеспечивает перехват и корректную обработку ошибок периода выполнения, чтобы при их появлении сценарий не завершал работу. Если метод SetPassword отработал успешно, функция main выводит соответствующее сообщение, в случае неудачного завершения работы метода SetPassword выводится сообщение об ошибке, содержащее ее номер в шестнадцатеричном представлении.
Для расшифровки номера ошибки используются последние четыре шестнадцатеричные цифры сообщения об ошибке, которые затем можно преобразовать к десятичному виду и передать результат команде Net Helpmsg. Например, ошибке с номером 0x80070035 будет соответствовать шестнадцатеричное число 35, которое в десятичном виде будет выглядеть как 53. Если теперь запустить команду:
Net Helpmsg 53
то в результате получим описание данной ошибки: The network path was not found («Не найден сетевой путь»).
Функция filetoarray. В сценарии SetLocal-Pass.js функция filetoarray используется для преобразования непустой строки текстового файла в массив. Сначала функция создает объект Scripting.FileSystemObject. Если соответствующий текстовый файл существует, вызывается метод OpenTextFile объекта FileSystemObject. Данный метод возвращает объект TextStream, в котором находится содержимое файла. Далее с помощью метода ReadLine объекта TextStream выполняется построчное считывание содержимого текстового файла. Если строка не пустая, функция заносит ее в массив в качестве очередного элемента.
Функция randompwd. Функция randompwd производит случайный выбор символа из переменной CHARSET, которая была определена в самом начале сценария SetLocalPass.js. Для этого используется метод random объекта Math. Метод random возвращает псевдослучайное число, которое лежит в интервале между 0 и 1. Далее это число умножается на количество символов в переменной CHARSET и округляется до целого значения с помощью метода floor объекта Math. В результате получается целое число, которое может принимать значения от 0 до количества символов в CHARSET, уменьшенного на 1. Данный процесс повторяется внутри цикла for для получения необходимого количества случайных символов, после чего возвращается результат. Чтобы увеличить сложность пароля, генерируемого функцией randompwd, нужно включить желаемые символы в переменную CHARSET, которая определяется в начале сценария.
Управление локальными паролями
Независимо от размера сети, локальные учетные записи не должны создавать потенциальную угрозу ее безопасности. Сценарий SetLocalPass.js предоставляет простое решение, которое может гарантировать, что пароли критичных учетных записей будут изменяться на регулярной основе.
Рисунок 1. Пример вывода SetLocalPass
User account: Administrator
New password: UifSA5xTp06mOQ
COMPUTER1: success
COMPUTER2: error 0x80070035
COMPUTER3: success
Листинг 1. Функция Main сценария SetLocalPass.js
function main()
{
var args, computers, account, pwd, n;
args = WScript.Arguments;
// НАЧАЛО ФРАГМЕНТА A
// НАЧАЛО КОММЕНТАРИЯ
// Если задано менее двух неименованных аргументов,
// не задано ни одного именованного аргумента
// или выбран ключ /?,
// выводится справка о корректном запуске сценария.
// КОНЕЦ КОММЕНТАРИЯ
if ((args.Unnamed.length < 2) || (args.Named.length == 0)
|| (args.Named.Exists("?")))
usage();
// КОНЕЦ ФРАГМЕНТА A
// НАЧАЛО КОММЕНТАРИЯ
// Если сценарий запущен не через cscript.exe, завершить работу.
// КОНЕЦ КОММЕНТАРИЯ
if (scripthost() != "cscript.exe") {
WScript.Echo("You must run this script using the CScript host.");
return 1;
}
// НАЧАЛО КОММЕНТАРИЯ
// Если первый неименованный аргумент начинается с символа @,
// предполагается, что после него следует имя файла,
// содержимое которого будет считано в массив.
// В противном случае создается массив из одного элемента,
// который содержит имя компьютера.
// КОНЕЦ КОММЕНТАРИЯ
if (args.Unnamed(0).charAt(0) == "@") {
// НАЧАЛО КОММЕНТАРИЯ
// Если после символа @ других символов нет, сценарий завершается
// с выводом сообщения об ошибке.
// КОНЕЦ КОММЕНТАРИЯ
if (args.Unnamed(0).length == 1) {
WScript.Echo("No file specified");
return 1;
}
// НАЧАЛО ФРАГМЕНТА B
computers = filetoarray(args.Unnamed(0).substr(1));
// КОНЕЦ ФРАГМЕНТА B
}
else
// НАЧАЛО ФРАГМЕНТА C
computers = new Array(args.Unnamed(0));
// КОНЕЦ ФРАГМЕНТА C
// НАЧАЛО КОММЕНТАРИЯ
// Если в массиве имен компьютеров нет ни одного элемента,
// сценарий завершается с выводом сообщения об ошибке.
// КОНЕЦ КОММЕНТАРИЯ
if (computers.length == 0) {
WScript.Echo("The computer list file was not found, there was "
+ "an error opening it, or it
contains only blank lines.");
return 1;
}
// НАЧАЛО КОММЕНТАРИЯ
// Имя учетной записи пользователя
// является вторым неименованным аргументом.
// КОНЕЦ КОММЕНТАРИЯ
account = args.Unnamed(1);
// НАЧАЛО ФРАГМЕНТА D
// НАЧАЛО КОММЕНТАРИЯ
// Считывание из командной строки ключа /pwd или /random.
// КОНЕЦ КОММЕНТАРИЯ
if (args.Named.Exists("pwd")) {
pwd = args.Named("pwd");
if (pwd == null) {
WScript.Echo("/pwd option requires a password");
return 1;
}
}
// КОНЕЦ ФРАГМЕНТА D
else if (args.Named.Exists("random")) {
// НАЧАЛО КОММЕНТАРИЯ
// Аргумент должен быть положительным числом, больше нуля.
// КОНЕЦ КОММЕНТАРИЯ
n = parseInt(args.Named("random"));
if (isNaN(n) || (n < 1)) {
WScript.Echo("/random option requires a number greater than 0");
return 1;
}
pwd = randompwd(n);
}
// НАЧАЛО КОММЕНТАРИЯ
// Если переменная pwd не задана, сценарий завершится с ошибкой.
// КОНЕЦ КОММЕНТАРИЯ
if (pwd == null) {
WScript.Echo("You must specify either /pwd or /random");
return 1;
}
// НАЧАЛО КОММЕНТАРИЯ
// Вывод на экран имени пользователя.
// КОНЕЦ КОММЕНТАРИЯ
WScript.Echo("User account: " + account + "
");
// НАЧАЛО КОММЕНТАРИЯ
// Если указан ключ /hidepwd, пароль на экран не выводится.
// КОНЕЦ КОММЕНТАРИЯ
if (! args.Named.Exists("hidepwd"))
WScript.Echo("New password: " + pwd + "
");
// НАЧАЛО ФРАГМЕНТА E
// НАЧАЛО КОММЕНТАРИЯ
// Обработка массива имен компьютеров.
// КОНЕЦ КОММЕНТАРИЯ
for (n = 0; n < computers.length; n++) {
try {
// НАЧАЛО КОММЕНТАРИЯ
// Вызов метода SetPassword провайдера ADSI WinNT.
// КОНЕЦ КОММЕНТАРИЯ
GetObject("WinNT://" + computers[n]
+ "/" + account + ",User").SetPassword(pwd);
WScript.Echo(computers[n] + ": success");
}
catch(err) {
WScript.Echo(computers[n] + ": error 0x" + hex(err.number));
}
}
// КОНЕЦ ФРАГМЕНТА E
return 0;
}