Cómo usar PowerShell para listar las extensiones del navegador en Windows

La gestión de las extensiones del navegador en múltiples usuarios y sistemas es un reto común en la gestión de TI. Las extensiones del navegador, aunque útiles, pueden plantear importantes riesgos de seguridad si no se controlan adecuadamente. Pueden introducir vulnerabilidades, espiar las actividades de los usuarios o convertirse en puntos de entrada de malware.

Este script ofrece una solución sólida para que los profesionales de TI y los proveedores de servicios gestionados (MSP) realicen un inventario de las extensiones del navegador instaladas en navegadores populares como Chrome, Firefox y Edge.

Contexto

A medida que las organizaciones dependen cada vez más de las aplicaciones basadas en web, el papel de las extensiones de navegador ha ido creciendo. Sin embargo, esto también aumenta la necesidad de supervisión para garantizar que las extensiones sean seguras y cumplan las políticas de la organización. Este script responde a estas necesidades proporcionando un informe completo sobre todas las extensiones de navegador instaladas para los principales navegadores, lo que lo convierte en una herramienta esencial para los profesionales de TI y los MSP que necesitan mantener la seguridad y el cumplimiento.

El script para listar las extensiones del navegador en Windows

#Requires -Version 5.1

<#
.SYNOPSIS
    Reports on all installed browser extensions for Chrome, Firefox and Edge.
.DESCRIPTION
    Reports on all installed browser extensions for Chrome, Firefox and Edge.
.EXAMPLE
    (No Parameters)

    A Google Chrome installation was detected. Searching Chrome for browser extensions...
    A Microsoft Edge installation was detected. Searching Microsoft Edge for browser extensions...
    A Firefox installation was detected. Searching Firefox for browser extensions...
    Attempting to set Custom Field 'Multiline'.
    WARNING: 10,000 Character Limit has been reached! Trimming output until the character limit is satisfied...
    Successfully set Custom Field 'Multiline'!
    Attempting to set Custom Field 'WYSIWYG'.
    Successfully set Custom Field 'WYSIWYG'!
    Browser extensions were detected.


    Browser      : Chrome
    User         : cheart
    Name         : askBelynda | Sustainable Shopping
    Extension ID : pcmbjnfbjkeieekkahdfgchcbjfhhgdi
    Description  : Sustainable shopping made simple with askBelynda. Choose ethical products o(...)

    Browser      : Chrome
    User         : cheart
    Name         : Beni - Your secondhand shopping assistant
    Extension ID : efdgbhncnligcbloejoaemnfhjihkccj
    Description  : The easiest way to shop secondhand.  Beni finds the best resale alternative(...)

    Browser      : Chrome
    User         : cheart
    Name         : Bonjourr · Minimalist Startpage
    Extension ID : dlnejlppicbjfcfcedcflplfjajinajd
    Description  : Improve your web browsing experience with Bonjourr, a beautiful, customizab(...)

    Browser      : Chrome
    User         : cheart
    Name         : Boxel 3D
    Extension ID : mjjgmlmpeaikcaajghilhnioimmaibon
    Description  : Boxel 3D is the 3rd release of your favorite box jumping game made by the d(...)

    ...

PARAMETER: -MultilineCustomField "ReplaceMeWithNameOfAMultilineCustomField"
    Specify the name of a multiline custom field to optionally store the search results in. Leave blank to not set a multiline field.

PARAMETER: -WysiwygCustomField "ReplaceMeWithAnyWYSIWYGCustomField"
    Specify the name of a WYSIWYG custom field to optionally store the search results in. Leave blank to not set a WYSIWYG field.

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2016
    Release Notes: Initial Release
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#>

[CmdletBinding()]
param (
    [Parameter()]
    [String]$MultilineCustomField,
    [Parameter()]
    [String]$WysiwygCustomField
)

