Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Sandboxing Python with Win32 App Isolation (windows.com)
64 points by pjmlp on March 14, 2024 | hide | past | favorite | 45 comments


Am I reading it correctly that because App Isolation is based on MSIX and MSIX requires code signing, that an app cannot be sandboxed unless it is signed? That seems silly.


It's mandatory on all sandboxing systems and for good reasons: sandboxing and identity are intimately related. To be usable the OS must cache permissions you grant the app but if your app isn't signed then nothing stops another app impersonating you and using your cached permissions.

This is true for literally every user-facing sandboxing system even when it may seem that it's not the case. For example, the web ties app identity to the domain name from where it was downloaded, but asserted domain name isn't good enough so in practice browsers require SSL/TLS for most permissions (which requires a certificate and is a form of pseudo-"signing").

It's even true on Linux. The designs on Linux aren't as robust or decentralised as on macOS or Windows, but packages are signed there too by your distro provider. Although you can install packages from outside the distro and thus work around this system, by doing so you disable any protections against apps stealing cached resources (exception: I think Flatpak doesn't have this problem but it uses a more store-like model where the store operators are expected to stop apps impersonating each other and if you install other "stores" then all bets are off again).

Now all that said. Conveyor (see my other post in this thread) can produce self-signed MSIXs, and those are fully fledged MSIX packages that will be able to take part in sandboxing when it's finally released. Installing such packages requires admin elevation because the installer has to add your self-signed root certificate to the user's trust store, and that's equivalent to overriding the protections (you could then sign an app as if it came from anyone). But it can be useful in corporate environments where the certificate is pre-distributed via Active Directory.


It's still silly to not allow sandboxing of unsigned binaries. There could at least be a user sandbox where all non-signed apps can live. There could be an option for a temporary sandbox. And so on.

There could be many useful options which would still improve security incrementally.


Windows already has that, it's a feature called Windows Sandbox:

https://learn.microsoft.com/en-us/windows/security/applicati...

But this blog post is about App Isolation which is more conventional kernel level sandboxing.


Thanks!


But with MSIX requiring installation, couldn't the cached credentials be tied to the container? I would think that the OS would need to protect the container to some extent, and thus an application that could break into the container would effectively already have filesystem privileges equivalent to administrator access.

It'd be more tolerable if self-signed certificates were allowed without janky workarounds like adding them to the cert store. There are too many significant downsides to commercial code signing certificates for individuals, like cost, loss of anonymity, certificates getting revoked for bogus reasons like antivirus false positives, and the slow creep toward more things requiring EV (like maintaining reputation). The majority of the time, the only actual trust I have in an application and its developer is from past history, and for that all I need is assurance that a new version was signed by the same entity as the last version.


The container identity is derived from the certificate identity in Windows.

Now, you could say that's wrong and it should be derived from the public key instead. That's how Android does it. It means self-signing works. But, Microsoft want the ability to make malware bans stick, and that means tying apps to something expensive to forge yet cheap to produce one or two of, and which can be used to inflict real world consequences for online actions (malware). Real-world identity does that.

Public keys have another problem: people lose them. Google had to give up on letting developers manage their own keys eventually. The OS still understands that, but most apps are distributed through the Play Store and when you do that Google re-signs your software with server side controlled keys. This ties apps to Google accounts instead of private keys, which is much safer for the developer (in availability terms).

MSIX identity has a similar problem - sometimes companies change their names, and sometimes CAs disagree on how a name should be formatted. Windows has a mechanism to let people migrate between names, but it's badly designed and basically unusable. Conveyor provides its own system that's more robust.

> There are too many significant downsides to commercial code signing certificates for individuals, like cost, loss of anonymity, certificates getting revoked for bogus reasons like antivirus false positives, and the slow creep toward more things requiring EV (like maintaining reputation).

Microsoft will sign your software for you if you ship via the Microsoft Store, and charge you a one-off $19 fee. So that's basically ~free. Conveyor can make MSIX packages and upload them to the store for you, and can do so from Linux or macOS, so cost is no longer an obstacle to being signed (although you do have to meet the Store policies).

Certificates don't get revoked for AV false positives. Apple doesn't care what AV vendors think and on Windows certificates are actually never revoked, not even if used unambiguously to sign malware. They're working on changing that at the moment but it's slow going due to the need for precise policies; it will certainly not be so trigger-happy as to be caused by AV false positives.

You can't be anonymous in any software distribution platform meant for end users. Even on the web, you need to buy a domain name and that means tying your name to a payment system that is required by law to verify your government-issued identity. All apparent anonymity on the web is simply having other people stand in your place and agree to take the bullets on your behalf, which can be done with code signing as well.

The exceptions are platforms meant only for software developers (Homebrew, Docker Hub, NPM etc) which I think don't do any ID verification, but they are also centralized and expected to police the packages they ship by their users. Arguably they don't and this is blowing up in the industries face, usually this is called the supply chain crisis. It's possible that code signing will come for libraries at some point too if we don't figure out in-process sandboxing and make it ubiquitous.

> The majority of the time, the only actual trust I have in an application and its developer is from past history

That's exactly what code signing is for: allowing you to develop a trust in the reputation of a developer. But it has to be ignited by something. If you don't have that then you can never install any newly developed software, which is infeasible and so you probably do install software from developers with unknown reputation.

I think a better argument against the way code signing works is that it should accept a domain name as an identity, like the web does. The code PKI system assumes that trust accumulates into individuals or companies, but on the internet trust mostly accumulates through social networks and links, which go to domain names not companies.


> Public keys have another problem: people lose them. Google had to give up on letting developers manage their own keys eventually. The OS still understands that, but most apps are distributed through the Play Store and when you do that Google re-signs your software with server side controlled keys. This ties apps to Google accounts instead of private keys, which is much safer for the developer (in availability terms).

So, to correct the number of mistakes in that:

- Apps distributed through Google Play are only resigned if the developer has uploaded their key to Google Play, or created the app within the last year

- Google added the resigning not to improve availability, but instead to allow Google Play to modify the included libraries and dependencies in the app ad-hoc

- For apps created within the last year, as the developer does not have access to their signing key, they cannot redistribute the app outside of the play store, which was the main goal from Google

- The vast majority of apps installed today, even when installed through the play store, are still signed by the developer


An additional bit of context for why this is so annoying - Microsoft recently switched code signing to physical hardware only, meaning you must buy a server with a HSM, a USB security key, or use a more expensive CA. There's no longer any free CAs for this.


You can use a cloud HSM and some CAs offer cloud signing. We described some info about setting that up with our tool in this blog post (it talks about Electron but the instructions work for any kind of app):

https://www.hydraulic.dev/blog/21-shipping-electron-apps-fro...


So how do I get one of these with my colo'd machines?


If you have physical access to your own server you could just plug in the USB devices CAs sell you. The cloud HSMs/code signing services are more for people who don't have hardware access and have to rely on someone else's HSM accessed over the network.


Search "FIPS hardware security module"


That is indeed the end goal, as revelead last year.

"Modernize your Win32 application for security and privacy" - BUILD 2023

https://www.youtube.com/watch?v=w6VwHGPz12w


>That seems silly.

That is already how it works on Android and iOS so I don't think it is very silly.


Android allows self-signing, though. The biggest use case of Android's signatures right now is to make sure only the packager can serve updates (and gain access to the data stored within the sandbox). You don't even need to provision any developer specific signing certificates to install these apps.

In theory Android could start using certificate verification, of course, but right now that's not being used.


That's true. I hope with proper sandboxing Microsoft will allow self signed certs for such apps. I can understand the risk when an application can access literally everything of the user's, but the level of trust needed for sandboxed apps is less.


So, is that basically app-armor for windows? You define which file/netowrk/process a program can access, while not result in a full container resolution like docker/snap/flat pack/uwp.


This is the evolution of UWP sandboxing into Win32, given that UWP went as we all know.


It's a really cool feature, but be aware that MSIX can be tricky to use in its raw form. My company makes a tool [1] that can produce both signed and self-signed MSIX packages from any OS including Linux or macOS using a much friendlier toolchain than what MS provides. It also produces a small installer EXE that works around the unfortunately large number of bugs in Windows to produce a reliable and easier to use experience.

We're waiting for this feature to emerge from the insiders builds before adding support for sandboxing, but should do so once it's ready.

Released Conveyor currently only supports native, Electron, Flutter and JVM apps. But, we have some unfinished support for shipping Python apps as well. In the tutorial MS sort of hand-wave how to do this, basically saying to repackage the entire Python interpreter and details like pipenvs and native code are left as an exercise for the reader. Real apps do need to think about this of course.

If anyone has a need for shipping sandboxed [Python] apps on Windows please do get in touch. It'd be good to understand how much demand is out there before investing in finishing off these features.

An alternative approach that can also work well is to use software-level sandboxing, either in replacement or in combination with OS level sandboxing. For example GraalPy is a Python interpreter and JIT engine that not only runs Python faster than CPython but which also supports sandboxing with a high level API and easy interop with other languages:

https://www.graalvm.org/python/

For example if you want to expose capabilities to your code, this is a good way to do it that's much easier than with kernel sandboxing (ditto for JavaScript). The cost is that Python compatibility isn't yet 100%, and memory usage can be higher. But if it works for your app it can be a useful weapon in your armory.

Tying the above together, because GraalPy can be turned into a JVM app (including an ahead-of-time compiled JVM app) you can also ship customized sandboxing Python interpreters with Conveyor, including making a server that uses systemd to start up and sandbox, to really lock down the process tightly. So you can get both software and kernel sandboxing on Linux and macOS, for desktop apps and servers, and maybe in future Windows too. Pretty cool stuff.

Disclosure: I own the company that makes Conveyor, and also do part time research work with the Graal team. So I'm talking my own book here, but only because it's a good book.

[1] https://conveyor.hydraulic.dev


Thank you to write this detailed post. It is very helpful.

Do I understand GraalVM correctly: It can run CPython native extensions (C code extensions compiled to run from CPython)? It sounds like you need to recompile the extension for GraalVM, but it should work OK.


Yes it can with caveats.

A lot of packages that use native code work but not all. The Python ecosystem is pretty messy and doesn't care much about alternative impls, so some packages do silly things like hard-code that the interpreter must be named "CPython". Others make extremely deep assumptions about the CPython internals that GraalPy can't meet. There's no equivalent of a standardized interface like JNI in the Python world yet (there's an effort called HPy but it's new). GraalPy comes with a modified pip that knows how to patch popular packages that do bad things to work with it.

Still, GraalPy actually can use native extensions and in theory in future even sandbox them (or maybe even today, I forget the status). Most alternative implemenations were never able to do it at all!

Now if you want to sandbox Python code you must also be able to sandbox arbitrary C/C++. Python is so slow that almost any real program will rely on extensions. The tech stack can do it and it's been done for other languages, because Graal can actually JIT compile and sandbox LLVM bitcode as well. In that case you recompile with a special toolchain and the native code always thinks it's running on Intel Linux. But that has some warmup time impacts and you have limited access to hardware/the OS in that mode obviously, as with any sandboxing. For example PyTorch couldn't be sandboxed in that way. Also I don't know if GraalPy supports sandboxing yet, it might be taking a back seat to other priorities like more ecosystem compatibility.


Is the GraalVM sandboxing only applicable to polyglot runtime or also to JVM itself?


Only the polyglot runtime. But, there's a JVM implemented using polyglot/truffle called Espresso. It doesn't fully support sandboxing yet but it's being worked on. Then you would be able to load Java code and execute it inside a sandbox without the SecurityManager. Ask on their slack if you want to learn more about it.


Is there something more granular than "File system access" or not? I would like for apps to have their own temp/cache and optionally access to common files. Having to do all or nothing significantly curtails the utility.


MSIX already provides that. All apps run in a very lightweight "sandbox" that virtualizes file access to the user's AppData directory. Writes inside it are redirected to a package-private location that's blown away on uninstall. Same for registry writes. It means the OS can cleanly and immediately uninstall such packages.

Temp is also virtualized in the same way, although Conveyor devirtualizes it in some cases due to a Windows kernel bug.

This is just intended for keeping the system clean. Your app can devirtualize locations in AppData by requesting it in package metadata, if you need to modify files belonging other apps for example, see:

https://conveyor.hydraulic.dev/13.1/configs/windows/#virtual...


Huh, interesting, I will have to reconsider MSIX then. It sounds very neat.


It already is granular. The "File system access" setting is unrelated to what this article is talking about. That setting is for the broadFileSystemAccess capability for UWP apps.


this talks about locking down network and filesystem access, but does Win32 App Isolation also offer a way to restrict CPU and memory usage?

I want to be able to run untrusted code in a sandbox such that "while True" won't exhaust my system resources, and with a limit on the amount of memory that the sandboxed program can allocate itself.


App Isolation is a feature layered on top of low level sandbox primitives and IIRC it doesn't support this.

On the other hand, it also wouldn't make sense for App Isolation because that's intended to give apps a lightweight way to opt-in to isolating themselves, in order to restrict the blast radius of exploits. It's not intended to let you run arbitrary third party code sandboxed. For that you would need to use something more like the Chromium sandbox that uses kernel job objects, which a sibling comment describes. Using that would require fluency in Win32.


I'm not an expert on the subject but just happen to know this was possible since XP: https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns...

See JOB_OBJECT_LIMIT_PROCESS_MEMORY and JOB_OBJECT_LIMIT_PROCESS_TIME.

Job is basically Windows equivalent of Linux Control Group.


No, the CPU one isn't the same. It's a cumulative limit (resulting in termination when exceeded). The parent is asking for rate limits (resulting in throttling when exceeded).


It's not really clear from OP that it's about rate. Cumulative limit would still fulfill requirement of stopping while(true) from exhausting resources.

But if you need rate there's this: JOB_OBJECT_ CPU_RATE_CONTROL_MIN_MAX_RATE https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns...


It's not explicitly written, but it's a pretty unavoidable conclusion.

As far as the verbiage goes -- "doesn't exhaust" means "uses less than the full amount available". Given that "total CPU time" isn't something you can run out of, "doesn't exhaust CPU" can only mean "uses less CPU than is currently available", i.e. "utilizes < 100% of the CPU", i.e. throttling.

As a practical matter, pretty much ~no user wants to kill a program after N seconds, and ~no program can limit itself to N seconds of work, either. People just want their CPU to have bandwidth to do other work, avoid heating up, etc. So unless you have a strong reason to believe otherwise, it's fair to assume people want to limit CPU utilization, not total CPU time.


> pretty much ~no user wants to kill a program after N seconds

This is exactly how the web and cloud FaaS like AWS Lambda does it, so I don't think it's right to say nobody wants this. Runaway code does need to be killed.


I didn't say nobody wants this, I said no user wants this. We're not talking web servers, we're talking about Windows desktop apps here... with humans using them. User != admin != owner. "Web and cloud FaaS like AWS Lambda" don't have "users" (i.e. the kind who would say "this shouldn't exhaust my system resources" like the parent did). They're cattle, running batch jobs, and/or serving APIs that other client applications invoke...


Web pages have users (JS has time limited execution and will be killed if the page hangs).


Are you under the impression web pages can access the Win32 API?


(Off topic, but heads up I'm getting NET::ERR_CERT_COMMON_NAME_INVALID at https://simonwillison.net/ and https://www.simonwillison.net is redirecting to 123-reg.co.uk )


Should be fixed now!


What's the best way to accomplish this on Linux? If there is a good mechanism, can you apply it to a currently running process or do you have to pre-define available resources?

The only trick I have up my sleeve is to use earlyoom to abort misbehaving processes if I suddenly squander all of my ram.


I do remember systemd having `CPUQuota` to set an absolute limit, I am not sure if this is only limited to actual units or if this can be applied to slices as well (KDE and GNOME default to running individual desktop apps in their own slice IIRC), but from the quick glance I took on https://www.freedesktop.org/software/systemd/man/latest/syst... I think it should be possible also to do it on a per slice basis.


SandBoxie is a thing, but I don't know if it supports denying read access to existing files. It's more designed to stop writes.


Tangent: The image at the top of this blog post is amazing. Why does the scale feel immense? It is like something from the film _Interstellar_.


Venv for python with a gui ??




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: