6 ms·
I’ve always liked the concept of a localhost’d web app talking back to a localhost web server. It seems like a great way to get the cross-platform ease of use o
by zaroth 8y ago
I’ve always liked the concept of a localhost’d web app talking back to a localhost web server. It seems like a great way to get the cross-platform ease of use of developing the UI without having to do everything in browser, so you can optimize the heavy lifting and don’t end up with an Electron app pulling 8Gb of RAM and 100% Of 16 cores.
But I could never quite satisfy the nagging feeling that the localhost server could adequately be secured against outside network requests being routed to it, or as TFA mentions, inside network requests being routed away from it to an outsider!
This article helped enumerate some of the difficulties of securing such a service. Things like a memory-safe parser, checking origins, etc.
I wonder is there a definitive guide someone had setup, or even better a sample Golang or similar localhost server, which demonstrates the dozen-odd layers of checks and protections and magical incantations necessary to have such a server “secure” in the sense that a localhost UI is able to make requests to it to receive sensitive data but it should be safe from external attackers trying to spoof the same requests?
- zerkten 8y agoWhen I was working on e-detailing apps for pharmaceutical sales reps in 2007-2010 we did this. Originally the UI for was based on Flash and intended for Windows Tablet PC usage. I think the server security was pretty rudimentary.
- rcfox 8y ago> But I could never quite satisfy the nagging feeling that the localhost server could adequately be secured against outside network requests being routed to it, or as TFA mentions, inside network requests being routed away from it to an outsider! Wouldn't that be solved by binding your listener to 127.0.0.1 (as opposed to 0.0.0.0 or your actual IP)? Sending a request to 127.0.0.1 shouldn't be able to send anything to the network.
- zaroth 8y agoBut can’t any JavaScript in the browser can call back to 127.0.0.1 (within the bounds of cross-origin policy). So you have to check Origin/Referer at least I think. Can you be sure Origin/Referer isn’t spoofed? Then there’s DNS rebinding which can get around the SOP. What about maybe strange ways that malformed requests could end up being reflected back to 127.0.0.1 through rebinding? So it just seems to me that even binding to 127.0.0.1 you have to be ready for just about anything to come in on the socket.
- mholt 8y ago> Can you be sure Origin/Referer isn’t spoofed? As far as I know, browsers forbid modifying these headers, (especially?) when making cross-origin requests.
- laumars 8y agoThe short answer is it depends on your firewall rules. The long answer is if the sender configured their routing to point localhost to your host then your service would still connect and route traffic back to the foreign address. But this type of attack can easily be firewalled against. There is also the potential problem of reverse proxies. However that requires local machine access anyway.
- mholt 8y ago> I’ve always liked the concept of a localhost’d web app talking back to a localhost web server. We're doing exactly this prime-time with Relica: https://relicabackup.com https://relicabackup.com (sorry, not much on the landing page yet, but we have emailed out some info about the UI already [1]). That technique will allow us to distribute backup software that works the same for macOS, Linux, BSD, and Windows, right away; screenshot: [2]. And it's very lightweight. An added benefit of this approach: we were able to take its REST API and, after writing a small custom Go package, we instantly gained an elegant CLI [3] so it has a headless mode too! With all the functionality of the GUI [4]. I haven't seen much consumer software that does this, and I'm not sure why, so I feel like we're taking a bit of a risk, but I think (hope) it will pay off; the benefits have already started becoming clear and they're definitely appealing. [1]: https://mailchi.mp/2b5e7f57e400/a-brief-introduction-to-relica https://mailchi.mp/2b5e7f57e400/a-brief-introduction-to-reli... [2]: https://twitter.com/relicabackup/status/1005105584260067329 https://twitter.com/relicabackup/status/1005105584260067329 [3]: https://twitter.com/relicabackup/status/1006204516344086528 https://twitter.com/relicabackup/status/1006204516344086528 [4]: https://twitter.com/relicabackup/status/1006206423821254656 https://twitter.com/relicabackup/status/1006206423821254656
- zaroth 8y agoThank you, yes, I think architecturally there are great advantages to splitting up an app like a client/server even when designed primarily to be accessed over localhost. Obviously the “server” API is extremely sensitive and I think you have to assume it is effectively exposed to the outside world, even with a 127.0.0.1 binding and a firewall. I guess if you make localhost users literally login and establish a session then you could consider yourself safe. But it’s a weird experience logging into a local application. So whatever you are doing to authenticate the request as local, it has to be unspoofable. I just don’t think I trust the HTTP headers enough!
- mholt 8y agoYeah, we don't trust just anything that comes in on that socket. For example, we implement standard CSRF mitigations like checking Origin/Referer headers. We also don't use DNS at all in the local frontend/backend interactions and require the Origin to be exactly "127.0.0.1" (or the IPv6 equivalent) which is what we bind to. (Edit: I just looked it up again and I'm 99% sure that web pages can't override the Origin header, especially when making cross-origin requests.)
- floatingatoll 8y agoThis is how, for example, Beats Updater and 1Password browser extensions work on macOS, as I understand it.
- icebraining 8y agogit-annex, which uses a web server on localhost for the Assistant UI, uses this process: - generate a random token - start the server configured to only accept requests that contain that token - generate an HTML file that redirects to a local address passing that token, with read permissions only for the current user - run the browser with that file as argument In theory, this should prevent external requests, as well as from other users in the same machine.
- sligor 8y agoHTTP over Unix domain socket (owned by the user) would simplify all this. Sadly, browsers don't support this... :(
- tootie 8y agoIf browser and server are on the same machine, you remove a whole host of the barriers to identification. You could use any sort of local knowledge like system files or your NIC as identification. NB: I haven't thought this through, but I'm sure there's something to it :)
- jclulow 8y agoOr a system call to identify the process or user on the remote end of a connection; e.g., http://illumos.org/man/3C/getpeerucred http://illumos.org/man/3C/getpeerucred
- adamfisk 8y agoYup that’s the type of thing we do with Lantern. In particular we: 1) Bind to a random port 2) Require a securely random base path for all requests. Anything without that path is rejected. The backend just opens the browser at the random port and base path.
- eximius 8y agoIt would be neat if you could listen to a unix domain socket and have a simple way to specify that as an endpoint for an http URI. i.e., http://[uds/path/to/uds.socket] http://[uds/path/to/uds.socket] I reuse ipv6's current syntax here just for the example. A lot of bikeshedding would be needed. It would also be ugly to expose those kinds of internals to an end user, so you'd need some additional technology on top of this to look pretty.
- inopinatus 8y agoThere is an unofficial unix: URI scheme. Browsers don't support it but system clients sometimes do, for example nginx when configuring upstream groups as a reverse proxy[1]. [1] http://nginx.org/en/docs/http/ngx_http_upstream_module.html http://nginx.org/en/docs/http/ngx_http_upstream_module.html
- orf 8y agoThis is how the Dell system detect utility worked. Back in the day I found out that they only checked if the referring domain ended with dell.com, so 'notdell.com' passed their validation[1]. Instant easy unstoppable root RCE on a lot of dell machines from any website (it was a get request as well if I remember correctly). No built in auto update, no system tray icon, no idea if it's running. Good times. I found something similar with HP as well[2]. Since then I'm ok with not having this functionality used and abused too much. There is too much scope for things to go wrong, and badly so. 1. https://tomforb.es/dell-system-detect-rce-vulnerability https://tomforb.es/dell-system-detect-rce-vulnerability 2. https://tomforb.es/hp-support-solutions-framework-security-issue https://tomforb.es/hp-support-solutions-framework-security-i...
- deleted 8y ago[deleted]
- deleted 8y ago[deleted]
- int0x80 8y agoEdit: I'm happy to be wrong. It seems the cross-domain checks dont allow this. I missunderstood stuff on the article/thread. I want to say a few things regarding "insecurtity" of localhost. I find it completely insane and a huge overlook that the downloaded js can access your localhost PRIVATE services. It should be able to access external public services sans cross-origin etc policies. But the browser should not allow access to your localhost services by default, never. They are assumed to be private services. Everyone uses them as such, taking advantage of the (lack of) security implications. If you download a program, and install it, you are open to the same problems. But, there is a huge difference between installing something and clicking a random link on the internet. Local services should be treated the same as files in your computer. Block everything unless explicitely allowed by the user.
- zaarn 8y agoI usually write a special middleware in golang for this. It sits in front of the actual router and http handlers and also buffers the response so it can modify it if necessary (this is unrelated to localhost security). Off the top of my head the checks are basically: 1. Who is the remote IP? 2. What does the origin and refer header say? 3. What does it say in the host? (I always enforce a whitelist for localhost applications) 4. Analyze the other headers? Anything out of the ordinary? 5. Enforce a strict CORS policy on ALL requests, no exceptions 6. Minimize contact points if external APIs are called (separate routers, preferably on a separate port) 7. Enforce a strict CSP policy (self only, no insecure eval or anything) on ALL requests 8. If outgoing requests are required, write a portal that they must go through (ie a common http client instance) that enforces where the requests are allowed to go)
- btym 8y agoThe two could use mutual TLS with baked certs
- logicalshift 8y agoI've been building an animation tool that provides a UI over http (https://github.com/logicalshift/flowbetween https://github.com/logicalshift/flowbetween) so it has a lot of these problems. For running as a local app, what I really want is to be able to run the server as a UNIX-domain socket. (Well, I can do that fairly easily but what I really want is for browsers to be able to connect to one of these). For a single-user app the main issue is that it could be running on a multi-user system so there's the possibility of contention for ports and so on, as well as the need to verify that the right person is connecting - while it's possible to just bind the server to the loopback address anybody on the same system can access it there so it's not necessarily secure enough. For localhost verification, accessing/setting some information from a file URL might work. With the loopback address, encryption doesn't seem to matter too much: anybody capable of intercepting traffic between a piece of software and the browser will also be in a position to just directly read what you're typing. Possibly by looking over your shoulder. However, one of the reasons I want a HTTP UI is to make it possible to use something like an iPad as an input device and there are definitely issues there when the service is something that's randomly stood up and torn down and usually running on a local network rather than the internet: in particular TLS really expects a centralized service so it seems anything other than a self-signed certificate isn't going to work and that comes with a bunch of scary messages for the user. The other issues of authentication all seem to be much the same as for any other web app, though it seems to me that it's possible to streamline it a bit as it'll be quite common for a user already authenticated on one device only to need to prove that they're the same user on another.
- homakov 8y agoI've done exactly that https://github.com/fairlayer/fair https://github.com/fairlayer/fair Single auth_code shared between web session and local app is all you need.