begin {
    # Replace parameters with the dynamic script variables.
    if ($env:multilineCustomFieldName -and $env:multilineCustomFieldName -notlike "null") { $MultilineCustomField = $env:multilineCustomFieldName }
    if ($env:wysiwygCustomFieldName -and $env:wysiwygCustomFieldName -notlike "null") { $WysiwygCustomField = $env:wysiwygCustomFieldName }

    # Check if $MultilineCustomField and $WysiwygCustomField are both not null and have the same value
    if ($MultilineCustomField -and $WysiwygCustomField -and $MultilineCustomField -eq $WysiwygCustomField) {
        Write-Host "[Error] Custom Fields of different types cannot have the same name."
        Write-Host "https://ninjarmm.zendesk.com/hc/en-us/articles/360060920631-Custom-Fields-Configuration-Device-Role-Fields"
        exit 1
    }

    # Function to get user registry hives based on the type of account
    function Get-UserHives {
        param (
            [Parameter()]
            [ValidateSet('AzureAD', 'DomainAndLocal', 'All')]
            [String]$Type = "All",
            [Parameter()]
            [String[]]$ExcludedUsers,
            [Parameter()]
            [switch]$IncludeDefault
        )
    
        # Patterns for user SID depending on account type
        $Patterns = switch ($Type) {
            "AzureAD" { "S-1-12-1-(\d+-?){4}$" }
            "DomainAndLocal" { "S-1-5-21-(\d+-?){4}$" }
            "All" { "S-1-12-1-(\d+-?){4}$" ; "S-1-5-21-(\d+-?){4}$" } 
        }
    
        # Fetch user profiles whose SIDs match the defined patterns and prepare objects with their details
        $UserProfiles = Foreach ($Pattern in $Patterns) { 
            Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" |
                Where-Object { $_.PSChildName -match $Pattern } | 
                Select-Object @{Name = "SID"; Expression = { $_.PSChildName } },
                @{Name = "UserName"; Expression = { "$($_.ProfileImagePath | Split-Path -Leaf)" } }, 
                @{Name = "UserHive"; Expression = { "$($_.ProfileImagePath)\NTuser.dat" } }, 
                @{Name = "Path"; Expression = { $_.ProfileImagePath } }
        }
    
        # Handle inclusion of the default user profile if requested
        switch ($IncludeDefault) {
            $True {
                $DefaultProfile = "" | Select-Object UserName, SID, UserHive, Path
                $DefaultProfile.UserName = "Default"
                $DefaultProfile.SID = "DefaultProfile"
                $DefaultProfile.Userhive = "$env:SystemDrive\Users\Default\NTUSER.DAT"
                $DefaultProfile.Path = "C:\Users\Default"
    
                $DefaultProfile | Where-Object { $ExcludedUsers -notcontains $_.UserName }
            }
        }

        # Return user profiles, excluding any specified users
        $UserProfiles | Where-Object { $ExcludedUsers -notcontains $_.UserName }
    }

    # Function to check if the current PowerShell session is running with elevated permissions
    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    function Set-NinjaProperty {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $True)]
            [String]$Name,
            [Parameter()]
            [String]$Type,
            [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
            $Value,
            [Parameter()]
            [String]$DocumentName
        )
    
        $Characters = $Value | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
        if ($Characters -ge 200000) {
            throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded; the value is greater than or equal to 200,000 characters.")
        }
        
        # If requested to set the field value for a Ninja document, we'll specify it here.
        $DocumentationParams = @{}
        if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName }
        
        # This is a list of valid fields that can be set. If no type is specified, it is assumed that the input does not need to be changed.
        $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG"
        if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type! Please check here for valid types: https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" }
        
        # The field below requires additional information to be set.
        $NeedsOptions = "Dropdown"
        if ($DocumentName) {
            if ($NeedsOptions -contains $Type) {
                # Redirect error output to the success stream to make it easier to handle errors if nothing is found or if something else goes wrong.
                $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1
            }
        }
        else {
            if ($NeedsOptions -contains $Type) {
                $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1
            }
        }
        
        # If an error is received with an exception property, the function will exit with that error information.
        if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions }
        
        # The below types require values not typically given in order to be set. The below code will convert whatever we're given into a format ninjarmm-cli supports.
        switch ($Type) {
            "Checkbox" {
                # Although it's highly likely we were given a value like "True" or a boolean datatype, it's better to be safe than sorry.
                $NinjaValue = [System.Convert]::ToBoolean($Value)
            }
            "Date or Date Time" {
                # Ninjarmm-cli expects the GUID of the option to be selected. Therefore, the given value will be matched with a GUID.
                $Date = (Get-Date $Value).ToUniversalTime()
                $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date
                $NinjaValue = $TimeSpan.TotalSeconds
            }
            "Dropdown" {
                # Ninjarmm-cli is expecting the guid of the option we're trying to select. So we'll match up the value we were given with a guid.
                $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name"
                $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID
        
                if (-not $Selection) {
                    throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown options.")
                }
        
                $NinjaValue = $Selection
            }
            default {
                # All the other types shouldn't require additional work on the input.
                $NinjaValue = $Value
            }
        }
        
        # We'll need to set the field differently depending on if its a field in a Ninja Document or not.
        if ($DocumentName) {
            $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1
        }
        else {
            $CustomField = $NinjaValue | Ninja-Property-Set-Piped -Name $Name 2>&1
        }
        
        if ($CustomField.Exception) {
            throw $CustomField
        }
    }

    # Function to find installation keys based on the display name, optionally returning uninstall strings
    function Find-InstallKey {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline = $True)]
            [String]$DisplayName,
            [Parameter()]
            [Switch]$UninstallString,
            [Parameter()]
            [String]$UserBaseKey
        )
        process {
            # Initialize an empty list to hold installation objects
            $InstallList = New-Object System.Collections.Generic.List[Object]

            # If no user base key is specified, search in the default system-wide uninstall paths
            if (!$UserBaseKey) {
                # Search for programs in 32-bit and 64-bit locations. Then add them to the list if they match the display name
                $Result = Get-ChildItem -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
                if ($Result) { $InstallList.Add($Result) }

                $Result = Get-ChildItem -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
                if ($Result) { $InstallList.Add($Result) }
            }
            else {
                # If a user base key is specified, search in the user-specified 64-bit and 32-bit paths.
                $Result = Get-ChildItem -Path "$UserBaseKey\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
                if ($Result) { $InstallList.Add($Result) }
    
                $Result = Get-ChildItem -Path "$UserBaseKey\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
                if ($Result) { $InstallList.Add($Result) }
            }
    
            # If the UninstallString switch is specified, return only the uninstall strings; otherwise, return the full installation objects.
            if ($UninstallString) {
                $InstallList | Select-Object -ExpandProperty UninstallString -ErrorAction SilentlyContinue
            }
            else {
                $InstallList
            }
        }
    }

    if (!$ExitCode) {
        $ExitCode = 0
    }
}
process {
    # Check if the script is running with elevated permissions (administrator rights)
    if (!(Test-IsElevated)) {
        Write-Host -Object "[Error] Access Denied. Please run with Administrator privileges."
        exit 1
    }

    # Search for Chrome installations on the system and enable chrome extension search if found.
    Find-InstallKey -DisplayName "Chrome" | ForEach-Object {
        $ChromeInstallations = $True
    }

    # Search for Firefox installations on the system and enable firefox extension search if found.
    Find-InstallKey -DisplayName "Firefox" | ForEach-Object {
        $FireFoxInstallations = $True
    }

    # Search for Edge installations on the system and flag if found and enable edge extension search if found.
    Find-InstallKey -DisplayName "Edge" | ForEach-Object {  
        $EdgeInstallations = $True
    }

    # Retrieve all user profiles from the system
    $UserProfiles = Get-UserHives -Type "All"
    # Loop through each profile on the machine
    Foreach ($UserProfile in $UserProfiles) {
        # Load User ntuser.dat if it's not already loaded
        If (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe LOAD HKU\$($UserProfile.SID) `"$($UserProfile.UserHive)`"" -Wait -WindowStyle Hidden
        }

        # Repeat search for installations of browsers but in the user's registry context
        Find-InstallKey -UserBaseKey "Registry::HKEY_USERS\$($UserProfile.SID)" -DisplayName "Chrome" | ForEach-Object { 
            $ChromeInstallations = $True
        }
        Find-InstallKey -UserBaseKey "Registry::HKEY_USERS\$($UserProfile.SID)" -DisplayName "Firefox" | ForEach-Object { 
            $FireFoxInstallations = $True
        }
        Find-InstallKey -UserBaseKey "Registry::HKEY_USERS\$($UserProfile.SID)" -DisplayName "Edge" | ForEach-Object { 
            $EdgeInstallations = $True
        }

        # Unload NTuser.dat
        If ($ProfileWasLoaded -eq $false) {
            [gc]::Collect()
            Start-Sleep 1
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden | Out-Null
        }
    }

    # Initialize a list to store details of detected browser extensions
    $BrowserExtensions = New-Object System.Collections.Generic.List[object]

    # If Chrome was found, search for Chrome extensions in each user's profile
    if ($ChromeInstallations) {
        Write-Host -Object "A Google Chrome installation was detected. Searching Chrome for browser extensions..."
        $UserProfiles | ForEach-Object {
            if (!(Test-Path -Path "$($_.Path)\AppData\Local\Google\Chrome\User Data" -ErrorAction SilentlyContinue)) {
                return
            }

            if(Test-Path -Path "$($_.Path)\AppData\Local\Google\Chrome\User Data\Local State" -ErrorAction SilentlyContinue){
                $AllProfiles = Get-Content -Path "$($_.Path)\AppData\Local\Google\Chrome\User Data\Local State" | ConvertFrom-JSON
            }

            $PreferenceFiles = Get-ChildItem "$($_.Path)\AppData\Local\Google\Chrome\User Data\*\Preferences" -Exclude "System Profile" | Select-Object -ExpandProperty Fullname

            foreach ($PreferenceFile in $PreferenceFiles) {

                $GooglePreferences = Get-Content -Path $PreferenceFile | ConvertFrom-Json
                if($AllProfiles){
                    $ProfileLocation = $PreferenceFile | Get-Item | Select-Object -ExpandProperty Directory | Split-Path -Leaf
                    $ProfileName = $AllProfiles.profile.info_cache | Select-Object -ExpandProperty $ProfileLocation | Select-Object -ExpandProperty Name
                }else{
                    $ProfileName = $GooglePreferences.profile.name
                }

                foreach ($Extension in $GooglePreferences.extensions.settings.PSObject.Properties) {
                    $BrowserExtensions.Add(
                        [PSCustomObject]@{
                            Browser        = "Chrome"
                            User           = $_.UserName
                            Profile        = $ProfileName
                            Name           = $Extension.Value.manifest.name
                            "Extension ID" = $Extension.name
                            Description    = $Extension.Value.manifest.description
                        }
                    )
                }
            }
        }
    }

    # If Edge was found, search for Edge extensions in each user's profile
    if ($EdgeInstallations) {
        Write-Host -Object "A Microsoft Edge installation was detected. Searching Microsoft Edge for browser extensions..."
        $UserProfiles | ForEach-Object {
            if (!(Test-Path -Path "$($_.Path)\AppData\Local\Microsoft\Edge\User Data" -ErrorAction SilentlyContinue)) {
                return
            }

            if(Test-Path -Path "$($_.Path)\AppData\Local\Microsoft\Edge\User Data\Local State" -ErrorAction SilentlyContinue){
                $AllProfiles = Get-Content -Path "$($_.Path)\AppData\Local\Microsoft\Edge\User Data\Local State" | ConvertFrom-JSON
            }

            $PreferenceFiles = Get-ChildItem "$($_.Path)\AppData\Local\Microsoft\Edge\User Data\*\Preferences" -Exclude "System Profile" | Select-Object -ExpandProperty Fullname

            foreach ($PreferenceFile in $PreferenceFiles) {

                $EdgePreferences = Get-Content -Path $PreferenceFile | ConvertFrom-Json
                if($AllProfiles){
                    $ProfileLocation = $PreferenceFile | Get-Item | Select-Object -ExpandProperty Directory | Split-Path -Leaf
                    $ProfileName = $AllProfiles.profile.info_cache | Select-Object -ExpandProperty $ProfileLocation | Select-Object -ExpandProperty Name
                }else{
                    $ProfileName = $EdgePreferences.profile.name
                }

                foreach ($Extension in $EdgePreferences.extensions.settings.PSObject.Properties) {
                    if ($Extension.Value.active_bit -like "False" ) { continue }
                    if (!$Extension.Value.manifest.name) { continue }
                    $BrowserExtensions.Add(
                        [PSCustomObject]@{
                            Browser        = "Edge"
                            User           = $_.UserName
                            Profile        = $ProfileName
                            Name           = $Extension.Value.manifest.name
                            "Extension ID" = $Extension.name
                            Description    = $Extension.Value.manifest.description
                        }
                    )
                }
            }
        }
    }

    # If Firefox was found, search for Firefox extensions in each user's profile
    if ($FireFoxInstallations) {
        Write-Host -Object "A Firefox installation was detected. Searching Firefox for browser extensions..." 
        $UserProfiles | ForEach-Object {
            if (!(Test-Path -Path "$($_.Path)\AppData\Roaming\Mozilla\Firefox\Profiles" -ErrorAction SilentlyContinue)) {
                return
            }

            $FirefoxProfileFolders = Get-ChildItem -Path "$($_.Path)\AppData\Roaming\Mozilla\Firefox\Profiles" -Directory | Where-Object { $_.Name -match "\.default-release$" } | Select-Object -ExpandProperty Fullname

            foreach ( $FirefoxProfile in $FirefoxProfileFolders ) {

                if (!(Test-Path -Path "$FirefoxProfile\extensions.json")) {
                    continue
                }

                $Extensions = Get-Content -Path "$FirefoxProfile\extensions.json" | ConvertFrom-Json

                foreach ($Extension in $Extensions.addons) {
                    $BrowserExtensions.Add(
                        [PSCustomObject]@{
                            Browser        = "Firefox"
                            User           = $_.UserName
                            Profile        = "N/A"
                            Name           = $Extension.defaultlocale.name
                            "Extension ID" = $Extension.id
                            Description    = $Extension.defaultlocale.description
                        }
                    )
                }
            }
        }
    }

    # Check if there are any browser extensions to process
    if ($BrowserExtensions.Count -gt 0) {
        # Format the BrowserExtensions list to include a shortened description if the description is too long.
        $BrowserExtensions = $BrowserExtensions | Select-Object Browser, User, Profile, Name, "Extension ID", @{
            Name       = "Description"
            Expression = {
                $Characters = $_.Description | Measure-Object -Character | Select-Object -ExpandProperty Characters
                if ($Characters -gt 75) {
                    "$(($_.Description).SubString(0,75))(...)"
                }
                else {
                    $_.Description
                }
            }
        }
    }

    # Check if extensions were found and if we were requested to set a multiline custom field
    if ($BrowserExtensions.Count -gt 0 -and $MultilineCustomField) {
        try {
            Write-Host "Attempting to set Custom Field '$MultilineCustomField'."
            $CustomFieldValue = New-Object System.Collections.Generic.List[string]

            # Sort and format the list of extensions for output
            $CustomFieldList = $BrowserExtensions | Sort-Object Browser, User, Profile, Name | Select-Object Browser, User, Profile, Name, "Extension ID", Description
            $CustomFieldValue.Add(($CustomFieldList | Format-List | Out-String))

            # Measure the total character count of the formatted string
            $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
            if ($Characters -ge 9500) {
                Write-Warning "10,000 Character Limit has been reached! Trimming output until the character limit is satisfied..."
                    
                # If it doesn't comply with the limits we'll need to recreate it with some adjustments.
                $i = 0
                do {
                    # Recreate the custom field output starting with a warning that we truncated the output.
                    $CustomFieldValue = New-Object System.Collections.Generic.List[string]
                    $CustomFieldValue.Add("This info has been truncated to accommodate the 10,000 character limit.")
                    
                    # Flip the array so that the last entry is on top.
                    [array]::Reverse($CustomFieldList)
    
                    # Remove the next item.
                    $CustomFieldList[$i] = $null
                    $i++
    
                    # We'll flip the array back to right side up.
                    [array]::Reverse($CustomFieldList)
    
                    # Add it back to the output.
                    $CustomFieldValue.Add(($CustomFieldList | Format-List | Out-String))
    
                    # Check that we now comply with the character limit. If not restart the do loop.
                    $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
                }while ($Characters -ge 9500)
            }

            Set-NinjaProperty -Name $MultilineCustomField -Value $CustomFieldValue
            Write-Host "Successfully set Custom Field '$MultilineCustomField'!"
        }
        catch {
            Write-Host "[Error] $($_.Exception.Message)"
            $ExitCode = 1
        }
    }

    # Check if extensions were found and if we were requested to set a WYSIWYG custom field.
    if ($BrowserExtensions.Count -gt 0 -and $WysiwygCustomField) {
        try {
            Write-Host "Attempting to set Custom Field '$WysiwygCustomField'."
    
            # Prepare the custom field output.
            $CustomFieldValue = New-Object System.Collections.Generic.List[string]
    
            # Convert the matching events into an html report.
            $htmlTable = $BrowserExtensions | Sort-Object Browser, User, Profile, Name | Select-Object Browser, User, Profile, Name, "Extension ID", Description | ConvertTo-Html -Fragment
    
            # Add the newly created html into the custom field output.
            $CustomFieldValue.Add($htmlTable)
    
            # Check that the output complies with the hard character limits.
            $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
            if ($Characters -ge 199500) {
                Write-Warning "200,000 Character Limit has been reached! Trimming output until the character limit is satisfied..."
                    
                # If it doesn't comply with the limits we'll need to recreate it with some adjustments.
                $i = 0
                do {
                    # Recreate the custom field output starting with a warning that we truncated the output.
                    $CustomFieldValue = New-Object System.Collections.Generic.List[string]
                    $CustomFieldValue.Add("<h1>This info has been truncated to accommodate the 200,000 character limit.</h1>")
    
                    # Flip the array so that the last entry is on top.
                    [array]::Reverse($htmlTable)
                    # If the next entry is a row we'll delete it.
                    if ($htmlTable[$i] -match '<tr><td>' -or $htmlTable[$i] -match '<tr class=') {
                        $htmlTable[$i] = $null
                    }
                    $i++
                    # We'll flip the array back to right side up.
                    [array]::Reverse($htmlTable)
    
                    # Add it back to the output.
                    $CustomFieldValue.Add($htmlTable)
    
                    # Check that we now comply with the character limit. If not restart the do loop.
                    $Characters = $CustomFieldValue | Out-String | Measure-Object -Character | Select-Object -ExpandProperty Characters
                }while ($Characters -ge 199500)
            }
    
            # Set the custom field.
            Set-NinjaProperty -Name $WysiwygCustomField -Value $CustomFieldValue
            Write-Host "Successfully set Custom Field '$WysiwygCustomField'!"
        }
        catch {
            Write-Host "[Error] $($_.Exception.Message)"
            $ExitCode = 1
        }
    }

    if ($BrowserExtensions.Count -gt 0) {
        Write-Host "Browser extensions were detected."
        $BrowserExtensions | Sort-Object Browser, User, Profile, Name | Format-List | Out-String | Write-Host
    }
    else {
        Write-Host "No browser extensions were found!"
    }

    exit $ExitCode
}
end {
    
    
    
}

 

