Safeguard
Cloud Security•

Block New Client Secrets in Entra Before an Attacker Adds One

In the ConnectWise app abuse, a hijacked app added client secrets to other apps for persistence. Entra app management policies can block that. Here is how to roll them out safely.

Safeguard Security Team
6 min read

During the ConnectWise Cloud Backup incident on 15 October 2026, Huntress found that in one partner tenant a ConnectWise Cloud Management app granted itself Global Administrator and then added new client secrets, valid until 2028, to two other applications. One of them was Huntress's own app. A client secret on someone else's app is quiet persistence: it survives password resets, session revocation and even disabling the app that planted it.

Microsoft Entra has a control built for exactly this: app management policies that block new password credentials on applications and service principals. Huntress lists them among its recommendations. This guide shows how to measure your exposure, turn the policy on and handle the apps that still need secrets.

What's affected

Any Entra tenant where the tenant default app management policy is off, which is the default state. Per Microsoft Graph documentation, the tenantAppManagementPolicy object "always exists and is disabled by default."

Two things make secrets attractive to an attacker. Anyone with enough directory privilege can add one to an app they don't own. And once added, it works from anywhere until it expires, which the attacker gets to choose.

One limit to keep in mind from the start: Microsoft says neither the tenant default nor per-app policies block token issuance for existing applications. An app that already violates the policy keeps working. Only the create or update operation that breaks the rule is blocked. So the policy stops new secrets, and you still have to clean up the old ones.

How to check if you're exposed

Connect with Microsoft Graph PowerShell and read the current policy:

Connect-MgGraph -Scopes "Policy.Read.All","Application.Read.All"
Import-Module Microsoft.Graph.Identity.SignIns
Get-MgPolicyDefaultAppManagementPolicy | Format-List IsEnabled, ApplicationRestrictions, ServicePrincipalRestrictions

If IsEnabled is false, nothing is enforced.

Next, count who actually relies on secrets. Applications you own and service principals of apps from other tenants are separate objects with separate restrictions, so check both.

$apps = Get-MgApplication -All -Property "id,displayName,appId,passwordCredentials"
$apps | Where-Object { $_.PasswordCredentials.Count -gt 0 } |
  Select-Object DisplayName, AppId, @{n="Secrets";e={$_.PasswordCredentials.Count}}

$sps = Get-MgServicePrincipal -All -Property "id,displayName,appId,passwordCredentials"
$sps | Where-Object { $_.PasswordCredentials.Count -gt 0 } |
  Select-Object DisplayName, AppId, @{n="Secrets";e={$_.PasswordCredentials.Count}}

Then look for long-lived secrets. The passwordCredential resource exposes startDateTime and endDateTime, so this flags anything valid for more than a year:

foreach ($o in @($apps) + @($sps)) {
  foreach ($c in $o.PasswordCredentials) {
    if (($c.EndDateTime - $c.StartDateTime).TotalDays -gt 365) {
      [pscustomobject]@{ Object = $o.DisplayName; AppId = $o.AppId; KeyId = $c.KeyId; Hint = $c.Hint; Expires = $c.EndDateTime }
    }
  }
}

Every row is a credential an attacker would love to have created. Any secret on a service principal that nobody on your team recognizes deserves a closer look now.

What to do now

  1. Fix the apps that need secrets first. Move them to certificates or workload identity federation before enforcement, or plan explicit exemptions for them in step 4.

  2. Turn on the restriction in the admin center. Microsoft's how-to: Entra ID, Enterprise apps, Application policies, select Block password addition, set the status to On with Applies to set to All applications, then Save. In the portal this setting combines the passwordAddition and symmetricKeyAddition restrictions.

  3. Or do it through Graph so it's in source control. The documented update call is PATCH /policies/defaultAppManagementPolicy, which accepts restrictions for both applicationRestrictions and servicePrincipalRestrictions. Include any restrictions already present, because omitted entries can be disabled by accident.

    Connect-MgGraph -Scopes "Policy.ReadWrite.ApplicationConfiguration"
    $block = @(
      @{ restrictionType = "passwordAddition"; maxLifetime = $null; restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2015-01-01T00:00:00Z") }
      @{ restrictionType = "symmetricKeyAddition"; maxLifetime = $null; restrictForAppsCreatedAfterDateTime = [System.DateTime]::Parse("2015-01-01T00:00:00Z") }
    )
    $params = @{
      isEnabled = $true
      applicationRestrictions = @{ passwordCredentials = $block }
      servicePrincipalRestrictions = @{ passwordCredentials = $block }
    }
    Update-MgPolicyDefaultAppManagementPolicy -BodyParameter $params
    

    The restrictForAppsCreatedAfterDateTime value controls which objects are covered. Microsoft notes that service principals with a null creation date are treated as created on 1 January 2019, so an earlier date covers them too.

  4. Exempt narrowly. For a specific app, create a custom policy under /policies/appManagementPolicies with the restriction set to disabled and assign it to that app. Only one custom policy can be assigned per app or service principal. For an automation that creates apps, Microsoft documents caller exemptions through custom security attributes referenced in excludeActors.

  5. Cap certificate lifetimes too. The asymmetricKeyLifetime restriction under keyCredentials takes an ISO 8601 duration, so a planted certificate can't be valid for years.

  6. Delete the long-lived secrets you found, after rotating the apps that legitimately used them.

Why it matters

Incident response tends to focus on the identity that was abused. In the ConnectWise case, that was the backup app. The secrets it added to other apps are what let an attacker come back after that app is disabled.

Policy is cheaper than hunting. Blocking new secrets tenant-wide means a future attacker with directory privileges has to use a noisier path, and any legitimate need for a secret becomes a documented exception instead of a default.

Try it yourself

Run the long-lived secret query from the check section in a test tenant or your production tenant. Sort by Expires and pick the three secrets that live longest. For each one, write down the owner, what it authenticates to and whether a certificate or federated credential could replace it. That list is your rollout plan.

Click through it at your own pace, or open it full screen.

Quick check

Does enabling the policy break apps that already use client secrets?

No. Microsoft states the policies don't block token issuance for existing applications. They only block the create or update operation that violates the rule, such as adding a new secret.

Which restriction set covers apps from another tenant, like a vendor's backup tool?

servicePrincipalRestrictions. Restrictions under applicationRestrictions apply to application objects your tenant owns.

How do you let one app keep adding secrets while the rest are blocked?

Create a custom app management policy with that restriction disabled and assign it to the app. The custom policy takes precedence over the tenant default for that object.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.