Security/CSP:Implementation

From MozillaWiki
Jump to: navigation, search

This is a collaboration page that is intended to help guide the process of implementing CSP in Firefox.

Parts:

Component
Each document that uses CSP has a CSP Component (CSPC) attached to it. This CSPC parses any policy, stores it, is responsible for reporting errors and handling content policy enforcement.
CSP Service
Each application has a singleton Content Security Policy Service (CSP-CPS) that delegates content policy decisions to each CSPC. It knows how to locate a CSPC instance for a given request, and for each ShouldLoad() call, decides which CSPC is responsible for the decision.

Csp component arch.png

Additionally, hooks must be placed in the appropriate document-loading spots so that CSP can be created and initialized in a way that is properly enforceable.


The CSP Component (CSPC)

Code Location
$SRC/caps/src/contentSecurityPolicy.js
Interface
$SRC/caps/idl/IContentSecurityPolicy.idl
Use
One per document.
Purpose
To do the bulk of the CSP policy enforcement and parsing

On nsDocument load, a CSPC is created when the appropriate HTTP header was specified in the response *and* the document is a content document (not chrome, or system privileged).

 //in nsDocument.cpp:StartDocumentLoad()
 nsAutoString cspHeaderValue;
 nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
 nsIAtom* cspHeaderAtom = NS_NewAtom("x-content-security-policy");
 
 this->GetHeaderData(cspHeaderAtom, cspHeaderValue);
 
 if(NS_SUCCEEDED(ssm->IsSystemPrincipal(NodePrincipal(), &system)) && system) {
   //only makes sense to register new CSP if this document is not priviliged
   return NS_OK;
 }
 
 if(cspHeaderValue.IsEmpty()) {
   // no CSP header present
   return NS_OK;
 }
 // mCSP is type (<nsICOMPtr> IContentSecurityPolicy)
 mCSP = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
 if(NS_FAILED(rv)) {
   return rv;
 }
 // Store the request context for violation reports
 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
 mCSP->ScanRequestData(httpChannel);

 // Start parsing the policy
 nsCOMPtr<nsIURI> selfURI;
 aChannel->GetURI(getter_AddRefs(selfURI));
 mCSP->RefinePolicy(cspHeaderValue, selfURI);

 // Check for frame-ancestor violation
 nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocumentContainer);
 if(docShell != NULL) {
   PRBool safeAncestry = false;
   // PermitsAncestry sends violation reports when necessary
   rv = mCSP->PermitsAncestry(docShell, &safeAncestry);
   if(NS_FAILED(rv)) { return rv; }

   if(!safeAncestry) {
     aChannel->Cancel(NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, 99));
   }
 }


API

The CSPC has four methods:

 void refinePolicy(in AString policyString, in nsIURI selfURI);

This method is called with a string representation of a CSP (e.g., "allow *") when a policy is encountered. It intersects the current policy with the provided one and returns a policy that is strict enough to satisfy both policies.

 boolean permitsAncestry(in nsIDocShell docShell);

This method is called as the document is being loaded, but after the CSPC has been initialized and refined, to determine if it is enframed in a way violating the policy. If the document's load violates the "frame-ancestors" directive, a report is sent and the document is not rendered.

 void scanRequestData(in nsIHttpChannel aChannel);

This method is called after the CSP object is created to fill in the appropriate request and request header information needed in case a report needs to be sent.


 short shouldLoad(in unsigned long   aContentType, 
                  in nsIURI          aContentLocation, 
                  in nsIURI          aRequestOrigin, 
                  in nsISupports     aContext, 
                  in ACString        aMimeTypeGuess, 
                  in nsISupports     aExtra);

This method is called by the CSP Service when a URI is requested. It queries the CSP policy structure (internal data representation of the policy) and returns true if the current CSP allows the load.

 short shouldProcess(in unsigned long   aContentType, 
                     in nsIURI          aContentLocation, 
                     in nsIURI          aRequestOrigin, 
                     in nsISupports     aContext, 
                     in ACString        aMimeType,
                     in nsISupports     aExtra);

This method is called by the CSP Service after a data at an URI has been retrieved. It is currently an inactive stub that always accepts the process request, but is available for future policy use.

The CSP Content Policy Service (CSP-CPS)

Code Location
$SRC/content/base/src/nsCSPService.*
Use
One per application
Purpose
Broker HTTP requests for appropriate CSP components.

This XPCOM service registers with the ContentPolicy service and delegates policy enforcement to any CSP components (documents) that have a CSP component.

Hooking Into Document Loading

This section describes how CSP is initialized when a document is loaded and was served with an X-CSP header.

In nsDocument::StartDocumentLoad(), an instance of the CSP component is created given the nsIChannel information. All the magic of CSP enforcement takes place from the CSP component instance.

The component is destroyed in nsDocument::Destroy().

Enforcing Base Restrictions

No-EVAL: Modifications to JSAPI

TODO

No-Inline-Script: Modifications to DOM

TODO