Subresource integrity is a recent W3C standard that protects against attackers modifying the contents of JavaScript libraries hosted on content delivery networks (CDNs) in order to create vulnerabilities in all websites that make use of that hosted library.
Earlier this month, I ran a Mozilla Observatory Audit and got an F. I implemented SRI in this PR, which upgraded my score to a B !
To implement SRI, you add a cryptographic hash to each <link> and <script> tag.
<script type='text/javascript' src="{% static 'mailchimp/local-mc-validate.js' %}" integrity="sha384-kNtXczGtgYT982q0agqJfC1hsrUBi3Z3JLr1VrrUIyUZRn2yS6GfAvZ0Dv10mpiy" crossorigin="anonymous"></script>
Most of my resources already have integrity values. For example, all bootstrap files served via CDN include SRI protection.
Still, many dependencies I use do not include it. The CSS/JS for the my code snippets syntax highlighter, Prism.js, does not come with SRI protection.
Luckily, I discovered django-sri which appears to be the go-to for SRI in Django. For css and .js files, you replace any {% static %}
tags with {% sri_static %}
. The module generates the whole <script>
tag, so
{% sri_static 'local.js' %}
becomes
<script> src="local.js" integrity="<cryptographic-hash>" crossorigin="anonymous"></script>
I don’t love this approach because it obfuscates how the resulting HTML will look.
I also encountered problems with django-sri. I couldn't get their hashes to work with my JavaScript files. Every time I used their tag, I got a 500 error on my server and there wasn't anything in the apache error log. The second issue was that django-sri does not seem to work with non-text files. Three of my <link>
tags point to favicon .pngs. When hashing these files, django-sri threw uncaught utf-8 parsing errors.
My workaround was to generate the hashes myself using the openssl library.
$ openssl dgst -sha384 -binary prism_patched.min.js | openssl base64 -A
I don't have any strong opinions on hashing algorithms for a personal blog. It's good enough for me that sha384 is a nist approved hash function.
After generating the hashes, I plugged them into the integrity values manually. The disadvantage is that I'll have to re-compute the hash if I change a resource. Luckily, I don't plan to modify my favicons. They are static assets.
After implementing SRI, I re-ran the Mozilla audit and was still getting flagged for non-compliance. Observatory doesn’t show which resources are missing integrity hashes. Luckily, I came across a GitHub repo called sri-check put out by a UK-based security company. It's a Python script you can point at any URL to tell you which resources don’t have SRI. It detected a JavaScript file hidden in one of my templates I hadn't hashed. After adding a hash and re-running the Mozilla audit, I got a green check mark and upgraded my score from a D- to B- !!!
Conclusion
SRI boosts site security by preventing CDN attacks. Many packages, like bootstrap, offer SRI protection out of the box, but I found several dependencies on my site without it. For those CSS/JS files, I hosted them locally on my server. I generated hashes using a combination of the django-sri module and the openssl library. My final security task to get a perfect score on Mozilla Observatory is to add a Content security policy. Expect a future post about that adventure!
Comments
- No comments yet.
John Solly
Hi, I'm John, a Software Engineer with a decade of experience building, deploying, and maintaining cloud-native geospatial solutions. I currently serve as a senior software engineer at HazardHub (A Guidewire Offering), where I work on a variety of infrastructure and application development projects.
Throughout my career, I've built applications on platforms like Esri and Mapbox while also leveraging open-source GIS technologies such as OpenLayers, GeoServer, and GDAL. This blog is where I share useful articles with the GeoDev community. Check out my portfolio to see my latest work!
0