In a previous blog post, we examined how a Content Security Policy (CSP) is like good advice given to kids. The blog entry explained the CSP mechanism in general and outlined the phases of a possible implementation project. Here, we'll look into the technical details: how do you maintain a CSP in an SAP S/4HANA Cloud Private Edition system?
The one-stop shop for maintaining CSPs is transaction ICF_HEADER_FRAMEWORK which is available from SAP S/4HANA Cloud Private Edition version 2022 (and which is continuously enhanced – you might need to implement some notes to get the latest features)
Let's quickly run through the features of the transaction.
A CSP in ICF_HEADER_FRAMEWORK
Clients and (no) transport
CSPs are client-dependent. This means you can tailor them to the specific needs of each client. But it also means that you need to maintain the configuration multiple times.
CSPs are also not transportable but must be maintained in every system individually. The reason for this is that CSPs in production systems are very likely to differ from those in test systems anyway (production systems will read fonts from production content servers and will probably be restrictive in general).
Luckily, there's an import-export feature available and simultaneous import into multiple clients is possible.
CSP per path
Maintaining a CSP for a path is fairly simple. Create a new line or select one from the list and open the edit window (easter egg: double-clicking the gray box on the far left of the row will also open the edit window).
There, you can specify the directive you want to maintain and the respective values. There's an F4-help for special values like $nonce, 'unsafe-inline' and the trusted sites lists (see below). After clicking "Add", the value will be added to the list (merging it into the right line if the directive already exists).
You might get multiple lines for one directive in case the "Value" field becomes too long.
CSP maintenance for one path. Highlighted: special values $nonce and the trusted-sites list $FONT_SITES
The special value $nonce is used in case the application uses inline scripting that is annotated with a nonce. The nonce must be made known to the http header framework by the application code, of course. If this is the case, the $nonce maintained in the CSP will automatically be replaced with the current nonce provided by the application.
Similarly, for $hash – if an inline script is used and the hash value of the script is made known to the http header framework by the application code, the $hash in the CSP configuration will be replaced with the hash for the script at runtime.
At runtime, the most specific matching CSP for a given path will be used.
Example: suppose you have CSPs maintained for /sap/bc/bsp/ and for /sap/bc/bsp/myapps.
Then the page /sap/bc/bsp/crm will get the CSP of /sap/bc/bsp and /sap/bc/bsp/myapps/app1 will get the one from /sap/bc/bsp/myapps/ because it's the more specific one.
This means that you can maintain a fallback CSP on path / which will automatically be used for any path you might have missed.
Report-only CSPs defined per UI technology with a catch-all default on /
Trusted sites are basically shorthand. You can define a list of sites for a particular use (e.g., the places where your custom fonts are) and then use the name of the list inside the CSP.
Maintaining a trusted site list
Using a trusted site list in a CSP
The trusted sites lists are shown in the value help of the "Value" field in the CSP maintenance. Just as the placeholders $nonce and $hash, the trusted site names are prefixed with $ in the maintenance UI (no, you don't want to call your trusted sites "nonce").
Advantage: if the same list turns up in multiple places, you only need to maintain the hostnames once. Additionally, if the list is long, the CSP will be easier to read in the editor if it only the name shows up rather than the full list.
Using a trusted site list in a CSP, II
The import/export feature under "Header Register" -> " Export CSP Headers" and "Import CSP Headers" does pretty much what it says on the tin. The export produces an .xlsx file that can be imported again.
Main menu showing import/export feature.
During import, you can decide if you want to overwrite the existing CSP completely (delete the table and import from scratch) or line-wise (update/insert from file). If you have sufficient privileges, you can also choose to import the file into multiple clients simultaneously.
Clicking "CSP Reports" takes you to a rather unsurprising UI for displaying the reports sent to the system's report endpoints.
Note that column width of the displayed table is restricted, so some values might be truncated. If you double-click a cell, a text view window will pop up to show you the full content.
The reporting endpoints require authentication by default. This means the system has user information in addition to the original report from the browser. This information is also shown in the table (a nice advantage compared to reporting to a third-party report-collection service).
CSP reports can be highly redundant, so by default, duplicates are filtered out to remove clutter. Yet at times, it can be useful to have all records to reproduce the exact sequence of events. Use the "Show duplicates" checkbox to make the grid do just that.
Under "Header Register" -> "Maintain Report URI", you can maintain custom reporting endpoints. The maintained endpoints are automatically added to the CSP in the report-uri directive. You can maintain separate endpoints for enforcing and report-only CSPs. If the fields are empty, the default endpoints of the system are used.
And yes, report-uriis a standard CSP-directive and you can maintain it in the CSP directly if you want different endpoints for different paths. Values maintained in the CSP take precedence over the general CSP report-uri configuration. But think twice before you do a per-path overwrite – it's likely to be a source of confusion!
Maintenance of report-uri
If you are a CSP-professional, you will know that report-uri is officially deprecated. The (not yet widely adopted) successor is the combination of the header field reporting-endpoints and the CSP directive report-to. The HTTP header framework enables you to use this as well. If you do, your CSP will contain both directives: report-uri and report-to. But browsers will only use one of them: if the browser supports report-to and report-to is present,report-uri will be ignored and if the browser doesn’t support report-to, it will ignore report-to as an unknown directive.
The number of CSP reports that come in via the reporting endpoint depends on the number of users and their activities. If your CSP happens to be particularly unsuitable and your users are particularly active, the report table will grow quickly. That's why there is an automatic cleanup mechanism. This mechanism can be configured in the main menu of "Display CSP Reports" under "CSP reports" -> "Maintenance".
By default, the cleanup runs every hour and deletes all entries that are older than 30 days. If the table has more than one million entries, the oldest are deleted.
Maintenance of cleanup parameters for CSP reports
Changes to the CSP configuration are logged and can be displayed under "Header Register" -> "Logged Changes"
You might know that CSPs also offer clickjacking protection via the frame-ancestors directive. Yet, SAP S/4HANA Cloud Private Edition is using a proprietary clickjacking protection method, so setting frame-ancestors in the CSP is not required. In fact, mixing the two technologies is not recommended (cf. SAP note 2319727).
The ICF_HEADER_FAMEWORK can also be used for other header fields. The main use-case is CSP maintenance, though.
More information can be found on the official help page and in SAP Note 3224606. The note also provides an example CSP in .xlsx format as a starting point for your implementation project.