FirefoxOS/New security model: Difference between revisions

Adjust format.
(csp section done)
(Adjust format.)
 
(35 intermediate revisions by 5 users not shown)
Line 12: Line 12:




==New Security Model - Meta Bugzilla List==
==New Security Model==
 
== Implementation ==
New security model {{Bug |1149545}}
<bugzilla>
  {
    "blocks": 1149545,
    "resolution": "---",
    "include_fields": "id, priority, summary, status, assigned_to",
    "order": "bug_id "
  }
</bugzilla>


=== Signing - {{Bug|1153420}}===
=== Signing - {{Bug|1153420}}===
Line 37: Line 26:


♦ '''Issue:''' Decide on exact signature format. Should we require that the  signature-files live at the start of the package. That way we'd always  have the signature available before the file contents covered by the  signature.
♦ '''Issue:''' Decide on exact signature format. Should we require that the  signature-files live at the start of the package. That way we'd always  have the signature available before the file contents covered by the  signature.
<bugzilla>
  {
    "blocks": 1153420,
    "resolution": "---",
    "include_fields": "id, priority, summary, status, assigned_to,resolution",
    "order": "bug_id "
  }
</bugzilla>


=== Verifying signatures - {{Bug|1153422}} ===
=== Verifying signatures - {{Bug|1153422}} ===
Line 59: Line 39:


Another thing that needs to be done before any content is served by the network layer is to look in the manifest and populate the nsIPermissionManager database with any permissions enumerated in the manifest. After having checked that the manifest properly matches the signature of course.
Another thing that needs to be done before any content is served by the network layer is to look in the manifest and populate the nsIPermissionManager database with any permissions enumerated in the manifest. After having checked that the manifest properly matches the signature of course.
<bugzilla>
  {
    "blocks": 1153422,
    "include_fields": "id, priority, summary, status, assigned_to,resolution",
    "order": "bug_id"
  }
</bugzilla>


=== CSP - {{Bug|1153423}} ===
=== CSP - {{Bug|1153423}} ===
We need to make sure that it can't load scripts from outside of the signed package. And we need to make sure that it can't use inline scripts.
We need to make sure that it can't load scripts from outside of the signed package. And we need to make sure that it can't use inline scripts.


The plan is to use the CSP code to accomplish this. We can mainly leverage existing code which enables applying a default CSP policy to certain content. We'll use this to apply a default CSP to all signed content similarly to how we currently apply a default CSP to all privileged apps.
The plan is to use the CSP code to accomplish this. We can mainly leverage existing code which enables applying a default CSP policy to certain content. We'll use this to apply a default CSP to all signed content similarly to how we currently apply a default CSP to all privileged apps.
Line 82: Line 52:
Hopefully this is a restriction we can eventually relax, for example by allowing pages in a signed package to opt in to being iframe-able. But this will require out-of-process <iframe>s and so will have to wait.
Hopefully this is a restriction we can eventually relax, for example by allowing pages in a signed package to opt in to being iframe-able. But this will require out-of-process <iframe>s and so will have to wait.


<bugzilla>
=== Process isolation - {{Bug|1153428}} ===
  {
    "blocks": 1153423,
    "include_fields": "id, priority, summary, status, assigned_to,resolution",
    "order": "bug_id"
  }
</bugzilla>
 
=== Process isolation ===
** [META] Tracking bug for Process Isolation implementation of New Security Model {{Bug|1153428}}
 
Bug XXX - Prevent signed packages from being framed cross-origin (x-frame-options )


In order to ensure that only signed content can access the APIs that it has been signed for, we want to always use separate child processes to run such content.
In order to ensure that only signed content can access the APIs that it has been signed for, we want to always use separate child processes to run such content.
Bug XXX- Switch privileged process when loading a page from a signed package


This means that when a user navigates from an unsigned page to a signed page, that we need to switch which process render the pages. Right now this can only be done by creating a new <iframe mozbrowser>.
This means that when a user navigates from an unsigned page to a signed page, that we need to switch which process render the pages. Right now this can only be done by creating a new <iframe mozbrowser>.
Line 107: Line 64:
We also need to change security checks that currently are done in the parent process. Currently many of them are heavily based on app-ids and installed apps. This may need to be changed.
We also need to change security checks that currently are done in the parent process. Currently many of them are heavily based on app-ids and installed apps. This may need to be changed.


