PowerShellでWebサーバーのセキュリティ対策を自動化した話

# PowerShellでWebサーバーのセキュリティ対策を自動化した

旧サーバーで実施したセキュリティ対策を新しく構築するサーバー(2台)でも実施する必要があった.
対策を手で2台もやるのは~~めんどくさい~~大変なのでスクリプトで自動化した.

## 実現したこと

- SSL証明書をインポートし, HTTPS通信を有効にする.
- レジストリを編集し, 脆弱な暗号化方式を無効にする.
- セキュリティ診断対応済みの設定ファイルをIISのルートディレクトリに配備し, 以下の脆弱性を解消する.
  - セキュリティ上推奨されるHTTPヘッダーが付加されるようにする.
  - デフォルトのページを非表示にするため, 規定のドキュメントを無効にする.
  - ディレクトリリスティングを無効にする.
- urlscanをインストールし, 推奨されないHTTPメソッドを無効にする.

## スクリプト

### 構成

```
ResolveVulnerability
┣Functions
┃┣Deploy-WebConfig.ps1
┃┣Disable-Encryption.ps1
┃┣Enable-SSL.ps1
┃┣Install-UrlScan.ps1
┃┣Remove-IISDefaultItems.ps1
┃┗Restart-IIS.ps1
┣Certificate
┃┗certificate.pfx
┣Config
┃┣UrlScan.ini
┃┗web.config
┣Installer
┃┗urlscan_v31_x64.msi
┗Script
  ┣Config.ps1
  ┣Index.ps1
  ┗Resolve-Vulnerability.ps1
```

### 実行方法

管理者権限でPowerShellを起動して下記コマンドを実行するだけ.

```powershell
Set-Location C:\ResolveVulnerability
.\Resolve-Vulnerability.ps1
```

ただ, インポートするSSL証明書は事前に用意してね.

### 中身

Resolve-Vulnerability.ps1でFunctions内の関数を呼ぶ感じ.
Resolve-Vulnerability.ps1ではIndex.ps1を読み込む.

```powershell:Resolve-Vulnerability.ps1
. ("{0}/Index.ps1" -f (Split-Path $MyInvocation.MyCommand.Path -Parent))

# SSL証明書をインポートし, https通信を有効にする
Enable-SSL -CertPath $CertPath -CertPass $CertPass -TargetSite $TargetSite

# 脆弱な暗号化方式を無効化する
# Protocols
Disable-Encryption -Key $SSL2Server 
Disable-Encryption -Key $SSL2Client 
Disable-Encryption -Key $SSL3Server 
Disable-Encryption -Key $SSL3Client 
# Cipher
Disable-Encryption -Key $CiphersNULL 
Disable-Encryption -Key $DES 
Disable-Encryption -Key $RC2_40 
Disable-Encryption -Key $RC2_56 
Disable-Encryption -Key $RC2_128 
Disable-Encryption -Key $RC4_40 
Disable-Encryption -Key $RC4_56 
Disable-Encryption -Key $RC4_128 
Disable-Encryption -Key $RC4_64 
Disable-Encryption -Key $3DES 
# Hashes
Disable-Encryption -Key $MD5 
# KeyExchangeAlgorithms
Disable-Encryption -Key $DiffieHellman 

# レジストリの設定を有効にするためIISを再起動する
Restart-IIS $IISSvcName

<# IISのルートディレクトリにWeb.configを配備することで下記セキュリティ対策を実施する
    ・セキュリティ上推奨されるHTTPヘッダーを付加する
    ・規定のドキュメントを無効にする(デフォルトのページが非表示になる)
    ・ディレクトリリスティングを無効にする #>
Deploy-WebConfig -WebConfigSrc $WebConfigSrc -IISRoot $IISRoot -WebConfigFile $WebConfigFile

# デフォルトのiisページを消す
Remove-IISDefaultItems $IISDefaultItems

# urlscanインストールして設定ファイル置換する
Install-UrlScan -UrlScanInstaller $UrlScanInstaller -UrlScanConfigSrc $UrlScanConfigSrc -UrlScanConfigDst $UrlScanConfigDst -UrlScanConfigFile $UrlScanConfigFile
```



Index.ps1ではConfig.ps1とFunctions内のスクリプトを一括で読み込む.



```powershell:Index.ps1
Set-Variable -Name BVIndexRoot -Value (Split-Path $MyInvocation.MyCommand.Path -Parent) -Option Constant

# 設定ファイル
. ("{0}\Config.ps1" -f $BVIndexRoot)

# 関数
Set-Variable -Name BVFunctionsRoot -Value ("{0}\Functions" -f $BVIndexRoot) -Option Constant
Get-ChildItem -Path $BVFunctionsRoot | ForEach-Object { . ("{0}\{1}" -f $BVFunctionsRoot, $_)}
```



Config.ps1に設定をまとめる.