Análisis detallado

Este script para listar las extensiones del navegador en Windows está escrito en PowerShell y requiere la versión 5.1 o superior. Aquí tienes un desglose paso a paso de cómo funciona:

1. Inicialización y parámetros

  • El script para listar las extensiones del navegador en Windows comienza comprobando los parámetros requeridos, $MultilineCustomField y $WysiwygCustomField, que pueden establecerse mediante variables de entorno o pasarse directamente.
  • Valida que estos campos, si ambos se proporcionan, no sean iguales para evitar conflictos.

2. Recuperación de colmenas de usuarios

  • La función Get-UserHives identifica los perfiles de usuario en el sistema, incluidas las cuentas AzureAD y Domain.
  • Esta función es crucial para asegurar que el script escanea todos los directorios de usuario relevantes para los datos del navegador.

3. Comprobación de permisos

  • La función Test-IsElevated garantiza que el script se ejecuta con privilegios administrativos, necesarios para acceder a archivos comprimidos y directorios del registro específicos del usuario.

4. Detección del navegador

  • El script busca instalaciones de Chrome, Firefox y Edge mediante la función Find-InstallKey, que analiza tanto las claves de registro de todo el sistema como las específicas del usuario.

5. Carga del perfil

  • Para cada navegador detectado, el script itera a través de los perfiles de usuario, carga las colmenas de registro necesarias y busca los datos de extensión del navegador.
  • Esto implica comprobar directorios específicos y archivos de configuración donde los navegadores almacenan información sobre extensiones del navegador.

