<html> <body> <div id="log"></div> <form id="form" method="POST" action="http://localhost/server/POST-echo.php"> <input name="internalId" value="id to be used to link the requests"> <textarea style="display: block;" name="fingerprint"></textarea> <button>submit</button> </form> <script> const origin = "iframe"; function draw(canvas){ "use strict"; canvas.setAttribute("width", 220); canvas.setAttribute("height", 30); const fp_text = "BrowserLeaks,com <canvas> 10"; const ctx = canvas.getContext("2d"); ctx.textBaseline = "top"; ctx.font = "14px 'Arial'"; ctx.textBaseline = "alphabetic"; ctx.fillStyle = "#f60"; ctx.fillRect(125, 1, 62, 20); ctx.fillStyle = "#069"; ctx.fillText(fp_text, 2, 15); ctx.fillStyle = "rgba(102, 204, 0, 07)"; ctx.fillText(fp_text, 4, 17); return ctx; } function topTest(){ "use strict"; // create window canvas const canvas = document.createElement("canvas"); // draw image in window canvas const ctx = draw(canvas); return { imageData: ctx.getImageData(0, 0, canvas.width, canvas.height), url: canvas.toDataURL(), isPointInPath: getIsPointInPath(ctx) }; } function getIsPointInPath(ctx){ "use strict"; ctx.beginPath(); ctx.moveTo(20, 19); ctx.lineTo(40, 19); ctx.lineTo(30, 30); ctx.closePath(); ctx.stroke(); return ctx.isPointInPath(30, 19); } function hashToString(hash){ "use strict"; const chunks = []; (new Uint32Array(hash)).forEach(function(num){ chunks.push(num.toString(16)); }); return chunks.map(function(chunk){ return "0".repeat(8 - chunk.length) + chunk; }).join(""); } const send = function(){ "use strict"; return async function send(form, {url, imageData, isPointInPath}){ const buffer = new TextEncoder("utf-8").encode(url); const hashes = await Promise.all([ crypto.subtle.digest("SHA-256", buffer), crypto.subtle.digest("SHA-256", imageData.data) ]); const data = JSON.stringify({ origin, urlHash: hashToString(hashes[0]), imageDataHash: hashToString(hashes[1]), isPointInPath }, null, "\t"); form.fingerprint.value = data; const xhr = new XMLHttpRequest(); xhr.open("POST", form.action, true); xhr.onreadystatechange = function(){ if (this.readyState === 4){ const status = this.status; if (status === 200 || status === 304) { console.log("Sending xhr successful from", origin, ":", data); } else { console.log("Sending xhr failed:", this); } } }; xhr.send(new FormData(form)); window.setTimeout(function(){ form.submit(); window.setTimeout( function(){ document.getElementById("log").textContent = "You see the real canvas fingerprint, but it cannot leak from this iFrame."; }, 250 ); }, 1000); }; }(); send(document.getElementById("form"), topTest()); </script> </body> </html>