Skip to content

Latest commit

 

History

History
208 lines (123 loc) · 23.7 KB

07-31.md

File metadata and controls

208 lines (123 loc) · 23.7 KB

July 31, 2020 Incubator Call Notes

Attendees:

  • Bradley Farias (GoDaddy, @bmeck) BFS
  • Shu-yu Guo (Google) SYG
  • Kris Kowal (Agoric, @kriskowal) KKL
  • Michael Ficarra (F5 Networks) MF
  • Keith Miller (Apple) KM
  • Mark S. Miller (Agoric) MM
  • Chip Morningstar (Agoric) CM
  • Dan Finlay (MetaMask) DJF
  • Erik Marks (MetaMask)
  • Brad Decker (MetaMask)
  • Dean Tribble (Agoric) DT
  • Kevin Gibbons (F5 Networks) KG
  • Daniel Ehrenberg DE
  • Mathieu Hofman MAH
  • Tom Dillon (F5 Networks)
  • Natalie Silvanovich (Google) NSH
  • Peter Hoddie (Moddable) PHE
  • Leo Balter (Salesforce) LEO
  • Richard Gibson (OpenJS Foundation) RGN
  • Yulia Startsev (Mozilla) YSV
  • Chengzhong Wu (Alibaba) CZW

SYG: We have a guest from Project Zero, NSH, who has done talks at previous meetings about security in browsers. Michael Ficarra asked what does the committee consider "security" due to questions about why browser vendors did not see function implementation hiding as a security feature.

SYG: The security model changed with Spectre. It's a side channel where you can get CPU data. We are assuming they're here to stay. We've been trying to mitigate that via process isolation - at Chrome this is site isolation, web standards at large this is new headers like COOP/COEP that let sites opt into enabling timers and so forth (given the Spectre landscape). It's my understanding that what we're trying to do is more about slowing down the attacks - we can't patch all Spectre class attacks, but we can do things like disable access to shared memory that give really high frequency timers that let them do Spectre-style attacks really easily.

BFS: What do you mean by security?

SYG: I'm hoping to talk about that today.

BFS: I don’t want this to be only about browsers since as a language body we don’t only serve browsers. You made an assertion that the security boundary has moved. That assumes there's a notion of security.

MM: I can propose a taxonomy I have been using. For most purposes, we can divide security issues into integrity, confidentiality and availability. I have found this to be a useful taxonomy. Spectre is a confidentiality issue: allows inferring information that they shouldn't be able to infer. The other dimension we haven’t talked about is granularity, about boundaries and which things can interact with each other. There are granularities to boundaries. For example, just because I have a house, doesn’t mean I don't need skin. Just because I have skin, doesn’t mean I don’t need cellular membranes.

NSH: I wanted to interject there. I think those are good categories. When you talk about confidentiality, integrity, the importance of that is broad categories. There could be a bug that could cause any problem. We need to define what security guarantees that JavaScript provides. What do we want to do in regards to the standard and security boundaries. Do we move the boundaries in regards to that. How do we consider implementation? In reality sometimes security should go into the standard, but sometimes are problem is how it is implemented even down to the hardware level. How do we interact with implementation and what are our security standards?

When you talk about confidentiality, integrity, availability, those have an impact, but the importance of those for broad categories isn't that important.

I think the importance of all that is not so important. Spectre and Meltdown is a hardware issue allowing confidentiality breakdowns, but maybe the next issue allows executing code. It's important to define what security guarantees JavaScript provides, and what we want to do with regard to security boundaries. Then there is also the question of, how do we deal with certain problems. Do we move the boundaries in response to them? And finally there's the question of how we consider implementation. Sometimes security needs to go in the standard, e,g., authentication, but most of the time, the problem is how it's implemented, even going down to the hardware level.

And finally, there are security features that could be added, for example crypto.

Those three categories are: What are the security guarantees? How do these interact with implementation Security features (which also make guarantees)

MM: You're right that hardware can also threaten integrity, e.g. RowHammer. That’s a serious threat. I'd acknowledge that but keep it distinct in the taxonomy because it's an attack on integrity rather than confidentiality. With regard to these security properties of JavScript, I'd say that JS has very strong integrity protection right now at multiple granularities.

Within strict code closure encapsulation is absolute. Within classes the encapsulation of private fields is absolute. Proxies do absolute encapsulation of their handler and target. WeakMaps do absolute encapsulation of values for anyone who doesn't have the key.

The JS standard talks about Realms. Only the creator gets initial connectivity to a new realm. Aside from that, realms are completely isolated from each other.

MM: For confidentiality, we need to distinguish between overt, covert, and side channels.

An overt channel is one guaranteed by the language spec.

