PowerShell / PowerShell

PowerShell for every system!
https://microsoft.com/PowerShell
MIT License
43.55k stars 7.06k forks source link

Add support for Enterprise signed scripts #21550

Open SteveL-MSFT opened 2 weeks ago

SteveL-MSFT commented 2 weeks ago

Summary of the new feature / enhancement

[Updated based on feedback below]

Azure has a new Trusted Signing service in public preview. One of the features is rotation of the signing cert. This breaks how PowerShell currently validates signed scripts.

PowerShell only trusts the leaf cert in the chain and this cert will be rotated (by Azure or any other cert service). So a customer may initially sign a script and it works, but then after the cert is rotated new scripts won't work since the leaf cert has changed and old certs might have expired so old scripts also stop working in an execution policy that requires signing.

Proposed technical implementation details (optional)

Azure Trust Signing customer signing cert has a customer unique EKU. We would allow storing additional signing metadata in the registry:

Existing AllSigned and RemoteSigned execution policies will respect the EKU if it has been stored in the registry (this is how the customer opts into this change in behavior):

This would allow for alternate signing services to also participate and work with PowerShell.

Cert revocation will continued to be handled by WinTrust and no change for PowerShell.

jborean93 commented 2 weeks ago

old scripts also stop working in an execution policy that requires signing.

Just to expand on this, they do still work it's just that PowerShell prompts whether you trust the new publisher or not.

Do you want to run software from this untrusted publisher?
File C:\temp\cert\ps_ca_signed.ps1 is published by CN=SelfSignedTest-Signed and is not trusted on your system. Only run
 scripts from trusted publishers.
[V] Never run  [D] Do not run  [R] Run once  [A] Always run  [?] Help (default is "D"):

A few questions

Personally I would not be a fan of having to opt into allowing this check. I would find that it adds a barrier to entry for using these types of certificates and punishes developers for trying to sign their code which is not something we would want to encourage. Instead if there is a desire to have a way to use the existing behaviour maybe that should be opt in rather than the new ay being opt in?

SteveL-MSFT commented 2 weeks ago

The opt-in is necessary as PowerShell would need to know the EKU to check against. If you're concerned about new execution policies, then an alternate option is that if the reg property exists, then PowerShell would run AllSigned and RemoteSigned as effectively EnterpriseSigned and EnterpriseRemoteSigned, but that could be confusing to users.

about Group Policy Settings - PowerShell
Describes the Group Policy settings for PowerShell
jborean93 commented 2 weeks ago

If the EKU is trusted, the idea is that you wouldn't be prompted. It doesn't seem like we need to know about the prefix and thus not make it configurable. The prefix seems to only be necessary if we want to restrict it to Azure signed certs, for example.

Sorry if I wasn't clear, this was more of a way to improve the UX of running an untrusted publisher for the first time. Right now if PowerShell is set to run a signed script with a publisher that is unknown it will prompt you if you wish to trust it. If you do it will add that cert to the TrustedPublisher store for you so you don't have to. The prompt question was whether this should also be implemented for this new check, at least for known EKU prefixes, to trust interactively. It also brings in question whether users can explicitly opt into a user trust level like in HKCU. Right now it seems like PowerShell checks both the CurrentUser and LocalMachine TrustedPublisher store.

The proposal is two new execution policies and AllSigned and RemoteSigned do not change.

The question was whether this new check should be a new policy or a new behaviour for the existing policies.

Good question I hadn't considered, PSResourceGet is using Get-AuthenticodeSignature currently, so it'll pass regardless of the EKU. Maybe it should be enhanced to give a warning if the EKU is set and doesn't match?

While I cannot say how PSResourceGet implemented things I know the PowerShellGet -SkipPublisherCheck was problematic as it scanned the subject of both the leaf and issuer certificates. I know PSResourceGet hasn't implemented this by default but I'm unsure whether the -AuthenticodeCheck is a simple signature check or whether it also does the extra checks that PowerShellGet also added on top. If it's just a simple signature check then it sounds like we don't need to worry about that.

The opt-in is necessary as PowerShell would need to know the EKU to check against.

It sounds like the opt-in is should be populating the registry value rather than opt-ing into checking for the registry value in the first place. My concern is that I publish a module that's signed by Azure TS (or even my own internal mechanism) and users who want to verify the signature now need to explicitly trust my EKU rather than today where PowerShell will at least prompt whether you trust it or not. As mentioned earlier it is also possible for non-admin users to trust a publisher.

One other area I haven't looked into too closely is how do revocations work with certs. Should PowerShell offer the ability to revoke a specific EKU. Does the normal authenticode signature check done by Windows take into account revoked certificates, etc.

SteveL-MSFT commented 2 weeks ago

Sorry if I wasn't clear, this was more of a way to improve the UX of running an untrusted publisher for the first time. Right now if PowerShell is set to run a signed script with a publisher that is unknown it will prompt you if you wish to trust it. If you do it will add that cert to the TrustedPublisher store for you so you don't have to. The prompt question was whether this should also be implemented for this new check, at least for known EKU prefixes, to trust interactively. It also brings in question whether users can explicitly opt into a user trust level like in HKCU. Right now it seems like PowerShell checks both the CurrentUser and LocalMachine TrustedPublisher store.

Ok, now I see what you are asking. Yes, it makes sense to improve the prompt but the user would need to be elevated to populate the registry.

The proposal is two new execution policies and AllSigned and RemoteSigned do not change.

The question was whether this new check should be a new policy or a new behaviour for the existing policies.

I believe the question was answered?

Good question I hadn't considered, PSResourceGet is using Get-AuthenticodeSignature currently, so it'll pass regardless of the EKU. Maybe it should be enhanced to give a warning if the EKU is set and doesn't match?

While I cannot say how PSResourceGet implemented things I know the PowerShellGet -SkipPublisherCheck was problematic as it scanned the subject of both the leaf and issuer certificates. I know PSResourceGet hasn't implemented this by default but I'm unsure whether the -AuthenticodeCheck is a simple signature check or whether it also does the extra checks that PowerShellGet also added on top. If it's just a simple signature check then it sounds like we don't need to worry about that.

Since PSResourceGet calls out to Get-AuthenticodeSignature, it's a simpler check that PowerShell currently won't necessarily allow as it's a CA check rather than a leaf check. That's a separate issue we should discuss in PSResourceGet repo if any change is needed (I believe intent was to just check it was signed and not whether it would work with an execution policy).

One other area I haven't looked into too closely is how do revocations work with certs. Should PowerShell offer the ability to revoke a specific EKU. Does the normal authenticode signature check done by Windows take into account revoked certificates, etc.

Authenticode (which we call the WinTrust APIs) should automatically take care of cert revocation.

jborean93 commented 2 weeks ago

I believe the question was answered?

To be clear I'm suggesting that instead of adding two completely new policies you either just add this new EKU registry checking behaviour by default with no opt in or out option or add in an opt out option instead of having people explicitly have to use a new policy. This would be in lieu of explicit execution policies to control things.

Authenticode (which we call the WinTrust APIs) should automatically take care of cert revocation.

To revoke a single cert, what happens if you want to distrust a specific publisher altogether. You would have to find every single cert they used to sign and add them to get that to work.

SteveL-MSFT commented 1 week ago

@jborean93 I see. So the fact that the registry property exists would be an indicator that the user has opted into the change in behavior. That seems reasonable to me.

Get-ExecutionPolicy currently returns the ExecutionPolicy enum value, but it can have a note property added that is the list of EKUs and FormatData can present it.

Set-ExecutionPolicy would need new parameters to set (replace) the EKUs.

Need to think more about revocation.

SteveL-MSFT commented 1 week ago

It seems like PowerShell just relies on the current system for cert revocation: https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-cert-revocation

This means that customers can either revoke individual certs (yes, can be laborious), or revoke all the certs.

Revoke a certificate profile in Trusted Signing
How-to revoke a Trusted Signing certificate from Azure portal.