```powershell:Config.ps1
# スクリプトのパス
Set-Variable -Name RVConfRoot -Value (Split-Path $MyInvocation.MyCommand.Path -Parent) -Option Constant 

#-- 証明書インポート/HTTPS有効 start --#
# 証明書のパス
Set-Variable -Name CertPath -Value ("{0}\Certificate\certificate.pfx" -f $RVConfRoot) -Option Constant 
# 証明書のパスワード
Set-Variable -Name CertPass -Value "1qwertyuiop@" -Option Constant 
# HTTPS通信の対象サイト
Set-Variable -Name TargetSite -Value "Default Web Site" -Option Constant 
#-- 証明書インストール/HTTPS有効 end --#

#-- 脆弱性のある暗号化方式対策 start --#
# Protocols
Set-Variable -Name SSL2Server -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -Option Constant 
Set-Variable -Name SSL2Client -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -Option Constant 
Set-Variable -Name SSL3Server -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -Option Constant 
Set-Variable -Name SSL3Client -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -Option Constant 
# Ciphers
Set-Variable -Name CiphersNULL -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL' -Option Constant 
Set-Variable -Name DES -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\DES 56\56' -Option Constant 
Set-Variable -Name RC2_40 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 40/128' -Option Constant 
Set-Variable -Name RC2_56 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 56/128' -Option Constant 
Set-Variable -Name RC2_128 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 128/128' -Option Constant 
Set-Variable -Name RC4_40 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128' -Option Constant 
Set-Variable -Name RC4_56 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128' -Option Constant 
Set-Variable -Name RC4_128 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128' -Option Constant 
Set-Variable -Name RC4_64 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 64/128' -Option Constant 
Set-Variable -Name 3DES -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\Triple DES 168' -Option Constant 
# Hashes
Set-Variable -Name MD5 -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Hashes\MD5' -Option Constant 
# KeyExchangeAlgorithms
Set-Variable -Name DiffieHellman -Value 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman' -Option Constant 
#-- 脆弱性のある暗号化方式対策 end --#

#-- IISサービス設定 start --#
# IISサービス名
Set-Variable -Name IISSvcName -Value 'W3SVC' -Option Constant 
#-- IISサービス設定 end --#

#-- Web.config配置設定 start --#
# セキュリティ上推奨されるHTTPヘッダーを追加するWeb.configのディレクトリ
Set-Variable -Name WebConfigSrc -Value ('{0}\Config' -f $RVConfRoot) -Option Constant 
# セキュリティ上推奨されるHTTPヘッダーを追加するWeb.config
Set-Variable -Name WebConfigFile -Value 'web.config' -Option Constant 
# IISのルートディレクトリ
Set-Variable -Name IISRoot -Value 'C:\inetpub\wwwroot' -Option Constant 
#-- Web.config配置設定 end --#

#-- 削除するIISデフォルトアイテム start --#
# IISのデフォルトアイテム一覧
$IISDefaultItems = @(('{0}\iis-85.png' -f $IISRoot), ('{0}\iisstart.htm' -f $IISRoot))
Set-Variable IISDefaultItems -Option ReadOnly
#-- 削除するIISデフォルトアイテム end --#

#-- Web.config配置設定 start --#
# urlscanのインストーラー
Set-Variable -Name UrlScanInstaller -Value ('{0}\Installer\urlscan_v31_x64.msi' -f $RVConfRoot) -Option Constant 
# UrlScan設定ファイルのディレクトリ
Set-Variable -Name UrlScanConfigSrc -Value ('{0}\Config' -f $RVConfRoot) -Option Constant 
# UrlScan設定ファイルの配置先
Set-Variable -Name UrlScanConfigDst -Value 'C:\Windows\System32\inetsrv\urlscan' -Option Constant 
# UrlScan設定ファイル
Set-Variable -Name UrlScanConfigFile -Value 'UrlScan.ini' -Option Constant 
#-- Web.config配置設定 end --#
```


あとは関数たち. 



Deploy-WebConfig.ps1ではIISのルートディレクトリにセキュリティ診断対策を施した設定ファイルを置くだけ.



```powershell:Deploy-WebConfig.ps1
function Deploy-WebConfig() {
    <#
    .SYNOPSIS
    Web.configを配備します.
    .DESCRIPTION
    IISのルートディレクトリにセキュリティ上推奨されるHTTPヘッダーを付加するようになります.
    規定のドキュメントを無効にします.
    #>
    Param(
        $WebConfigSrc, 
        $IISRoot, 
        $WebConfigFile
    )
    Robocopy.exe $WebConfigSrc $IISRoot $WebConfigFile
}
```


Disable-Encryption.ps1ではレジストリを弄って脆弱性のある暗号化方式を無効にする.


```powershell:Disable-Encryption.ps1
function Disable-Encryption() {
    <#
    .SYNOPSIS
    指定したキーの暗号化を無効化します.
    #>
    Param(
        $Key
    )

    New-Item $Key -Force
    New-ItemProperty -path $Key -name Enabled -value 0 –PropertyType DWORD
    if($Key.Contains("SSL")){
        # SSLはDisabledByDefaultも設定する
        New-ItemProperty -path $Key -name DisabledByDefault -value 1 –PropertyType DWORD
    }
}
```