A non-overt channel is due to spec non-determinism --- degrees of freedom allowed by the spec --- where an observer may infer illicit information by observing what choice an implementation made among those it was allowed. The classic example is timing. Non-overt channels may be further divided into covert and side channels.

If the computation legitimately holding the secret was purposely written to try leaking it over a non-over channel, that’s a covert channel. Otherwise it is a side channel.

The typical covert communication channel is timing. By observing duration, you can infer information you were not supposed to have. JavaScript is very strong on confidentiality with respect to overt communication channels, but not on non-overt communication.

MM: The reason that makes a difference is that when you can constrain sub-computations to executing in a deterministic manner where it cannot sense duration, those sub-computations cannot read non-overt channels and the protection over overt channels is quite strong. This covers only a restricted category of code, which needs no network access or interaction with the user, for example. However, there are a tremendous number of transformation libraries --- pattern matchers, compilers, linear algebra libraries, constraint solvers --- which take in input data, compute a result, and stop. By denying these libraries the ability to sense duration, the remaining worries about non-overt leakage is reduced to a much smaller body of code.

NSH: It's important that we don't get too far into the weeds here. The vast majority of security problems in JavaScript are not subtle. Mostly, something is implemented in a way that causes memory corruption. Then, this means that none of the security guarantees are met.

Maybe a definition to work towards is that if the language behaves in the way of the specification indicates, it is secure. If not, it is a security issue.

MM: There's certainly flawed behaviors, but assuming a correct implementation of the specification: The specification can help or hinder the writing of programs with strong security properties.

I applaud Natalie for focusing on making it easier for implementations to correctly implement the spec.

KM: It seems like, in my experience, a lot of those security bugs that come from memory corruption are due to very complex optimizations, to address the common case, at the expense of uncommon cases. Do you think it would make sense for us to streamline that, to avoid uncommon edge cases which need to be optimized around, creating a whole bunch of machinery to undo it?

NSH: Yes, exactly, for example, things like making a property readonly or less used features like that lead to security problems. Devs try to optimize them and then they go crazy and when the less-used feature get used there's problems. Formally, most bugs happen when there's something in the spec that has side effects that get called in the process of something being implemented. If we could reduce the number of features and things that have side effects that would reduce the number of bugs.

NSH: Specifically reducing the number of side effects would reduce the number of bugs.

KM: Do you mean side effects like, it changes the global state in some way?

NSH: When performing an action has some effect not related to that action. For example, putting an element into an array can call a getter or setter which can call any other part of the VM.

KM: a lot of those are just however the VM implements what the spec says. What are the cases where they tend to get added by implementations because of something in the spec? Those are the most actionable things. How can we avoid adding features to the spec that do lead to implementations having those side effects.

NSH: Here's a better definition: In the process of going through one method, you can call untrusted code written by the user. The fewer times when implementing Array.prototype.slice, the fewer times it runs untrusted code written by the user, the less likely it will lead to security issues.

SYG: As an example, I was working on relaxing Atomics to work on ArrayBuffers. And you have to add a number of detached checks, because any time you coerce something to a Number, it could call arbitrary code, and so it might detach the ArrayBuffer you're working with.

KM: We have a ton of these bugs in the compiler where we model the effects wrong. It’s usually within webcore is the most common place. Where something does something bizarre that overrides code written 30 years ago.

SYG: I think this is a concrete actionable thing: what can we do when we do language design and spec work, to avoid unintended side effects?

BFS: We cannot solve all the implementation complexity without discussing other things. This week in node we were calling user code from our JS hosted runtime.Hosts, or self hosting environments, using the language features, are also at risk for this. We need to differentiate a few things we are alluding to. We do have a focus on the implementation side of security. The statement most of the bugs are from ?? we can’t agree to that until we know what security is. What guarantees is the host able to make by using Javascript ApIs. This is my concern as someone working on Node. We have a third category: ecosystem libraries. If you have survived an npm audit recentantly you get a tone of warnings. The problems are not from memory address reading or implementation fault of the VM. It is important to discuss: if the language itself cannot secure the dangerous parts that are exposed. What are we trying to accomplish by establishing a security model.

We have:

  • Node
  • Deno
  • Cloudflare workers
  • SES

KM: I think there are two ways to see security, and I think it is worth refining that. The end user of whatever the product is. THEIR stuff is secure. The runtime itself is protected. From other code.

I think what you are describing is code being protected from code.

BFS: As a host, node does that. As a runtime it doesn’t.

KM: So what you are saying is that node is able to escape their sandbox and take over the sandbox?

BFS: A malicious dependency, if it was able to get into a project, it could access code to control a page.

MM: Is everyone aware of the event stream incident? According to NPM, the typical application is 97% linked in third party libraries. Only 3% is code specific to the application. This is the issue that we most need to focus on in terms of security. Everything that is linked together is vulnerable to everything else.

