The good news is in 2021 it’s much harder to use it to crash a user’s browser - in
Firefox dialog.showModal is behind a flag
so it has to be
specifically enabled by users. In all browsers preferred way to use this tag in 2021 is to use the
which is a much more sensible way of using the tag. However, it
is still possible
to do some things mentioned below
and kill browser performance
(and it’s still possible to steal focus in
I thought it would be a good opportunity to revisit it as I feel it still has issues that makes it a potentially dangerous tag to use on the web without thinking about the implementation. One thing that blogpost (and me pointing it out) does is bring out people to say it’s nothing to do with the platform…
Certainly, some people didn’t agree - and it would be a valid suggestions to say this is achievable in other ways. However, several years later I still stand by my original points:
- By default, library modals do not completely steal the users mouse focus even when in a loop of generating modals you can click elsewhere on the page (although of course they always can be made to!)
- It’s still very easy to kill browser performance very easily, very quickly because there is no limit to the number of dialogs that can be created
<dialog>tag is built into the platform, so there’s no way to stop its use by blocking any offending library code - bad actors have all the tools to do this available in the DOM with a single HTML file.
- A single API is also a single attack surface - any script injection attacks don’t have to worry about library-specific
implementations - they can
always rely on
<dialog>elements on the page.
Over the last few years, users have also been taught bad habits to click any popup in operating systems, or in the browser through the use of Cookie popups, newsletter popups and other generally badly implemented ideas.
Revisiting The Hacks
Now on StackBlitz, I’m using the same demo as before with some updates. The example does the following:
- Adds a listener over the close button which, when the user hovers over it disables the button and moves the modal.
- Adds a second close button that when pressed closes the modal but also triggers an undesirable path (such as installing malware because we have a trusted user-triggered Event that allows Web APIs)
- 2 Buttons that send you into a loop of death in infinitely creating modals that can’t be escaped, and always steals focus.
- Hijacking the users keyboard, so users cannot use
Esc, not can they
Tabaway from the dialog or use
Spaceto click any buttons.
Overriding the DOM for fun
One issue is that you pretty much have full control over the DOM of the dialog. This is good in many ways, but with great power…
With our annoying button, we use this to control the
transform style of the modal itself and move it beyond the reach
of the user. We add a second button that does close the modal using a call to
modal.close() but after that we can
My argument here is that outside of styling some parts of the look and feel, it should not be possible to mess with the dialog after it has been presented.
Blocking the Escape Route
Of course most users might try press the Escape key in hopes that whatever is on the screen will disappear, but we have that particular route covered.
In this case we hijack when we detect the code for the
Escape key, as well as any other buttons the user might try to
use to escape - and we use it to just keep clicking the open button for creating more modals.
Killing the Browser
It’s quite easy to create a stack overflow In 2021 it’s less easy to create a Stack Overflow with the dialog, but it is still possible to slow down the users browser and spike CPU by just calling a setInterval on it:
Here we can see that we get a lot of spikes in CPU usage and memory garbage collection is constantly triggered:
My argument here is to limit the number of dialogs that can be shown on screen at once - modern browsers already do this for
alert- allowing the user to set
Don't show popus anymore for this websitewhen it’s abused by developers.
One way around this in your own code is to use the
<dialog> tag only within your own components - I’ve
created another example with a web component
the dialog in to a ShadowRoot
and provides the global a
state, so you cannot open more than one at a time and third-party scripts cannot access the internal
Hopefully you enjoy the demo and this post. I’m not against the use of dialogs themselves - but I think providing some way to limit the number of modals that can be shown, and limit what can and cannot be changed within it would provide a deeper layer of secure use across the web platform.