background

In order to prevent information leakage or intellectual property infringement, it is necessary to add watermarking to pages and pictures in certain scenes. Adding a watermark to a Web page can be broken down into two main parts:

  • Generate a watermark – Prepare a watermark image and add it to the page
  • Protect watermark – Prevents users from maliciously modifying watermark

To generate the watermark

Suppose we want to generate the following watermark:

  • The watermark content is text
  • The Angle between the watermark and the horizontal axis is 15°
  • Wrong line effect between lines

The style the watermark should look like

In the scenario of full-screen watermarking, we usually set the watermark as the absolute positioning element of the highest Z-index and use Pointer-events: None to achieve click penetration without affecting the normal use of users.

position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
pointer-events: none;
z-index: 9999;
Copy the code

It is recommended to set the watermark under the body element. If the watermark is not set directly under the body element, ensure that position: relative is not used in the parent element. , to prevent the watermark is only displayed in the parent element, not to achieve full screen effect.

At present, there are mainly several front-end page watermarking schemes:

Canvas watermark

For the front end, the easiest way to create an image might be to use canvas. Canvas scheme can be summarized as the following steps:

  1. To create acanvasThe canvas instance
  2. usefillTextAdds text to the canvas
  3. throughcanvas.toDataUrlOutput as image
  4. Set the image as the background of the full-screen Watermark DIV

Creating watermarks is easy if you don’t need a split-line effect. You can rotate a single image to the desired Angle using the rotate method and tile the image onto the page repeatedly using the BACKground-repeat property of CSS. If we need the staggered line effect, we need to calculate the position of each watermark in the page and add them to the canvas one by one to generate the whole page watermark. We can use the transform and rotate of the canvas to achieve the rotation effect. Note that the canvas’s transform and rotate transform apply to the entire canvas. Since the canvas finally exports an image, the watermark will be slightly blurred when the page is enlarged.

SVG watermark

When only text is required for our watermarking, it is easier to implement the watermarking in SVG. The SVG solution can be summarized as follows:

  1. To create asvgThe label
  2. insvgAdd in labeltextTag that sets the text to the text taginnerHtml
  3. throughBase64.toBase64(new XMLSerializer().serializeToString(svgElement))Output the SVG tag as a Base64 image
  4. Set the image as the background of the Watermark div

In SVG, you can use the transform property to implement rotation, which has a different syntax than CSS’s transform, for example:

Text x={50} y={50} transform="rotate(-15,50,50)"}>Copy the code

Here the transform is for the current element, and we can easily fill the rotated text tag into the SVG.



SVG describes vector graphics, so zooming in on a page doesn’t affect the sharpness of the watermark.

Element watermark

Using elements to generate a watermark is to add tags one by one to the watermark div, which is easy to implement, but will add more tags to the page.

Protect the watermark

Watermarks are used to maintain information security. If no defense measures are taken, the watermarks added in the front end can be easily removed:

  • Delete the full-screen watermark div
  • Modify the style of the watermark so that it is not visible on the page
  • Modify or delete the content of watermark picture
  • Disable JavaScript
    • Of course, with JavaScript disabled, normal content on the page may not be displayed

So we also need a way to protect against watermark modification, mainly using the browser MutaitionObserver

The function of the MutaitionObserver is to listen for changes in the DOM of the current page. When the DOM changes, it can respond with a callback function, especially for those who want to know when and how the DOM elements of the current page have changed. MutationObserver is currently available in the following browsers:

MutationObserverA simple example

Const targetNode = document.getelementById ('some-id'); / / callback function that should execute when the observed changes, / / mutaions data structure reference https://developer.mozilla.org/zh-CN/docs/Web/API/MutationRecord const callback = function(mutations, observer) { // handle mutations }; Const Observer = new MutationObserver(callback); / / viewer configuration (need to see what changes) / / https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserverInit const config = { attributes: true, childList: true, subtree: true }; Observe.observe (targetNode, config); // Stop observing observer.disconnect() if not needed;Copy the code

Specifically, the MutaitionObserver allows us to listen for the following DOM changes:

  • Specifies all property changes on the target node
  • Specifies the addition and deletion of all child elements on the target node
  • Specifies the character data contained by the nodes in the target node or child node tree

When a change occurs, the MutaitionObserver instance calls the callback function we passed in, where we normally handle restoring the watermark.

Prevents deletion of watermark div

Note that the MutaitionObserver callback is not triggered if the monitored target node itself is deleted. In this case, we can listen to the parent element of the watermark div or the body element. When the parent element of the watermark is the main container of the page, only the parent element MutaitionObserver of the watermark will be invalid if deleted, and the content that needs to be protected in the page may also be deleted. In this case, deleting the watermark is of little significance, as is deleting the body element. AppendChild adds the deleted element back to the listener’s target node (in this case, the parent element of the watermark div) :

 parentNode.appendChild(elementRef.current)
Copy the code

Prevents modification of watermarking styles

Without shadow DOM, our style is exposed to the console, and if the watermark is set to display: None, it also loses protection. In this case, we can listen for the style attribute of the watermark div, and when the attribute changes, use setAttribute to reset the correct style to the element:

elementRef.current? .setAttribute('style', DEFAULT_STYLE_STRING);Copy the code

Note that we use inline CSS to store the style of the watermark. If a class is used to store styles, modifying styles in the class on the console does not trigger a callback.

Prevents modification or deletion of watermarked image contents

As mentioned above, MutationObserver can help us to listen for all the attributes of the target node change, but this is special for the watermark image content: When the page is resized and we need to recalculate the watermark image to cover the entire page, our code will also trigger an action to modify the watermark image. In this case, both code modification and manual malicious modification will trigger a callback in MutationObserver.

In this case, what we need to do is to distinguish between code changes, here is an idea:

  1. When generating watermark images, md5 is also performed according to the calculated results.
  2. When the triggerMutationObserverOn the callback, md5 is performed on the modified image to compare it with the MD5 generated for the image

    A. If the comparison results are the same, no action is required

    B. If the comparison results are different, generate a watermark image again

Of course, there is another way to prevent the modification of the watermark style and image content, that is, when the MutationObserver callback of the watermark element is triggered, the watermark div is deleted and a new watermark div is generated instead of making specific judgment and recovery logic. In this way you also need to stop the previous MutationObserver and start a new MutationObserver instance.