Security is hard. Every developer needs to write some HTML/CSS at some point. Some of us might think that HTML and CSS can’t really be an attack vector. But modern web technologies bring them wide capabilities that can also be used for malicious purposes. This checklist aims to help developers learn security best practices and avoid vulnerabilities. Every item of the checklist are sorted by category to make it more usable.
Continue improving your security with Sqreen's monitoring and protection platform. It just takes a few minutes to get started.
Try Sqreen nowWhen you click on a link with a target=”_blank”
the new tab/window will have access to window.opener
which points to the window
of the first page and can be modified. To prevent it, add the attribute rel=”noopener” to avoid passing the window object to the new window. rel=”noreferrer”
was used in an older browser which kept the referrer through the same method.
Learn more:
https://mathiasbynens.github.io/rel-noopener/ https://www.w3.org/TR/html5/links.html#link-type-noreferrer
Using ids can cause issues with old browsers. This is why it’s not recommended to use ids on sensitive elements because it makes the element easier to find and target.
Learn more:
https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors
The adoption of password managers is skyrocketing 🚀, which is great, but it also comes with responsibilities for developers. It is important to keep an HTML form structure as simple as possible to avoid users having to manually type their passwords (and loose the security benefit of password managers).
There is also the attribute autocomplete="new-password"
that tells password managers to generate a random password directly into the form.
Learn more:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/password#Allowing_autocomplete
Smart email applications can automatically create links from the email content such as phone numbers, email address, URL and location. To prevent bad behavior (e.g having a URL as a username in your company report) you can add invisible/non-recognisable characters into the displayed string to break the recognition.
Learn more:
Iframes are part of the internet landscape with all the third-party widgets. To make sure that they can’t do anything harmful, it’s recommended to set restrictions on them. Try to always use a sandbox
attribute or additional flags like allow-forms
, allow-popups
etc.
Learn more:
https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
Packages are part of the standard development ecosystem. But we can’t forget that they introduce code that we don’t control. Don’t just rely on the number of downloads and have a look at sources to make sure nothing harmful is hidden. You can also prevent to load untrusted content by authenticating the content with a cryptographic hash representing the data you expect to load.
Learn more:
https://medium.com/@jdan/i-peeked-into-my-node-modules-directory-and-you-wont-believe-what-happened-next-b89f63d21558 https://www.w3.org/TR/SRI/ https://github.com/dxa4481/cssInjection
Browser validation of user inputs is more and more powerful and HTML has some mechanisms and properties that can be very helpful to create a first base layer of validation. But you can’t only rely on the browser validation of the user input. You need a server-side validation for sensitive actions (authentication, sanitization, etc). Browser and frontend validation is more acting as the first layer of filtering before sending data to the backend.
Learn more:
https://www.smashingmagazine.com/2009/07/web-form-validation-best-practices-and-tutorials/ https://www.rdegges.com/2018/please-stop-using-local-storage/
If you need to comment some parts of your code, be careful to not deploy them in production. As browser inspectors are more and more powerful and easy to use, everyone can display back the commented content and possibly get sensitive information from it.
Learn more:
https://github.com/bahmutov/eslint-rules#no-commented-out-code
https://github.com/webpack-contrib/uglifyjs-webpack-plugin#uglifyoptions
Source code can sometimes hide useful hints or even worse, sensitive functions or data. We often think about removing content from HTML but we shall not forget CSS too. So, it’s better to remove unused references in your CSS. (e.g: .new-secret-color-not-released-yet: {color: red}
)
Never trust user inputs. It’s probably the most important thing that you can do to prevent a lot of attacks using HTML/CSS vulnerabilities. Weak or missing sanitization can let your app be vulnerable to XSS injections. If you rely on user inputs to generate HTML, JSON, CSS, etc., use proper escaping functions to do so.
Learn more:
https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
https://www.sjoerdlangkemper.nl/2016/01/29/circumventing-xss-filters/
https://developer.salesforce.com/page/Secure_Coding_Cross_Site_Scripting
The internet ecosystem is moving fast and new security features and improvements are pushed everyday. It’s highly recommended to keep abreast of the latest news to keep your application secure.
Lear more:
https://www.google.com/search?q=site%3Achromereleases.googleblog.com%20security https://www.mozilla.org/en-US/security/ https://support.apple.com/en-us/HT201222 https://developer.microsoft.com/en-us/microsoft-edge/platform/status/?q=category%3Asecurity https://www.opera.com/help/tutorials/security/
Each script or widget that you embed on your website is an attack vector. You can use a Content Security Policy to protect your app from potential attacks by defining the assets you allow/disallow to load. You can use the nonce
to override the restrictions in the directives for old code.
Lear more:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP https://developers.google.com/web/fundamentals/security/csp/ https://www.w3.org/TR/CSP3/#security-nonces
Check your HTML/CSS code with a validator. You can also use automated tools like Lighthouse to check other best-practices.
Learn more:
https://developers.google.com/web/tools/lighthouse/ https://jigsaw.w3.org/css-validator/ https://validator.w3.org/
There are many ways to avoid coding mistakes and a linter is one of them. Depending on the rules you enable, they can also help your prevent basic security issues. (e.g force attribute rel="noopener noreferrer"
on links with a target="_blank"
). But keep in mind that the level of false positives can be quite high.
Learn more:
There are multiple ways to hide content in CSS. With display, opacity, visibility, transform
, you’re easily able to visually hide content of your page.
But be careful about the content you’re hiding. You should make sure it doesn’t contain sensitive data or actions.
The property pointer-events
lets you decide which element can be the target of mouse events. It’s pretty usual to set pointer-events: none
to disable the behavior of an element. But in case of sensitive actions, blocking the action should be done backend side.
Learn more:
https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
Monitoring and protection platform made to be incredibly powerful yet very easy to use.
Unmatched security insights: Access to more detailed security analytics than ever, including applevel incidents you can act on immediately.
Instant Protection: Out-of-the-box modules protect apps against a broad array of threats. Setup takes minutes, no config required.
Easily meet enterprise compliance needs: Get access to the best controls without hiring expensive security teams or consultants.
Want this handbook as a PDF?
Scan the QR-code, or go to:
https://www.sqreen.com/checklists/html-css-security-checklist