Saptak's Blog

Adding CSP hashes for styles in Chromium

Posted: 2021-01-17T14:13:55+05:30

Hashes to allow inline styles & scripts

The usual practice of having a tight, secure CSP is to not allow any inline style or inline scripts. This helps mitigate malicious scripts entered via data injection from getting executed.

When I say inline scripts, one might understand 2 different scenarios:


<!-- Scenario 1 -->
<script>alert('Hello world');</script>

or


<!-- Scenario 2 -->
<button onclick="alert('Hello world');">
    Click me!
</button>

Now, the easiest way to allow this would be to add unsafe-inline in script-src of the CSP. But then we are back to the problem of executing malicious scripts entered by data injection. There are two ways to still allow only these scripts to work: nonce and sha256 hashes. We are going to talk about sha256 hashes here.

The idea is to get the sha256 hash of the entire script and add it to the script-src. So in this case, it would be something like this:

script-src 'self' 'sha256-DUTqIDSUj1HagrQbSjhJtiykfXxVQ74BanobipgodCo='

You can get the hash from https://report-uri.com/home/hash. Also, chromium browsers will usually show the hash that should be added for a particular inline script.

The Problem

Now, all this sounds good, and in Firefox, just adding the above to your CSP will make both the scripts to work. However, in chromium, the above CSP will work only in Scenario 1 but not in Scenario 2. You can read more about the discussion here: https://bugs.chromium.org/p/chromium/issues/detail?id=546106#c8

In JavaScript, I think in general scenario 1 will be much more encouraged than scenario 2. So scenario 2 might not be encountered that often. However, the situation changes, when it comes to styles (or CSS)

In case of inline styles, following are the scenarios:


<!-- scenario 1 -->
<style>p{color: blue;}</style>

and


<!-- scenario 2 -->
<p style="color: blue;">This is a text</p>

In CSS, the second scenario is much more common when someone does inline styles than scenario 1. But again, in this case, adding a sha256 hash to style-src won't execute the scenario 2 in chromium browsers.

This is because styles added in scenario 2 are part of the style attribute in the HTML tag which in CSP terms are essentially event handlers. According to w3c CSP draft, the hash in style-src allows the inline styles mentioned inside <style> tag to pass but doesn't allow event handlers (as is the case in scenario 2). There's more on this discussion here.

So it's a feature?

Yes, it is a feature. In chromium browsers, adding a hash to style-src only allows any inline style written inside the <style> tags to execute. This is by design. If you need to execute the inline styles present in style= attribute of HTML tags, you need to use another directive in CSP called style-src-attr. Similarly, script-src-attr should be used if you are doing JavaScript event handling in the HTML tag itself.

So, for example, if you want to only allow an inline CSS such as this:


<p style="color: blue;">This is a text</p>

all you need to do is put the sha256 hash in style-src-attr along with 'unsafe-hashes'. This will tell the browser to allow any inline style, with the hashes that you added in style-src-attr to be executed.

So the CSP will have something like this:

style-src-attr 'unsafe-hashes' 'sha256-C8uD/9cXZAvqgnwxgdb67jgkSDq7f8xjP8F6lhY1Gtk='

And, that's it! That will do the trick in any chromium browser. The related code for chromium can be found here. According to caniuse.com, all chromium browsers above 75 supports this behaviour.

Even though firefox still doesn't have support for style-src-attr but it allows inline styles and scripts of all types to pass based on style-src and script-src hashes. So as long as the hash is mentioned in both style-src and style-src-attr, it should work in most of the browsers.

As for the explanation behind why 'unsafe-hashes', there is a pretty good explainer document written by andypaicu talking about exactly this.

Also, read more about style-src-attr in detail in the w3c draft to understand exactly what's happening and what kind of risk it may still pose.

PS: Inline JavaScript event handlers using script-src-attr can be very risky given an attacker can trigger a passing javascript from within an unrelated HTML tag.


Configuring Jest with React and Babel

Posted: 2019-08-26T11:38:00+05:30
Jest is a really good frontend testing framework and works great with React and Babel out of the box, along with Enzyme for component testing. But, imports with React and Babel can often be filled with nasty imports. I wrote in a previous blog about how to make better more cleaner imports using some webpack tweaks.

But the problem appears when we try to write Jest and Enzyme tests with them. Because Babel can now longer understand and parse the imports. And without Babel parsing them and converting to ES5, jest cannot test the components. So we actually need a mix of Babel configuration and Jest configuration.

Note: This post assumes you already have jest, babel-jest and babel/plugin-transform-modules-commonjs packages installed using your favorite javascript package manager.

Basically, the workaround is first we need to resolve the cleaner imports into the absolute paths for the import using Jest configurations, and then use the Babel configurations to parse the rest code (without the modules) to ES5.

The configuration files look something like these:

babel.config.js

module.exports = api => {
const isTest = api.env('test');
if (isTest) {
return {
presets: [
[
'@babel/preset-env',
{
modules: false,
},
],
'@babel/preset-react',
],
plugins: [
"@babel/plugin-transform-modules-commonjs",
],
}
} else {
return {
presets: [
[
'@babel/preset-env',
{
modules: false,
},
],
'@babel/preset-react',
],
}
}
};