Some have suggested you shouldn’t link any code you haven’t audited and have perfect confidence in. This is unrealistic victim-blaming. It is a recommendation known to be impossible to practice, so that the inevitable failure to practice it can then be blamed as the source of the problem. As with the design of any tool, we need to design our language to be more forgiving of realistic user behaviors.

KG: Keith, I broadly agree that the host and runtime have to be protected. The reason we care about the host being compromised is because it leads to users being compromised. If there's a memory corruption in Chrome, that could lead to bad things happening outside of the browser. But, bad things that happen inside of your page with a credit card form are also bad--a different kind of bad, but with user impact.

KM: Like XSS; This is at the host level, but it’s still an XSS effectively.

I wonder whether the solutions will be different because of differences in how they come about, because they still...

Some are like “the VM introduced this by optimizing” vs developers introducing malicious dependencies.

KG: This alludes to Natalie’s taxonomy before: What are the guarantees that the language provides Whether the implementations are able to provide those guarantees

NSH: One thing to start off with is, focusing on what we can control. There are a lot of problems in JavaScript, some we can control from the lang, some we can’t. Things like XSS are mostly outside the control of the JS language; it’s more about how the page + HTML is designed. Likewise, things like npm modules--we can try to give people the tools they need, but there are some things that we can directly address in the language. I don't know how to deal with this, but we might want to prioritize: Some things we can fix in the language now, others would require vast projects. I think we should try and separate those out.

MM: On JS vs XSS There are things we have been doing in the language for over 10 yrs, from Google Caja, and the support in ES5 to enable it (strict mode, …). The danger with code is not that it is executed, but the authority that is granted to that code. So, under SES, the eval and Function constructor evaluate the code in a completely confined manner. You can still deny availability by going into an infinite loop, but you can't threaten integrity or confidentiality over overt channels. If JavaScript permits an initialization like SES, which is being used by MetaMask, Moddable, the XS machine, which only have safe evaluators. SES - one of the ways XSS happens is when the evaluators are reached by navigation. SES is configured such that you can't reach evaluators by navigation. These are things you can do in the language that you can do and that Agoric, SalesForce, Moddable are doing and are now deeply invested in.

BFS: I'm seeing some division on "who is the most popular thing that we need to patch". I don't think this is a useful discussion--who is the most important technology, what problems are the most relevant so far. We're talking about a security model for JavaScript--not what we are currently striving to fix. We keep talking about guarantees, and in particular, the ability to reproduce guarantees. A reproduction either by the implementation of a VM, or the reimplementation of guarantees in JavaScript that is self-hosted somehow. So, I think we should try to go down that route, of trying to guarantee what we can. We should see what the particular avenues of threat are. We haven't discussed who the user is, and I think this is part of the issue: who can reproduce guarantees is, For me, the ability to reproduce the guarantees of the language when running libraries (in Node) For Mark, it's the SES containers' guarantees when running arbitrary user code For implementations, they're trying to reproduce guarantees about how the spec works

MM: I agree with Natalie that having things be effectful is a big danger--not just with implementations, but with writing defensive code, where the code should mean what the programmer thought it meant.

JS since ES5 has had ways of limiting mutability: Object.freeze, hiding objects from primordials, etc. Anything to encourage more things to compute with frozen objects, the better off we are. Many of Natalie’s memory unsafety exploits can’t happen under SES with frozen primordials, because they depend on primordial mutation.

KM: Are you just talking about frozen primordials, or frozen objects too?

MM: Both. Mostly about the second, but also am including the first. Object.freeze is a wonderful primitive. The browser makers in particular screwed up by de-optimizing frozen objects, which causes people to use Object.freeze in development but not in prod to avoid the perf impact. Like turning off seatbelts to carry passengers. Anything we can do to encourage people to use Object.freeze will make applications more robust + defensive.

KM: This could result in engines trying to do optimizations like “I assume no one can observe this here”, and then there will be a bug, the thing will leak, but now you’ll have no control of it as a user. (not sure I got this - notetaker)

MM: I agree, there are risks, but the total state-space the implementer needs to worry about (semantic state space) is much smaller. Many of Natalie’s exploits had to do with finding areas of the state space that had escaped the implementor’s attention.

KM: Today we don’t worry about if they’re frozen or not, and then we’d have to wonder if someone is looking at it or not, and now there’s book-keeping, and book-keeping can have bugs.’

MM: There are two levels: Don’t de-optimize on Freeze. This causes no explosion of state space. Further, realize optimization opportunities enabled by Freeze. But don’t-de-optimize-on-freeze should be uncontroversial.

KM: I mostly agree, except….

YSV: Moving to the next topic. But there is a +1 from Shu regarding Keith’s point on freezing and deoptimizing frozen objects, and also from myself.