6. Recogida de datos

  • Recopila una lista de las extensiones instaladas, incluyendo detalles como nombre del navegador, usuario, perfil, nombre de la extensión, ID y descripción.

7. Actualizaciones de campos personalizados

  • Si se encuentran extensiones, el script intenta establecer campos personalizados en un sistema de documentación utilizando Set-NinjaProperty. Gestiona los posibles límites de caracteres recortando la salida según sea necesario.

8. Output:

  • El resultado final es una lista ordenada de las extensiones del navegador, que puede mostrarse o almacenarse en campos personalizados para su posterior análisis.

Posibles casos de uso

Imagina a un profesional de TI que gestiona la infraestructura informática de una empresa mediana. Debe asegurarse de que todas las extensiones del navegador instaladas en los equipos de los usuarios cumplen las políticas de la empresa. Ejecutando este script, puede inventariar rápidamente todas las extensiones en Chrome, Firefox y Edge, identificar cualquier extensión no autorizada o de riesgo y tomar medidas correctivas.

Por ejemplo, si una nueva política prohíbe extensiones que no han sido examinadas por el departamento de TI, este script permite al administrador de TI generar una lista completa de las extensiones actuales. A continuación, el administrador puede cotejar esta lista con las extensiones del navegador aprobadas y eliminar a distancia las que supongan un riesgo.

