Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Jeremy Keith
Jeremy Keith

Posted on • Originally published atadactio.com on

Securing client-side JavaScript

I mentioned thatI overhauled the JavaScript onThe Session recently. That wasn’t just so that I could mess about withHTML web components. I’d been meaning to consolidate some scripts for a while.

Some of the pages on the site had inline scripts. These were usually one-off bits of functionality. But their presence meant that mycontent security policy wasn’t as tight as it could’ve been.

Being a community website, The Session accepts input from its users. Literally. I do everything I can to sanitise that input. It would be ideal if I could make sure that any JavaScript that slipped by wouldn’t execute. But as long as I had my own inline scripts, my content security policy had to allow them to be executed withscript-src: unsafe-inline.

That’s why I wanted to refactor the JavaScript on my site and move everything to external JavaScript files.

In the end I got close, but there are still one or two pages with internal scripts. But that’s okay. I found a way to have my content security policy cake and eat it.

In my content security policy header I can specifiy that inline scripts are allowed, but only if they have a one-time token specified.

This one-time token is calleda nonce. No, really. Stop sniggering. Naming things is hard. And occassionally unintentionally hilarious.

On the server, every time a page is requested it gets sent back with a header like this:

content-security-policy: script-src 'self' 'nonce-Cbb4kxOXIChJ45yXBeaq/w=='
Enter fullscreen modeExit fullscreen mode

That gobbledegook string is generated randomly every time. I’m using PHP to do this:

base64_encode(openssl_random_pseudo_bytes(16))
Enter fullscreen modeExit fullscreen mode

Then in the HTML I use the same string in any inline scripts on the page:

<script nonce="Cbb4kxOXIChJ45yXBeaq/w==">…</script>
Enter fullscreen modeExit fullscreen mode

Yes, HTML officially hasan attribute callednonce.

It’s working a treat.The security headers for The Session are looking good. I have some more stuff in my content security policy—check out the details if you’re interested.

I initially thought I’d have to make an exception forthe custom offline page on The Session. After all, that’s only going to be accessed when there is no server involved so I wouldn’t be able to generate a one-time token. And I definitely needed an inline script on that page in order to generate a list of previously-visited pages stored in a cache.

But then I realised that everything would be okay. When the offline page is cached, its headers are cached too. So the one-time token in the content security policy header still matches the one-time token used in the page.

Most pages onThe Session don’t have any inline scripts. For a while,every page had an inline script in thehead of the document like this:

<script nonce="Cbb4kxOXIChJ45yXBeaq/w==">document.documentElement.classList.add('hasJS');</script>
Enter fullscreen modeExit fullscreen mode

This is something I’ve been doing for years: using JavaScript to add a class to the HTML. Then I can use the presence or absence of that class to show or hide elements that require JavaScript. I have another class calledrequiresJS that I put on any elements that need JavaScript to work (like buttons for copying to the clipboard, for example).

Then in my CSS I’d write:

:not(.hasJS) .requiresJS { display: none;}
Enter fullscreen modeExit fullscreen mode

If thehasJS class isn’t set, hide any elements with therequiresJS class.

I decided to switch over to usinga scripting media query:

@media (scripting: none) {  .requiresJS {   display: none;  }}
Enter fullscreen modeExit fullscreen mode

This isn’t bulletproof by any means. It doesn’t account for browser extensions that disable JavaScript and it won’t get executed at all in older browsers. But I’m okay with that. I’ve putthe destructive action in the more modern CSS:

I feel that the more risky action (hiding content) should belong to the more complex selector.

This means that there are situations where elements that require JavaScript will be visible, even if JavaScript isn’t available. But I’d rather that than the other way around: if those elements were hidden from browsers thatcould execute JavaScript, that would be worse.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

¯\_(ツ)_/¯
  • Location
    Brighton, UK
  • Work
    Web Stuff Do-er at Clearleft
  • Joined

More fromJeremy Keith

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp