Sandboxing Python with Win32 App Isolation
blogs.windows.com
blogs.windows.com
"Modernize your Win32 application for security and privacy" - BUILD 2023
That is already how it works on Android and iOS so I don't think it is very silly.
In theory Android could start using certificate verification, of course, but right now that's not being used.
https://www.hydraulic.dev/blog/21-shipping-electron-apps-fro...
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.
There could be many useful options which would still improve security incrementally.
https://learn.microsoft.com/en-us/windows/security/applicati...
But this blog post is about App Isolation which is more conventional kernel level sandboxing.
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.
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.
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
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.
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.
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.
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...
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.
The only trick I have up my sleeve is to use earlyoom to abort misbehaving processes if I suddenly squander all of my ram.
See JOB_OBJECT_LIMIT_PROCESS_MEMORY and JOB_OBJECT_LIMIT_PROCESS_TIME.
Job is basically Windows equivalent of Linux Control Group.
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...
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.
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.
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.