Comparaciones

En comparación con los métodos manuales de comprobación individual de cada navegador y perfil de usuario, este script es significativamente más eficaz y exhaustivo. Otros enfoques podrían implicar el uso de herramientas específicas del navegador o la inspección manual, ambos métodos requieren mucho tiempo y son propensos a errores humanos. Este script automatiza el proceso, garantizando la coherencia y ahorrando un tiempo valioso.

FAQ

P: ¿Este script se puede ejecutar a distancia?

R: Sí, siempre que tengas los permisos administrativos necesarios y acceso a las máquinas remotas.

P: ¿Qué ocurre si la descripción de una extensión supera el límite de caracteres?

R: El script recorta la descripción para ajustarla al límite de caracteres, garantizando que la salida siga siendo manejable.

P: ¿El script funciona en todas las versiones de Windows?

R: El script requiere Windows 10 o Windows Server 2016 y posteriores, ya que depende de funciones disponibles en estas versiones.

P: ¿Se puede modificar el script para que sea compatible con otros navegadores?

R: Sí, con las modificaciones adecuadas en la lógica de detección y recopilación de datos, se puede añadir compatibilidad con navegadores adicionales.

Implicaciones

Los resultados de este script para listar las extensiones del navegador en Windows pueden tener importantes implicaciones para la seguridad informática. Al identificar todas las extensiones del navegador instaladas, los profesionales de TI pueden detectar posibles riesgos para la seguridad y garantizar el cumplimiento de las políticas de la organización. Este enfoque proactivo ayuda a mitigar el riesgo de infecciones por malware y violaciones de datos asociadas a extensiones maliciosas o mal mantenidas.

