App Sandbox for "droplets"

My Mac app produces mini Mac apps configured for a particular purpose. Panic's Transmit does something similar with "droplets".

These little .app bundles are generated at runtime, and each embeds a unique file specified by the user in its Resources folder.

I would like the scaffold for these mini apps to use the App Sandbox (there's no reason not to, as they only access a single file from within their own bundles).

However, I can't code-sign the scaffold when building it from Xcode, as embedding a new resouce later will void the code signature (and then Gatekeeper will reject it, if it ever ends up in quarantine).

Is there a correct way to do something like this? The mini apps are not notarized or DeveloperID signed (that's fine, users can open them with control-clicking-open), but I would like to maximize security if at all possible, and use the sandbox.


Replies

Yes, you're supposed to use a special extended attribute which is a UUID to "personalize" the separate app stubs. See the section "Moving Away From Custom Resource Rules" of TN2206 macOS Code Signing In Depth for a description of com.apple.application-instance
Thanks for the tip. com.apple.application-instance doesn't seem like the way to go for me, because the resouce needs to be stored in the droplet bundle itself (the droplet is designed to be shared).

The section on Changes That Don't Invalidate a Code Signature sounds promising, but I don't understand what it actually means yet:

If you have optional or replaceable content you wish to change without invalidating the code signature, nested code can be replaced with equivalent (conforms to the designated requirement) nested code without disturbing the outer signature. This is the design mechanism for indirection in code bundles. It is acceptable to code-sign a bundle containing no main executable, and then treat it as nested code (typically in Contents).

What is indirection in code bundles?
You may not have much of a choice to use the UUID because there is no other mechanism which doesn't disturb the signature. Do your users actually have to share the .app directly? Seems unnecessary so long as the user on the other end can create an app from whatever the sharing mechanism is.
The indirection part doesn't really apply in your case because you don't have optional content which is also signed. The idea is you would bundle the optional/replaceable part, sign it, and put it in the app (or other bundle), instead of directly modifying files in e.g. Resources/. Then it says these bundles don't actually need an executable, they could just contain other resources.
TyngJJ wrote:

You may not have much of a choice to use the UUID because there is no other mechanism which doesn't disturb the signature.

The alternative is to actually re-sign the droplet. You can sign it ad hoc by default, but offer the user the ability to use their a signing identity of their choice.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

The alternative is to actually re-sign the droplet.

Continuing this line…

Now, the user won’t be able to distribute their droplet without notarising, which is a whole other kettle of fish. However, they should be able to continue running it locally. I fully expect this to get further locked down in the future but that there will always be a way for users to run locally-created code. See the announcements in WWDC 2019 Session 701 Advances in macOS Security.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Hmm. The droplets are designed to be shared with others, not just run locally. My users are not necessarily developers. It seems in the absence of custom resource rules the droplets will need to remain unsigned/unsandboxed. This seems like a shame to me... it means receiving them will be riskier than it needs to be.
So long as you're ok with the restriction that receivers of these shared droplets must also be users of your app, I don't see an issue.
The idea behind com.apple.application-instance is you're uniquely identifying the particular instance of a script or other automation without relying on naming/path/etc. Your app stub looks up its UUID in the local, though shared, database of droplets and executes the particular action stored there.
When one user shares an "app" with another, your solution merely packages the database record in a file, which is then reconstituted into an app stub by the recipient.
If on the other hand your solution was a generic app creator/maintainer, that is fundamentally incompatible with code signing/notarization/sandboxing/etc unless you also require your users to have development toolchains available (which you might automate for them).
There is no central database of droplets. Each droplet is a unique user-generated self-contained bundle. The user creating the droplets should not need to be a developer, and the recipient of a droplet should not need additional software to run it. Think FileMaker Pro or Hypercard.
So, let’s summarise your goals:
  • You want to create droplets, that is, customisable apps with a core of native code.

  • This customisation is done by the user when they save the droplet.

  • You want the user to be able to distribute a droplet to arbitrary other users without Gatekeeper entanglements.

  • That droplet must be self contained.

  • You want to avoid anything to do with code signing and notarisation.

It’s not possible to meet all of those goals. Gatekeeper requires that all code be signed and notarised. If you modify the droplet, you will break the seal on its code signature. The only escape hatch here is com.apple.application-instance, but those droplets aren’t self contained.

You will have to compromise somewhere.

Notably, not even AppleScript meets these goals. When you export a script as an app, there’s a Code Sign popup for choosing the code signing identity. And those apps are not notarised, hence the need for tools like SD Notary.

latenightsw.com/sd-notary-notarizing-made-easy/

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Thank you Quinn “The Eskimo!” and TyngJJ.

What I was really after was custom resource rules; it's a shame Apple knocked that on the head.

In an ideal world, I'd be able to sandbox and notarize the executable code in the bundle, and specify that this single non-code resource does not affect the code signature. This would reduce the attack area to virtually nil in my case.

As it stands, it seems distributing the app bundles unsigned/unsandboxed (like Transmit droplets) is my only option. Recipients will need to be directed to the the control-click + Open trick.

This is a shame, because the bundles will be vulnerable to all sorts of attacks (like code modification) that could otherwise be prevented.

If the exception isn't for executable code, have you thought about a different mechanism? For example setting the effective icon (rather than Info.plist + Resources/AppIcon.icns) doesn't affect the signature.
Depending how the app uses the file, the custom resource rule would open a vulnerability to attacks anyway.

For example setting the effective icon (rather than Info.plist + Resources/AppIcon.icns) doesn't affect the signature.

Ah, um, yeah it does. The Info.plist is a critical file that has its own slot in the code directory:

Code Block
% codesign -d -vvvvvv /Applications/BBEdit.app 2>&1 | grep -- "-1="
-1=4eea0af2cd2b202cf8bdbcfa9e1f6efb8373009fe9070588fbade7b7dddc7723
% shasum -a 256 /Applications/BBEdit.app/Contents/Info.plist
4eea0af2cd2b202cf8bdbcfa9e1f6efb8373009fe9070588fbade7b7dddc7723 …


The app’s icon is a resource and thus it’s sealed in the code signature’s resources file:

Code Block
% grep "BBEditApplication.icns" -A 6 /Applications/BBEdit.app/Contents/_CodeSignature/CodeResources
<key>Resources/BBEditApplication.icns</key>
<data>
VUsjiEcfRmwuleDzFTKs3HpjFa8=
</data>
--
<key>Resources/BBEditApplication.icns</key>
<dict>
<key>hash2</key>
<data>
Il+N0Z6jHmxXR8X4+680aqLG33WJCXmGXVVkscYWZTs=
</data>
</dict>


This is, in turn, sealed within the code directory:

Code Block
% codesign -d -vvvvvv /Applications/BBEdit.app 2>&1 | grep -- "-3="
-3=ab269e19737411f4cb8850ae51241c410c5478cb943cf92f0b068b588e837a59
% shasum -a 256 /Applications/BBEdit.app/Contents/_CodeSignature/CodeResources
ab269e19737411f4cb8850ae51241c410c5478cb943cf92f0b068b588e837a59 …


I think you might be thinking of a custom icon, which is stored within an Icon\r file in the root of the .app directory. And no, I don’t recommend using that file to store user data! That would most definitely constitute managerial abuse.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
I'll break down my last comment into parts:
  • The custom resource rule isn't used for executable code

  • The custom resource rule applies in Resources/

  • The custom resource rule might apply to something which has a different customization mechanism

  • The custom resource rule might apply to something which has a different customization mechanism, which also doesn't affect the signature

  • For example, icons have customization mechanisms which don't affect the signature

  • The effective icon (reference to the getter in NSURL, custom icon is unimplemented, must be set with NSWorkspace) does not affect the signature (tested using codesign -v -vv and Finder Get Info)

  • The effective icon has no effect on the signature, unlike Info.plist and Resources/*.icns, which some might use without realizing another method is available

For anyone reading this thread later and wanting to do something similar, note the following:

New in macOS 11 on Apple silicon Mac computers, and starting in the next macOS Big Sur 11 beta, the operating system will enforce that any executable must be signed with a valid signature before it’s allowed to run. There isn’t a specific identity requirement for this signature: a simple ad-hoc signature issued locally is sufficient, which includes signatures which are now generated automatically by the linker

So producing non-code signed app bundles at runtime on the customer's Mac is a no-go.

So producing non-code signed app bundles at runtime on the customer's
Mac is a no-go.

Right, but you can ad hoc sign them. This doesn’t require a signing identity, leaving you in pretty much the same place you started.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"