jest.config.js

module.exports = {
moduleNameMapper: {
'^~/(.*)$': '<rootDir>/path/to/jsRoot/$1'
}
}

So let's go through the code a little.

In babel.config.js, we make a check to see if the code is right now in test environment. This is helpful because
  1. Jest sets the environment to "test" when running a test so it is easily identifiable
  2. It ensures that the test configuration don't mess up with the non test configurations in our Webpack config (or any other configuration you are using)
So in this case, I am returning the exact same Babel configuration that I need in my Webpack config in non-test environment.

In the test configuration for Babel, we are using a plugin "@babel/plugin-transform-modules-commonjs". This is needed to parse all the non component imports like React, etc. along with parsing the components from ES6 to ES5 after jest does the path resolution. So it helps to convert the modules from ES6 to ES5.

Now, let's see the jest.config.js. The jest configuration allows us to do something called moduleNameMapper. This is a very useful configuration in many different usecases. It basically allows us to convert the module names or paths we use for module import to something that jest understands (or in our case, something that the Babel plugin can parse). 

So, the left hand part of the attribute contains a regular expression which matches the pattern we are using for imports. Since our imports look something like '~/path/from/jsRoot/Component', so the regular expression to capture all such imports is '^~/(.*)$'. Now, to convert them to absolute paths, we need to append '<rootDir>/path/to/jsRoot/' in front of the component path.

And, voila! That should allow Jest to properly parse, convert to ES5 and then test.

The best part? We can use the cleaner imports even in the .test.js files and this configuration will work perfectly with that too.

Making cleaner imports with Webpack and Babel

Posted: 2019-08-10T12:30:00+05:30
You can bring in modules from different javascript file using require based javascript code, or normal Babel parse-able imports. But the code with these imports often become a little bad because of relative imports like:

import Component from '../../path/to/Component'

But a better, more cleaner way of writing ES6 imports is

import Component from '~/path/from/jsRoot/Component'

This hugely avoids the bad relative paths  for importing depending on where the component files are. Now, this is not parse-able by babel itself. But you can parse this by webpack itself using it's resolve attribute. So your webpack should have these two segments of code:

resolve: {
        alias: {
            '~': __dirname + '/path/to/jsRoot',
            modernizr$: path.resolve(__dirname, '.modernizrrc')
        },
        extensions: ['.js', '.jsx'],
        modules: ['node_modules']
    },

and

module: {
        rules: [
            {
                test: /\.jsx?$/,
                use: [
                    {
                        loader: 'babel-loader',
                        query: {
                            presets: [
                                '@babel/preset-react',
                                ['@babel/preset-env', { modules: false }]
                            ],
                        },
                    }
                ],
            },
}

The {modules: false} ensures that babel-preset-env doesn't handle the parsing of the module imports. You can check the following comment in a webpack issue to know more about this.

Ticket Ordering and Positioning (Front-end)

Posted: 2017-06-21T13:52:00+05:30

As discussed in my last blog about ticket ordering and positioning, in this blog we are gonna talk about how we implement the front-end part of re-arranging the tickets. We essentially do it using compute and methods of Vue.js. The functionality that is expected in the front-end is, the event organizer should be able to move the tickets Up or Down the order and save that position so that it gets displayed later in that very particular order.
Like I said above we use two main things of Vue.JS for this purpose - Compute and Methods.

Compute

We use this to get the sorted list of tickets based on the position key of the tickets and use this sorted list to display the tickets in the event editing wizard. Whenever you change the value of the position for a ticket, it automatically updates the list to sorted list again and hence the order of ticket in the front-end also updates. To add a compute function in Vue.JS, inside the new Vue() object creation, we add an attribute computed and inside that we put all the functions that we are gonna use. So in our case the function is sortedTickets . We make use of the sort function of lodash to sort the tickets array based on it's position attribute.

Now while showing or looping over the tickets, we loop over sortedTickets  rather than the original ticket array.

Method

This method is called when the button is clicked to move it up or down. This makes the calculations to determine the values of the position of the two tickets which are being re-ordered in a single click. To add a method, we do it in a similar way like computed but using methods attribute instead. The methods we have written to move tickets up or down is moveTicket.

It has 3 parameters - ticket, index and direction. So when this function call is emitted, depending on the button, the direction is either "up" or "down" while the other two parameters are the ticket properties of the particular ticket. So in this function we check the direction and accordingly update the value of position for the tickets involved in the arranging. As we update the position here, because of the compute, the UI automatically updates to show the tickets in the new order.
Finally after all arrangement is done, the position is always saved in a hidden input field which is then passed as form data and is saved in the database so that the position value can be used in other pages for showing the ticket in order.

Show Ordered Ticket

In other pages, while showing ordered ticket, we already receive the ticket array in sorted format based on the position key. Hence, we don't need to worry about it in the front-end and it automatically is shown in the sorted order.