Recomendaciones

Cuando utilices este script para listar las extensiones del navegador en Windows, ten en cuenta las siguientes prácticas recomendadas:

  • Ejecuta el script regularmente como parte de tu programa de mantenimiento informático.
  • Revisa la lista de extensiones del navegador detectadas y compáralas con tu lista de extensiones del navegador aprobada.
  • Conciencia a los usuarios sobre los riesgos asociados a las extensiones del navegador y anímalos a instalar sólo extensiones aprobadas.

Reflexiones finales

La gestión de las extensiones del navegador es un aspecto crítico del mantenimiento de la seguridad informática. Este script proporciona una forma automatizada y eficaz de inventariar y supervisar las extensiones del navegador en varios usuarios y navegadores. Al integrar esta herramienta en sus procesos de gestión de TI, puedes mejorar la postura de seguridad de tu organización y garantizar el cumplimiento de las políticas.

NinjaOne ofrece una serie de herramientas y funciones que complementan este script, proporcionando una solución integral para la gestión y la seguridad de TI. Con NinjaOne, puedes automatizar las tareas rutinarias, supervisar los endpoints y garantizar que tu entorno de TI sea seguro y cumpla las normativas.

Próximos pasos

La creación de un equipo de TI próspero y eficaz requiere contar con una solución centralizada que se convierta en tu principal herramienta de prestación de servicios. NinjaOne permite a los equipos de TI supervisar, gestionar, proteger y dar soporte a todos sus dispositivos, estén donde estén, sin necesidad de complejas infraestructuras locales.

