Как подписать свой первый скрипт за 48 часов

Проблема


Когда задача, требующая решения, небольшая, совсем не хочется писать для её решения отдельную утилиту, особенно, если ты — .NET-программист.
Скрипт? Однозначно, да, но ставить на боевую машину под управлением Windows сторонний интерпретатор совсем уж не по-христиански. Так почему бы не воспользоваться Windows Powershell? Готов сразу честно признаться: практически никакого опыта с ним не было, но уж больно заманчиво выглядел.
Скрипт, решающий задачу, был готов через 15 минут, если не учесть одно «но». Скриптом пока назвать это было сложно, потому что это был набор инструкций, непригодный для выполнения в виде скрипта. Непригодный, с точки зрения PowerShell.

Всему есть разумное объяснение — конечно же, на выполнение скриптов накладываются определенные ограничения, задаваемые политикой исполнения скриптов. Создав скрипт, я не смог выполнить его тут же на своей машине. Однако проблема, решаемая для локальной машины временным изменением политики исполнения на Unrestricted или, правильнее, RemoteSigned, на рабочем сервере поднимается всерьез.

Выход есть всегда. Логично, что готовый скрипт для исполнения нужно подписать. Процесс организации механизма подписи достаточно протяжён и тернист, но послужил хорошей почвой для этого поста.

После двух суток мучений дома и в офисе, представляю на суд общественности краткий мануал по подписи скриптов для PowerShell.

Решение


По умолчанию, исполнение любых скриптов запрещено. Для начала, необходимо разрешить выполнение только подписанных скриптов от доверенных издателей с доверенным корневым сертификатом. В рамках сеанса администратора Powershell:

> Set-ExecutionPolicy AllSigned

Дальнейшие танцы с бубном касаются утилиты по созданию корневого и персонального сертификатов.
Все сертификаты, согласно «инструкции» по подписыванию скриптов

> Get-Help About_Signing

создаются с использованием утилиты makecert.exe, находящейся в %Program Files%\Microsoft SDKs\Windows\v7.0A\bin\makecert

Следующие два действия выполняются в рамках обычного сеанса командной строки:

> makecert -n "CN=PowerShell Local Certificate Root" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine

> makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer


Первая строка создает корневой сертификат, используя в качестве удостоверяющего центра локальную машину. Наиболее популярный «бесплатный» способ получения сертификата.
Вторая строка создает персональный сертификат пользователя PowerShell, которым будут подписываться скрипты, заверяя его корневым сертификатом. Если всё прошло успешно, обе строки должны показать результат Succeeded.

Приведенные выше манипуляции можно и не выполнять, если персональный сертификат X509 уже есть.

После этого, на всякий случай, можно проверить, что ОС в курсе насчет только что созданного сертификата. PowerShell:

> Get-Childitem cert:\CurrentUser\my -codesigning

Подписывать скрипты уже можно, но, в таком случае, на подпись скрипта каждый раз будет уходить такая строка Powershell:

> Set-AuthenticodeSignature "FileName" @(Get-ChildItem cert:\CurrentUser\My -codesigning)[0]

Было бы логичным создать скрипт, который будет подписывать другие скрипты. Скрипт нужно создавать в любом текстовом редакторе, кроме PowerShell ISE. Для скриптов, созданных внутри этой среды, возникнут проблемы с подписыванием в виде Unknown Error. Решение взято здесь.

Сам скрипт выглядит так:
param([string] $file=$(throw "Please specify a filename."))
$cert = @(Get-ChildItem cert:\CurrentUser\My -codesigning)[0]
Set-AuthenticodeSignature $file $cert

При помощи инструкций, указанных выше, подпишем сам этот скрипт в интерактивном режиме PowerShell.
После этого, скрипт для выполнения на другой машине может быть подписан так:

> .\Add-Signature.ps1 MyScript.ps1

О грустном


На любой машине, кроме той, на которой создавался сертификат:
  1. Если корневой сертификат не является доверенным, выполнить скрипт не удастся вообще.
  2. Если персональный сертификат издателя, подписавшего скрипт, не является доверенным, скрипт выполнится только с подтверждением.

Для того, чтобы добавить корневой и персональный сертификаты в доверенные, нужно воспользоваться админской консолью mmc с оснасткой Certificates. Сертификаты добавлять в папки Trusted Root Certification Authorities и Trusted Publishers.