Enable-SSL.ps1ではSSL証明書をインポートしてHTTPSの443にバインドする.


```powershell:Enable-SSL.ps1
function Enable-SSL(){
    <#
    .SYNOPSIS
    SSL証明書をインポートし, https通信を有効にします.
    #>
    Param(
        $CertPath,
        $CertPass,
        $TargetSite
    )
      
    # 証明書をインポートする  
    $cert = Import-PfxCertificate -Filepath $CertPath -Password (ConvertTo-SecureString $CertPass -AsPlainText -Force) -CertStoreLocation "Cert:\LocalMachine\My" -Exportable
    # IISに443httpsバインドを作成する
    New-WebBinding -Name $TargetSite -IP "*" -Port 443 -Protocol https
    # httpsバインドを取得し証明書を設定する
    $httpsBinding = Get-WebBinding -Protocol https
    $httpsBinding.AddSslCertificate($cert.GetCertHashString(),"my")
}
```

Install-UrlScan.ps1ではUrlScanをインストールして, 設定ファイルを置き換える.

```powershell:Install-UrlScan.ps1
function Install-UrlScan() {
    <#
    .SYNOPSIS
    urlscanをインストールしセキュリティ対策を施した設定ファイルに置き換えます.
    #>
    Param(
        $UrlScanInstaller,
        $UrlScanConfigSrc, 
        $UrlScanConfigDst, 
        $UrlScanConfigFile
    )

    $process = Start-Process -FilePath "msiexec.exe" -ArgumentList ("/i {0} /passive" -f $UrlScanInstaller) -Verb runas -PassThru
    Wait-Process -Id ($process.Id)
    Rename-Item -Path ("{0}\{1}" -f $UrlScanConfigDst, $UrlScanConfigFile) -NewName ("_{0}" -f $UrlScanConfigFile) -Force
    Robocopy.exe $UrlScanConfigSrc $UrlScanConfigDst $UrlScanConfigFile
}
```

Remove-IISDefaultItems.ps1ではIISをインストールした際にデフォルトで置かれているアイテムを削除する.

```powershell:Remove-IISDefaultItems.ps1
function Remove-IISDefaultItems() {
    <#
    .SYNOPSIS
    IISのデフォルトページや画像を削除します.
    #>
    Param(
        $Items
    )

    foreach($item in $Items) {
        if(Test-Path $item){
            Remove-Item -Path $item -Force
        }
    }
}
```

Restart-IIS.ps1ではIISを再起動する. ~~わざわざ関数にしなくても~~

```powershell:Restart-IIS.ps1
function Restart-IIS() {
    <#
    .SYNOPSIS
    IISを再起動します.
    #>
    Param(
        $SvcName
    )

    Restart-Service $SvcName
}
```


### Web.configの中身

- defaultDocumentで規定のドキュメントを無効にする(本番環境にデフォルトのページはいらない).
- directoryBrowseでディレクトリリスティング(ブラウザにディレクトリ構造見せちゃうヤツ)を無効にする.
- customHeadersで推奨されるHTTPヘッダーを追加する.
  - X-Content-Type-Options: nosniff(これ指定しないとIEがレスポンス解析してContent-Type無視した動作しちゃったり...)
  - X-Frame-Options: DENY(これ指定しないとクリックジャッキングされる恐れ有り)
  - X-XSS-Protection: 1; mode=block(ブラウザのXSSフィルターの機能を有効にし, XSSを検知したらページのレンダリングを停止する)


```xml:Web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.web>
        <httpRuntime enableVersionHeader="false" />
    </system.web>
    <system.webServer>
        <defaultDocument enabled="false" />
		<directoryBrowse enabled="false" />
	    <httpProtocol>
	      <customHeaders>
	        <clear/>
	        <remove name="X-Powered-By"/>
	        <add name="X-XSS-Protection" value="1; mode=block"/>
	        <add name="X-Frame-Options" value="DENY"/>
	        <add name="X-Content-Type-Options" value="nosniff"/>
	      </customHeaders>
	    </httpProtocol>
    </system.webServer>
</configuration>
```

### UrlScan.iniの中身

- OPTIONSを明示的に無効にする(Webサーバーが許可しているメソッドが表示されちゃうからね).

```txt:UrlScan.ini
[AllowVerbs]

;
; The verbs (aka HTTP methods) listed here are those commonly
; processed by a typical IIS server.
;
; Note that these entries are effective if "UseAllowVerbs=1"
; is set in the [Options] section above.
;

GET
HEAD
POST

[DenyVerbs]

;
; The verbs (aka HTTP methods) listed here are used for publishing
; content to an IIS server via WebDAV.
;
; Note that these entries are effective if "UseAllowVerbs=0"
; is set in the [Options] section above.
;

PROPFIND
PROPPATCH
MKCOL
DELETE
COPY
MOVE
LOCK
UNLOCK
SEARCH
PUT
OPTIONS
```