Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 1 | # The Rule Of 2 |
| 2 | |
| 3 | When you write code to parse, evaluate, or otherwise handle untrustworthy inputs |
| 4 | from the Internet — which is almost everything we do in a web browser! — we like |
| 5 | to follow a simple rule to make sure it's safe enough to do so. The Rule Of 2 |
| 6 | is: Pick no more than 2 of |
| 7 | |
| 8 | * untrustworthy inputs; |
| 9 | * unsafe implementation language; and |
| 10 | * high privilege. |
| 11 | |
| 12 | ## Why? |
| 13 | |
| 14 | When code that handles untrustworthy inputs at high privilege has bugs, the |
| 15 | resulting vulnerabilities are typically of Critical or High severity. (See our |
| 16 | [Severity Guidelines](severity-guidelines.md).) We'd love to reduce the severity |
| 17 | of such bugs by reducing the amount of damage they can do (lowering their |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 18 | privilege), avoiding the various types of memory corruption bugs (using a safe |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 19 | language), or reducing the likelihood that the input is malicious (asserting the |
| 20 | trustworthiness of the source). |
| 21 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 22 | For the purposes of this document, our main concern is reducing (and hopefully, |
| 23 | ultimately eliminating) bugs that arise due to _memory unsafety_. [A recent |
| 24 | study by Matt Miller from Microsoft |
| 25 | Security](https://github.com/Microsoft/MSRC-Security-Research/blob/master/presentations/2019_02_BlueHatIL/2019_01%20-%20BlueHatIL%20-%20Trends%2C%20challenge%2C%20and%20shifts%20in%20software%20vulnerability%20mitigation.pdf) |
| 26 | states that "~70% of the vulnerabilities addressed through a security update |
| 27 | each year continue to be memory safety issues". A trip through Chromium's bug |
| 28 | tracker will show many, many vulnerabilities whose root cause is memory |
| 29 | unsafety. (For example, [Type=Bug-Security |
| 30 | sanitizer](https://bugs.chromium.org/p/chromium/issues/list?can=1&q=Type%3DBug-Security+sanitizer&colspec=ID+Pri+M+Stars+ReleaseBlock+Component+Status+Owner+Summary+OS+Modified&x=m&y=releaseblock&cells=ids).) |
| 31 | |
| 32 | Security engineers in general, very much including Chrome Security Team, would |
| 33 | like to advance the state of engineering to where memory safety issues are much |
| 34 | more rare. Then, we could focus more attention on the application-semantic |
| 35 | vulnerabilities. 😊 That would be a big improvement. |
| 36 | |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 37 | ## What? |
| 38 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 39 | Some definitions are in order. |
| 40 | |
| 41 | ### Untrustworthy Inputs |
| 42 | |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 43 | _Untrustworthy inputs_ are inputs that |
| 44 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 45 | * have non-trivial grammars; and/or |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 46 | * come from untrustworthy sources. |
| 47 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 48 | If there were an input type so simple that it were straightforward to write a |
| 49 | memory-safe handler for it, we wouldn't need to worry much about where it came |
| 50 | from **for the purposes of memory safety**, because we'd be sure we could handle |
| 51 | it. We would still need to treat the input as untrustworthy after |
| 52 | parsing, of course. |
| 53 | |
Chris Palmer | 42cd401 | 2019-01-26 02:06:07 | [diff] [blame] | 54 | Unfortunately, it is very rare to find a grammar trivial enough that we can |
| 55 | trust ourselves to parse it successfully or fail safely. (But see |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 56 | [Normalization](#Normalization) for a potential example.) Therefore, we do need |
| 57 | to concern ourselves with the provenance of such inputs. |
Chris Palmer | 42cd401 | 2019-01-26 02:06:07 | [diff] [blame] | 58 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 59 | Any arbitrary peer on the Internet is an untrustworthy source, unless we get |
| 60 | some evidence of its trustworthiness (which includes at least [a strong |
| 61 | assertion of the source's |
| 62 | identity](#verifying-the-trustworthiness-of-a-source)). When we can know with |
| 63 | certainty that an input is coming from the same source as the application itself |
| 64 | (e.g. Google in the case of Chrome, or Mozilla in the case of Firefox), and that |
| 65 | the transport is integrity-protected (such as with HTTPS), then it can be |
| 66 | acceptable to parse even complex inputs from that source. It's still ideal, |
| 67 | where feasible, to not have to trust the source — such as by parsing the input |
| 68 | in a sandbox. |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 69 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 70 | ### Unsafe Implementation Languages |
| 71 | |
| 72 | _Unsafe implementation languages_ are languages that lack [memory |
| 73 | safety](https://en.wikipedia.org/wiki/Memory_safety), including at least C, C++, |
| 74 | and assembly language. Memory-safe languages include Go, Rust, Python, Java, |
| 75 | JavaScript, Kotlin, and Swift. (Note that the safe subsets of these languages |
| 76 | are safe by design, but of course implementation quality is a different story.) |
| 77 | |
| 78 | ### High Privilege |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 79 | |
| 80 | _High privilege_ is a relative term. The very highest-privilege programs are the |
| 81 | computer's firmware, the bootloader, the kernel, any hypervisor or virtual |
| 82 | machine monitor, and so on. Below that are processes that run as an OS-level |
| 83 | account representing a person; this includes the Chrome browser process. We |
| 84 | consider such processes to have high privilege. (After all, they can do anything |
| 85 | the person can do, with any and all of the person's valuable data and accounts.) |
| 86 | |
Chris Palmer | 93c230e | 2019-02-12 21:54:49 | [diff] [blame] | 87 | Processes with slightly reduced privilege include (as of February 2019) the GPU |
| 88 | process and (hopefully soon) the network process. These are still pretty |
| 89 | high-privilege processes. We are always looking for ways to reduce their |
| 90 | privilege without breaking them. |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 91 | |
| 92 | Low-privilege processes include sandboxed utility processes and renderer |
| 93 | processes with [Site |
| 94 | Isolation](https://www.chromium.org/Home/chromium-security/site-isolation) (very |
| 95 | good) or [origin |
| 96 | isolation](https://www.chromium.org/administrators/policy-list-3#IsolateOrigins) |
| 97 | (even better). |
| 98 | |
| 99 | ## Solutions To This Puzzle |
| 100 | |
Alex Gaynor | 5697511 | 2019-02-07 19:15:07 | [diff] [blame] | 101 | Chrome Security Team will generally not approve landing a CL or new feature |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 102 | that involves all 3 of untrustworthy inputs, unsafe language, and high |
| 103 | privilege. To solve this problem, you need to get rid of at least 1 of those 3 |
| 104 | things. Here are some ways to do that. |
| 105 | |
| 106 | ### Privilege Reduction |
| 107 | |
| 108 | Also known as [_sandboxing_](https://cs.chromium.org/chromium/src/sandbox/), |
| 109 | privilege reduction means running the code in a process that has had some or |
| 110 | many of its privileges revoked. |
| 111 | |
| 112 | When appropriate, try to handle the inputs in a renderer process that is Site |
| 113 | Isolated to the same site as the inputs come from. Take care to validate the |
| 114 | parsed (processed) inputs in the browser, since the semantics of the data are |
| 115 | not necessarily trustworthy yet. |
| 116 | |
| 117 | Equivalently, you can launch a sandboxed utility process to handle the data, and |
| 118 | return a well-formed response back to the caller in an IPC message. An example |
| 119 | of launching a utility process to parse an untrustworthy input is [Safe |
| 120 | Browsing's ZIP |
| 121 | analyzer](https://cs.chromium.org/chromium/src/chrome/common/safe_browsing/zip_analyzer.h). |
| 122 | |
| 123 | ### Verifying The Trustworthiness Of A Source |
| 124 | |
| 125 | If you can be sure that the input comes from a trustworthy source, it can be OK |
| 126 | to parse/evaluate it at high privilege in an unsafe language. A "trustworthy |
| 127 | source" meets all of these criteria: |
| 128 | |
| 129 | * communication happens via validly-authenticated TLS, HTTPS, or QUIC; |
| 130 | * peer's keys are [pinned in Chrome](https://cs.chromium.org/chromium/src/net/http/transport_security_state_static.json?sq=package:chromium&g=0); and |
| 131 | * peer is operated by a business entity that Chrome should trust (e.g. an [Alphabet](https://abc.xyz) company). |
| 132 | |
Chris Palmer | 3230111 | 2019-02-06 00:02:56 | [diff] [blame] | 133 | ### Normalization {#normalization} |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 134 | |
| 135 | You can 'defang' a potentially-malicious input by transforming it into a |
Chris Palmer | 42cd401 | 2019-01-26 02:06:07 | [diff] [blame] | 136 | _normal_ or _minimal_ form, usually by first transforming it into a format with |
Chris Palmer | f4bff3f | 2019-02-05 19:51:55 | [diff] [blame] | 137 | a simpler grammar. We say that all data, file, and wire formats are defined by a |
| 138 | _grammar_, even if that grammar is implicit or only partially-specified (as is |
| 139 | so often the case). A file format with a particularly simple grammar is |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 140 | [Farbfeld](https://tools.suckless.org/farbfeld/) (the grammar is represented in |
| 141 | the table at the top). |
| 142 | |
| 143 | It's rare to find such a simple grammar for input formats, however. |
Chris Palmer | 42cd401 | 2019-01-26 02:06:07 | [diff] [blame] | 144 | |
| 145 | For example, consider the PNG image format, which is complex and whose [C |
| 146 | implementation has suffered from memory corruption bugs in the |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 147 | past](https://www.cvedetails.com/vulnerability-list/vendor_id-7294/Libpng.html). |
Chris Palmer | 42cd401 | 2019-01-26 02:06:07 | [diff] [blame] | 148 | An attacker could craft a malicious PNG to trigger such a bug. But if you |
| 149 | transform the image into a format that doesn't have PNG's complexity (in a |
| 150 | low-privilege process, of course), the malicious nature of the PNG 'should' be |
| 151 | eliminated and then safe for parsing at a higher privilege level. Even if the |
| 152 | attacker manages to compromise the low-privilege process with a malicious PNG, |
| 153 | the high-privilege process will only parse the compromised process' output with |
| 154 | a simple, plausibly-safe parser. If that parse is successful, the |
| 155 | higher-privilege process can then optionally further transform it into a |
| 156 | normalized, minimal form (such as to save space). Otherwise, the parse can fail |
| 157 | safely, without memory corruption. |
| 158 | |
| 159 | The trick of this technique lies in finding a sufficiently-trivial grammar, and |
| 160 | committing to its limitations. |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 161 | |
Chris Palmer | f4bff3f | 2019-02-05 19:51:55 | [diff] [blame] | 162 | Another good approach is to define a Mojo message type for the information you |
| 163 | want, extract that information from a complex input object in a sandboxed |
| 164 | process, and then send the information to a higher-privileged process in a Mojo |
| 165 | message using the message type. That way, the higher-privileged process need |
| 166 | only process objects adhering to a well-defined, generally low-complexity |
| 167 | grammar. This is a big part of why [we like for Mojo messages to use structured |
| 168 | types](mojo.md#Use-structured-types). |
| 169 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 170 | For example, it would be safe enough to convert a PNG to an SkBitmap in a |
| 171 | sandboxed process, and then send the `SkBitmap` to a higher-privileged process |
| 172 | via IPC. Although there may be bugs in the IPC message deserialization code |
| 173 | and/or in Skia's `SkBitmap` handling code, we consider this safe enough for a |
| 174 | few reasons: |
| 175 | |
| 176 | * we must accept the risk of bugs in Mojo deserialization; but thankfully |
| 177 | * Mojo deserialization is very amenable to fuzzing; |
| 178 | * it's a big improvement to scope bugs to smaller areas, like deserialization |
| 179 | functions and very simple classes like `SkBitmap` and `SkPixmap`; and |
| 180 | * ultimately this process results in parsing significantly simpler grammars (PNG |
| 181 | → Mojo + `SkBitmap` in this case). |
| 182 | |
| 183 | > (We have to accept the risk of memory safety bugs in Mojo deserialization |
| 184 | > because C++'s high performance is crucial in such a throughput- and |
| 185 | > latency-sensitive area. If we could change this code to be both in a safer |
| 186 | > language and still have such high performance, that'd be ideal. But that's |
| 187 | > unlikely to happen soon.) |
| 188 | |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 189 | ### Safe Languages |
| 190 | |
| 191 | Where possible, it's great to use a memory-safe language. Of the currently |
| 192 | approved set of implementation languages in Chromium, the most likely candidates |
| 193 | are Java (on Android only) and JavaScript (although we don't currently use it in |
| 194 | high-privilege processes like the browser). One can imagine Swift on iOS or |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 195 | Kotlin on Android, too, although they are not currently used in Chromium. (Some |
| 196 | of us on Security Team aspire to get more of Chromium in safer languages, but |
| 197 | that's a long-term, heavy lift.) |
| 198 | |
| 199 | For an example of image processing, we have the pure-Java class |
| 200 | [BaseGifImage](https://cs.chromium.org/chromium/src/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java?rcl=27febd503d1bab047d73df26db83184fff8d6620&l=27). |
| 201 | On Android, where we can use Java and also face a particularly high cost for |
| 202 | creating new processes (necessary for sandboxing), using Java to decode tricky |
| 203 | formats can be a great approach. We do a similar thing with the pure-Java |
| 204 | [JsonSanitizer](https://cs.chromium.org/chromium/src/services/data_decoder/public/cpp/android/java/src/org/chromium/services/data_decoder/JsonSanitizer.java), |
| 205 | to 'vet' incoming JSON in a memory-safe way before passing the input to the C++ |
| 206 | JSON implementation. |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 207 | |
| 208 | ## Existing Code That Violates The Rule |
| 209 | |
Chris Palmer | 8070803 | 2019-03-06 20:21:28 | [diff] [blame^] | 210 | We still have a lot of code that violates this rule. For example, until very |
| 211 | recently, all of the network stack was in the browser process, and its whole job |
| 212 | is to parse complex and untrustworthy inputs (TLS, QUIC, HTTP, DNS, X.509, and |
| 213 | more). This dangerous combination is why bugs in that area of code are often of |
| 214 | Critical severity: |
Chris Palmer | aef94dd | 2019-01-18 00:34:15 | [diff] [blame] | 215 | |
| 216 | * [OOB Write in `QuicStreamSequencerBuffer::OnStreamData`](https://bugs.chromium.org/p/chromium/issues/detail?id=778505) |
| 217 | * [Stack Buffer Overflow in `QuicClientPromisedInfo::OnPromiseHeaders`](https://bugs.chromium.org/p/chromium/issues/detail?id=777728) |
| 218 | |
| 219 | We now have the network stack in its own dedicated process, and have begun the |
| 220 | process of reducing that process' privilege. ([macOS |
| 221 | bug](https://bugs.chromium.org/p/chromium/issues/detail?id=915910), [Windows |
| 222 | bug](https://bugs.chromium.org/p/chromium/issues/detail?id=841001)) |