Bug XXX - Fix security checks so that they work without appid by tagging a child process with a package identifier.
=== Installing and updating - {{Bug|1153432}} ===
 
 
♦ '''Issue:''' We need to figure out if changes are needed to the security checks of sensitive APIs.
 
 
=== Installing and updating ===
** [META] Tracking bug for Installing and Updating implementation of New Security Model {{Bug|1153432}}


Issue: How can we register web activities when we pin a page. And update those registries when the pinned manifest is updated.
Issue: How can we register web activities when we pin a page. And update those registries when the pinned manifest is updated.
Line 120: Line 70:


Signed packages follow normal http semantics. I.e. if the package still exists in our http cache when the user revisits a signed page, but the cache headers indicate that the content needs to be updated, we do a normal GET request to see if a new version needs to be downloaded.
Signed packages follow normal http semantics. I.e. if the package still exists in our http cache when the user revisits a signed page, but the cache headers indicate that the content needs to be updated, we do a normal GET request to see if a new version needs to be downloaded.
Bug XXX - make sure http cache follows normal HTTP semantics for packages
Bug XXX - implement cache-pinning of packages
Bug XXX- connect gaia pinning with gecko cache-pinning for packages


If a new version of the package is being sent, we follow the same behavior as when visiting a package for the first time. I.e. we need to reverify signatures as well as update any permissions in the nsIPermissionManager database.
If a new version of the package is being sent, we follow the same behavior as when visiting a package for the first time. I.e. we need to reverify signatures as well as update any permissions in the nsIPermissionManager database.
Line 132: Line 78:


Gecko can then use the diff to patch the existing package.
Gecko can then use the diff to patch the existing package.
Bug XXX - support differential package updates (not 2.5)


Note that sending a diff is entirely the server's choice. If the server doesn't support this newly created diff mechanism, then it will simply serve a full package. Likewise if the user is on a very old version which the server doesn't have a diff for or if the diff has bigger size than the resulting package, the server can simply serve a full package.
Note that sending a diff is entirely the server's choice. If the server doesn't support this newly created diff mechanism, then it will simply serve a full package. Likewise if the user is on a very old version which the server doesn't have a diff for or if the diff has bigger size than the resulting package, the server can simply serve a full package.
Line 143: Line 87:
Installing a signed package mainly consists of pinning it in the http cache such that it doesn't get evicted. We still need to check for updates according to normal "app update" scheduling.
Installing a signed package mainly consists of pinning it in the http cache such that it doesn't get evicted. We still need to check for updates according to normal "app update" scheduling.


Bug XXX - implement updating of pinned packages
=== Service Workers - {{Bug|1153433}} ===
 
Bug XXX - handle case where developer has removed the package from their server
 
=== Service Workers ===
** META] Tracking bug for Service Workers implementation of New Security Model {{Bug|1153433}}


One of the central pieces of the new Gaia architecture is the use of service workers. This isn't just to support offline for gaia apps, but also to support dynamic generation of page markup, and the ability to run logic in order to decide what resource to return for a given URL.
One of the central pieces of the new Gaia architecture is the use of service workers. This isn't just to support offline for gaia apps, but also to support dynamic generation of page markup, and the ability to run logic in order to decide what resource to return for a given URL.


In order to make service workers work with the package update logic we should couple package update with service worker update. When the ServiceWorker spec require the browser to check for updates of, or download updates of, the ServiceWorker script, we instead update the full signed package.
In order to make service workers work with the package update logic we should couple package update with service worker update. When the ServiceWorker spec require the browser to check for updates of, or download updates of, the ServiceWorker script, we instead update the full signed package.
Bug XXX- check for full package update when doing service work update


This means that both when we do an "automatic" ServiceWorker update check, such as when the user visit a page which uses the ServiceWorker, and when the ServiceWorkerRegistration.update() function is called, that we update the full package rather than just the ServiceWorker script.
This means that both when we do an "automatic" ServiceWorker update check, such as when the user visit a page which uses the ServiceWorker, and when the ServiceWorkerRegistration.update() function is called, that we update the full package rather than just the ServiceWorker script.


Once a new package has been downloaded, we go through the normal ServiceWorker update cycle. I.e. Gecko fire both "install" and "activate" events on the ServiceWorker. This will happen any time that a package is updated, even if the contents of the ServiceWorker script hasn't changed.
Once a new package has been downloaded, we go through the normal ServiceWorker update cycle. I.e. Gecko fire both "install" and "activate" events on the ServiceWorker. This will happen any time that a package is updated, even if the contents of the ServiceWorker script hasn't changed.
Bug XXX - fire install and activate events on serviceworker