Obtén más información sobre NinjaOne Endpoint Management, echa un vistazo a un tour en vivo o comienza tu prueba gratuita de la plataforma NinjaOne.

Categorías:

Quizá también te interese…

×

¡Vean a NinjaOne en acción!

Al enviar este formulario, acepto la política de privacidad de NinjaOne.

Términos y condiciones de NinjaOne

Al hacer clic en el botón «Acepto» que aparece a continuación, estás aceptando los siguientes términos legales, así como nuestras Condiciones de uso:

  • Derechos de propiedad: NinjaOne posee y seguirá poseyendo todos los derechos, títulos e intereses sobre el script (incluidos los derechos de autor). NinjaOne concede al usuario una licencia limitada para utilizar el script de acuerdo con estos términos legales.
  • Limitación de uso: solo podrás utilizar el script para tus legítimos fines personales o comerciales internos, y no podrás compartirlo con terceros.
  • Prohibición de republicación: bajo ninguna circunstancia está permitido volver a publicar el script en ninguna biblioteca de scripts que pertenezca o esté bajo el control de cualquier otro proveedor de software.
  • Exclusión de garantía: el script se proporciona «tal cual» y «según disponibilidad», sin garantía de ningún tipo. NinjaOne no promete ni garantiza que el script esté libre de defectos o que satisfaga las necesidades o expectativas específicas del usuario.
  • Asunción de riesgos: el uso que el usuario haga del script corre por su cuenta y riesgo. El usuario reconoce que existen ciertos riesgos inherentes al uso del script, y entiende y asume cada uno de esos riesgos.
  • Renuncia y exención: el usuario no hará responsable a NinjaOne de cualquier consecuencia adversa o no deseada que resulte del uso del script y renuncia a cualquier derecho o recurso legal o equitativo que pueda tener contra NinjaOne en relación con su uso del script.
  • CLUF: si el usuario es cliente de NinjaOne, su uso del script está sujeto al Contrato de Licencia para el Usuario Final (CLUF).