Every once in a while I’ll see frontend developers tweet or blog about how great the HTML dialog tag is and each time I’m reminded me of an old Medium post I made a few years ago about its use.
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 open
attribute
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
Chrome).
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
- The
<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
document.getElementsByTagName('dialog')
to access<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 theyTab
away from the dialog or useSpace
to 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
trigger any JavaScript we want, such as loading new tabs, running bitcoin miners, searching for aliens, etc.
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.
My argument here is when a dialog is displayed, don’t allow JavaScript to hijack the keyboard and stop the user from closing the dialog - this is also bad for accessibility.
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 setDon't show popus anymore for this website
when it’s abused by developers.
Component Solution
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
that embeds
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 <dialog>
.
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.