Gecko need to still serve the previous package content until the "activate" event for the new ServiceWorker version fires. I.e. until the new version has been installed, the old version of the package needs to be served for any network requests.
Gecko need to still serve the previous package content until the "activate" event for the new ServiceWorker version fires. I.e. until the new version has been installed, the old version of the package needs to be served for any network requests.
Bug XXX- preserve previous package until activate event


♦ '''Issue:''' How do we enable the newly installing serviceworker to load content from the new package version, even though the previous package version is the one pinned in the cache.
♦ '''Issue:''' How do we enable the newly installing serviceworker to load content from the new package version, even though the previous package version is the one pinned in the cache.
Line 170: Line 103:
♦ '''Issue:''' Does CSP allow putting limits on where serviceworkers can be loaded from? We need to restrict ServiceWorkers scopes as well as script-urls to be from inside the package.
♦ '''Issue:''' Does CSP allow putting limits on where serviceworkers can be loaded from? We need to restrict ServiceWorkers scopes as well as script-urls to be from inside the package.


=== Origins and cookie jars - {{Bug|1153435}} ===
The biggest change here is that we should stop always using different cookie jars for different apps. In particular normal unsigned content should always use the same cookie jar no matter where it was loaded.


=== Origins and cookie jars ===
However signed packages will get their own cookies and IndexedDB data. Content inside a signed package will not share cookies, IndexedDB data, etc with unsigned content from the same domain. It will also not share data with content from other signed packages from the same domain. This is to ensure that unsigned content from the same domain can't read for example sensitive data that the signed content has cached in IndexedDB. And to prevent unsigned content from writing into the localStorage that signed content uses and thereby tricking the signed content into performing unintended actions.


** [META] Tracking bug for Origins and Cookie Jars implementation of New Security Model {{Bug|1153435}}
However when pages from inside a signed package makes network requests to other websites, it should still use the normal cookies from those websites. And if a page from a signed package creates an <iframe> containing an unsigned website, then that website will be loaded with its normal cookies and will have access to its normal IndexedDB data.


The biggest change here is that we should stop always using different cookie jars for different apps. In particular normal unsigned content should always use the same cookie jar no matter which app it belongs to.
In other words, each signed package acts like a separate website. They do not act like a separate "world"/"context".


Bug XXX - define app identifier for use in origin attributes
The way that we will implement this is by generalizing the current <tt>appId</tt> and <tt>isInBrowserElement</tt> mechanism. We will introduce a <tt>OriginAttributes</tt> struct which will hold the "cookie jar" that is used for a given web page. We can then write policies for which parts of this struct is inherited into iframes, and which parts do not. The nsIPrincipal interface will contain one of these structs. We will also have functions for serializing this struct to a string, and for parsing such a string back into a struct.


However signed packages will get their own cookie jars. So a signed package will not share cookies, IndexedDB data, etc with unsigned content from the same domain. It will also not share data with other signed packages from the same domain. This is to ensure that unsigned content from the same domain can't read for example sensitive data that the signed content has cached in IndexedDB.
Most code will treat this OriginAttributes struct as an opaque value. When we store data we store, as part of the key, the serialization of the OriginAttributes.


♦ '''Issue:''' Do requests from a signed package to unsigned content use the package's cookie jar? Or the normal cookie jar. I.e. if a signed package does an XHR request to a normal website, does that use the website's cookies?
Two pages will only be considered same-origin if they have the same scheme+host+port, but also if all of the values inside the OriginAttributes of their nsIPrincipal have the exact same values.


'''Issue:''' Figure out how to give signed content its own cookie jar. One potential solution here is to remove our close tie between cookie jar and appid. Another possible solution would be to make the various APIs use the full package path instead of the domain as key.
We will then add a 'signedPkg' member to OriginAttributes. When a page is loaded from inside a signed package, we will read a package-identifier from inside the package and set it in the OriginAttributes of the nsIPrincipal of the page.