Michael Ficarra: My topic is a continuation of Bradley’s. I’d like to explore the security properties that language features have, and see how that interacts with security. Mark mentioned what he considered guarantees provided by diff features (Closure encapsulation, etc). Function implementation hiding helps with something which currently violates that guarantee.

Some of us see some things as guarantees provided by the language, and others don’t. I’m coming from a point where I assume many parties are interacting within the same Realm, and they want to take advantage of these features to provide certain guarantees. Hopefully this is clarified in discussion later.

NSH: outside of discussing specific guarantees I think we need to discuss how we decide what is a guarantee. Instead of deciding what are the guarantees we have a process to decide what the guarantees are and what they provide.

MF: I think we should do a survey of the existing guarantees right now as well.

MM: I don’t know if you know this, but Yulia and I were writing down current language invariants. Many of these security properties fall into that category. Many of the properties that provide security are capturable as invariants, which then provide guides for the language future.

MF: Let's make a security TG, since we have a broad set of topics within security to discuss.

BFS: We need a task group especially if we're going to argue about what existing security guarantees and guarantees of new features of the language are. I don't think we can have one-off sessions for that given the volume of proposals.

MM: There's an SES group which meets 1/week for two hours. Should we declare that the TG?

SES has turned into a set of proposals that are advancing through the committee, but starting with ES5 it was implementable as a library - once the library is loaded, the JS environment then becomes one that conforms to OCAP security constraints in which all primordials are frozen and the shared primordials are effectively free of mutable state and IO, so sharing of the shared primordials does not provide any authority or communication channels.

NSH: I think we should have a different group so it's not focused on that solution.

MF: I'd prefer to do planning for the TG offline. I'm volunteering to coordinate.

YSV: Can you open a reflector thread?

KG: A bit of background from our perspective for how webapps are built. We work at Shape Security, part of F5 networks. We add functionality to network fetches, post, etc. As part of that we see a lot of web applications. The application is usually built by a variety of different teams, that are pulling code from a variety of different places, and sticking it onto the code at the same time. There isn't a central authority controlling even the code on the page, much less how they interact. The way that this works is that JavaScript has a fairly powerful ability in early-run code to control what happens in later-run code. This really isn’t a security guarantee in the classic sense, because early-run code locking-down code that wants to prevent access to the fetch API will not make hard guarantees. You are rarely trying to protect against malicious code, just trying to build an application that's coherent. So later-running code which is oblivious to the earlier-running code will be controlled by the early-running code's restrictions. It's a security property (not a guarantee because it doesn't protect against malicious code). This is how websites are built today: many many websites have code on the page that has early-run code that replaces fetch/XHR or whatever. That code needs to cooperate in this oblivious fashion is an important part of the security model of JavaScript: websites are already built on this assumption.

DJF: I want to tie into an earlier couple of points. There's various levels of security we can strive for and sometimes concerns about host virtualization vulns. A simple definition: security is code that behaves the way you would expect it to especially wrt potentially malicious agents trying to interfere with that behavior. E.g. can you trust a developer to protect you against their own malicious dependencies? If a naive developer does not have tools to protect against a hashing library interfering with how arrays operate it is hard to trust. As an example, not giving access to timing channels is a simple way to protect against timing channel attacks. If one code author is not malicious we can trust them to be able to defend against their dependencies.

SYG: To agree with Keith and Natalie, and I want to push back a bit on Bradley. I think it is helpful to prioritize.

BFS: I think we're trying to prioritize the wrong categories.

SYG: From my point of view, it seems like the actual implementation sandbox and correctness is your first line of defense. If you can own that you get your shell code and you can do whatever you want. On top of that, additional security guarantees require additional complexity. I see that as somewhat in conflict with the practical implication that, the less surface you have, the less chances you have of terrible bugs. Proxies are a concrete example. They allow you to express membrane security but add a lot of additional complexity to implementations. That's a tension that we need to address: if a security guarantee holds given that you have a perfect implementation, that sounds great, but that's a dream, and it depends on the feasibility of what you're asking to be created.

Implementor's reaction is often no, you're asking too much --- they can't hold in practice even if they do in theory because it's too hard to implement securely given incentives like optimizations. I'd like us to prioritize ease of correctness of implementation.

YSV: There are +1s from me and Keith.

BFS: I think we can't limit it to just VM implementations. Exfiltration of cookies for example. Hosts are often powerful - for example node ships with shell access as a JS API. You do execve, we can't limit it to VM implementations.

YSV: We have an action item from Michael to set an issue on the Reflector for the TG. We've come across a number of definitions of security, from Mark, Bradley, Natalie, and Dan.

SYG: Thank you Yulia for helping facilitate. It wasn't as focused as I'd like, but thank you very much for all participating.