Не претендует на роль единственно верного решения, но результаты исследований показывают, что пока это более или менее нормальный способ. Прошу знающих помочь прояснить ситуацию и ткнуть ссылкой, по возможности.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 29

    +3
    Не могли бы вы пояснить чем именно плохо изменить политику на Unrestricted. Если я могу выполнить скрипт на какой либо машине я могу выполнить и экзешник, подписи для которого не требуются.
    Где ошибка в моём рассуждении?
      +2
      Ошибок нет. Но чертова паранойя. В общем, чем меньше можно, тем больше нельзя. Ваш К.О.
        0
        У МС хватает перестраховок, ещё известная гадость — запрет на открытие сертификатов в почтовых вложениях.
      0
      Наверное тем, что даже без администраторского доступа запущенный скрипт имеет очень много вариантов нагадить в системе.
      0
      Для выполнения скрипту нужны права администратора?

        0
        Для запуска на выполнение — нет. Но для успешного прохождения некоторых инструкций могут понадобиться.
          +2
          Мне кажется слишком сложное решение получилось. В линуксе на скрипт ушли те же 15 минут с выставлением прав на соответсвующего юзера.

          Непонятно, почему нельзя было сделать небольшую утилиту с правами запуска от администратора.
            0
            Мне непонятно, как вы измерили время написания скрипта, при условии, что я не указывал, что именно делает скрипт
              0
              Ну вы же сами написали:
              > Скрипт, решающий задачу, был готов через 15 минут…
                0
                Да, конечно, мой скрипт был готов через 15 минут. У него существует определенная задача. А какую задачу решает ваш скрипт, который вы приготовили тоже за 15 минут? Не поймите меня не правильно, просто мне непонятно, какой именно скрипт вы сделали.
                  0
                  Вы таки хотите сказать, что ваш скрипт мог быть реализован только средствами powershell?
                    0
                    Нет, я хочу сказать, что не нужно мериться временем реализации решения, если неизвестно, что требовалось сделать.
                      0
                      И что же нужно было сделать, если не секрет?
        +1
        по-моему мс перемудрили с этими сертификатами. Защита от выполнения чужого скрипта таки правами решаться должна, а если кто-то похачил систему и получил админа, его уже не остановить.
          0
          --sign.vbs--
          Set oSigner = WScript.CreateObject("Scripting.Signer")
          oSigner.SignFile "D:\MyScripts\MyScript.vbs", "MyCert"

          --end--
          Вообще, если это standalone машина, то тогда почему бы не сделать политику Unrestricted? Если же машина внутри корпорации, то наверняка в корпорации есть свой доверенный УЦ, там можно получить сертификат по шаблону «Подписывание кода».
            0
            В том и дело, взять сертификат изначально было неоткуда, пришлось генерить свой. Только не могу понять, при чем здесь подписывание VB-скриптов?
            0
            Да просто, чтоб показать, что тема с подписыванием скриптов не нова, она существует уже какое-то время.
            А в вашем скрипте я бы заменил строчку:
            $cert = @(Get-ChildItem cert:\CurrentUser\My -codesigning) | Where-Object {$_.Subject -eq "CN=<субъект>"}
            У вас же может статься так, что в хранилище тысяча сертификатов, подписывающих код. Зачем нужно брать самый первый, не пойму? Не проще ли обратиться к сертификату по имени? Да и в параметры к скрипту вставить можно.
              0
              Да, вы совершенно правы. Первый попавшийся брал, потому что сертификат был на машине единственный, да и задачка достаточно узкая. На новизну темы не претендовал, просто поделился, так сказать, наболевшим. И ваш вариант с подписью, как и следовало ожидать, тоже работает корректно. Интересно только то, что сам текст подписи формируется другой, хоть формат и схож.
                0
                Есть еще одна бага, может пригодится, может нет, а может это и фича вовсе… Не знаю, MS мне на их форумах так внятного ответа и не дали. Короче, если групповой политикой на Logon поставить неподписанный повершелл-скрипт — раз, и два — поставить политику запуска хоть даже AllSigned, то Logon-скрипт все равно запустится, он отрабатывает в режиме Undefined. Почему так — никому не известно.
              0
              RemoteSigned — компросс между AllSigned и Unrestricted. Тогда подписанными должны быть только скрипты, скачанные из интернета.
              Почитать о том, как PowerShell определяет, скачан скрипт или нет, можно в следующей статье: blogs.msdn.com/b/powershell/archive/2007/03/07/how-does-the-remotesigned-execution-policy-work.aspx
                0
                Плюсануть, к сожалению, не могу. Спасибо за ценную инфу.
                0
                Автор кое-что забыл упомянуть.
                Когда я разбирался по данной теме я столкнулся с проблемой в момент подписывания, выдавалось такое:

                SignerCertificate Status Path
                — — — UnknownError CheckMSE.ps1

                Разобравшись в данном вопросе, выяснил, что виновата кодировка скрипта. К сожалению, PowerShell ISE второй версии не позволял менять кодировки, по этому приходилось либо открывать скрипт в Notepad++ и менять кодировку на UTF8, либо с помощью небольшого скриптика:

                Get-Content CheckMSE.ps1 | out-file .\CheckNew.ps1 -encoding UTF8
                Set-AuthenticodeSignature .\CheckNew.ps1 $cert

                И все было ок:

                SignerCertificate Status Path
                — — — 0990C1A9E09DCE1263DD5F7D7688FFB8E20AD25E Valid CheckNew.ps1

                З.Ы. В 3-й версии эту проблему исправили.

                  0
                  Прошу прощения за неудачные вставки из кода
                    0
                    Невнимательно читаете. Не забыл.

                    Скрипт нужно создавать в любом текстовом редакторе, кроме PowerShell ISE. Для скриптов, созданных внутри этой среды, возникнут проблемы с подписыванием в виде Unknown Error. Решение взято здесь.
                      0
                      Да уж, буду внимательнее, извините.
                    0
                    у меня получилось почему-то так:
                    cd «C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\»
                    .\makecert -n «CN=PowerShell Local Certificate Root» -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv d:\root.pvk d:\root.cer -ss Root
                    в Вашем случае создание файла ключа идет где-то на диске С, куда запись не оченоь приветствуется, поэтому лучше писать куда-то, к примеру в $Home.
                      0
                      Да, ошибку с PSH ISE подтверждаю.
                      оказалось просто скопипастить текст скрипта в редактор FAR после чего подпись прошла на ура

                      Only users with full accounts can post comments. Log in, please.