However when we are loading the package itself, we don't use the cookie jar of the signed app. Instead we use the cookie jar of the unsigned content for the origin which the package lives un. We have to do it this way since when we fetch a signed package the first time, we don't know that the package is signed, and so we use the normal cookie jar for that domain.
However, when we do the network request for the package itself, this is treated like other network requests to the webserver. I.e. the network request is considered as an unsigned request and so the normal cookies of the webserver is sent. We have to do it this way since when we fetch a signed package the first time, we don't know that the package is signed, and so we use the normal cookie jar for that domain.


♦ '''Issue:''' What happens if unsigned content does an XHR request to a URL inside a signed package. There doesn't seem to be any security issues involved in allowing that.
The effect of this is that when we are making network requests, these are never affected by package signing. As mentioned above, requests for the package itself are not affected, and requests for pages inside the package don't send any cookies at all since they are simply loaded from the package.


♦ '''Issue:''' Does this mean that the *cookies* used for signed content is the same as the cookies used for unsigned content? I.e. that only IDB/localStorage/permissions are separate for signed content. That seems to be the case if network requests to normal websites from signed content uses the normal cookie jar. What does document.cookies return? Should we make it return null?
However, when a page from a signed package access the document.cookies API, the cookies returned *do* use the full OriginAttributes. I.e. the document.cookies API is treated like other storage APIs like IndexedDB and localStorage.


♦ '''Issue:''' One potential solution here is to do security checks in the parent only to protect the storage data and permissions of the signed content. And make sure to flag the principals used for documents loaded from signed packages as belonging to the appropriate package. But for anything related to network, just treat signed content like normal content belonging to the normal cookie jar.
♦ '''Issue:''' Verify with Honza that this is is doable and not complex. It should hopefully be the most natural way to write the cookie code.
 
If any page does a XHR request to a URL inside a signed package this is treated like any other network request and is permitted. This doesn't expose any user-private information and simply returns content that resides on the server.
 
== Implementation ==
Search for all the open nsec bugs: http://mzl.la/1SLAWum
 
* P1: Milestone 1 (Sept 4)
* P2: Milestone 2 (Oct 2)
* P3: Feature complete (Nov 2)
* P4: Post-2.5 release
 
=== Signing - {{Bug|1153420}}===
<bugzilla>
  {
    "blocks": 1153420,
    "resolution": "---",
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id "
  }
</bugzilla>
 
=== Verifying signatures - {{Bug|1153422}} ===
<bugzilla>
  {
    "blocks": 1153422,
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>
 
=== CSP - {{Bug|1153423}} ===
 
<bugzilla>
  {
    "blocks": 1153423,
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>
 
=== Process isolation - {{Bug|1153428}} ===
 
<bugzilla>
  {
    "blocks": 1153428,
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>
 
=== Installing and updating - {{Bug|1153432}} ===
 
<bugzilla>
  {
    "blocks": 1153432,
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>
 
=== Service Workers - {{Bug|1153433}} ===
 
<bugzilla>
  {
    "blocks": 1153433,
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>
 
=== Origins and cookie jars - {{Bug|nsec-origins}} ===
 
<bugzilla>
  {
    "blocks": "1153435,1163254,1179985",
    "include_fields": "id, priority, summary, status, assigned_to,resolution,milestone",
    "order": "bug_id"
  }
</bugzilla>


♦ '''Issue:''' Would it be simpler to make signed content use an entirely separate cookie jar. Including for XHR requests and <iframe>s to content outside of the signed package? That might allow us to use a more generic cookie jar feature.
== 2.5 Sprint Status ==


Signed content must never be considered same-origin with unsigned content, or content from another signed package. This is to ensure that unsigned content from the same https domain can't open the signed content in an <iframe> and then reach in to the opened page and use its privileges.
*[https://wiki.mozilla.org/FirefoxOS/New_security_model/FxOS_2.5_Scrum FxOS 2.5 Scrum Status]
*[https://wiki.mozilla.org/FirefoxOS/New_security_model/2.5_Status 2.5 Status]


The mechanism which is used to ensure that signed packages get a unique cookie jar should also be used to make sure that principals from signed an unsigned pages are never considered same-origin.
== Meeting Note ==
*[https://wiki.mozilla.org/FirefoxOS/New_security_model/Meetings Meeting Notes]


♦ '''Issue:''' Figure out exactly what field to use to indicate which signed package a principal belongs to.
== References ==
*[https://wiki.mozilla.org/FirefoxOS/New_security_model/Getting_Started_with_Signed_Packages Getting Started with Signed Packages]
Confirmed users
620

edits