-
Notifications
You must be signed in to change notification settings - Fork 3
Replace insecure ROT13 obfuscation with secure XOR-based methods, remove jQuery dependency, add mailto parameter support, and fix JavaScript binary data handling #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
14b3cf3
Initial plan
Copilot b101797
Implement XOR-based email obfuscation methods (methods 2.7 and 2.8)
Copilot f226bcf
Fix namespace issues and complete XOR obfuscation implementation
Copilot 3c86028
Remove jQuery dependency from emailobfuscator.js - addresses issue #40
Copilot 81229ae
Add support for mailto links with subject and body parameters - addre…
Copilot b1194d5
Update README with new XOR methods, security improvements, and jQuery…
Copilot 9e1ba87
Fix XOR JavaScript implementation - unified approach using external J…
Copilot cfb9232
Fix JavaScript inclusion configuration: show autoload_js setting for …
Copilot 79746cd
Update lib/EmailObfuscator.php
skerbis d149c95
Update assets/emailobfuscator.js
skerbis 1f0b9fd
Update lib/EmailObfuscator.php
skerbis d4e8e6f
Update assets/emailobfuscator.js
skerbis 0c10f0f
Update lib/EmailObfuscator.php
skerbis 5623ff8
Update assets/emailobfuscator.js
skerbis 5823579
Update assets/emailobfuscator.js
skerbis 270827b
Update lib/EmailObfuscator.php
skerbis 6d7059f
Update lib/EmailObfuscator.php
skerbis e7c5d97
Fix JavaScript syntax errors in XOR email deobfuscation function
Copilot b80b631
Fix base64UrlDecode InvalidCharacterError with proper padding logic a…
Copilot 28898b2
Fix base64UrlDecode InvalidCharacterError with improved binary data h…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| test_full_obfuscation.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,35 +5,70 @@ Das [REDAXO](http://www.redaxo.org)-Addon sorgt dafür, dass alle E-Mailadressen | |
|
|
||
| ## Funktionsweise | ||
|
|
||
| Durch die Integration des email_obfuscator Addons von RexDude stehen verschiedene Verschleierungsmethoden für E-Mailadressen zur Verfügung: | ||
| Durch die Integration des email_obfuscator Addons stehen verschiedene Verschleierungsmethoden für E-Mailadressen zur Verfügung: | ||
|
|
||
| 1. __ROT13 Einhorn-Markup__: Diese Methode findet alle E-Mailadressen und ersetzt deren `@` durch spezielles Einhorn-Markup: `<span class="unicorn"><span>_at_</span></span>`. Dadurch kann die E-Mailadresse nicht mehr so einfach von Bots ausgelesen werden und sollte ziemlich gut vor Spam geschützt sein. Weiterhin werden auch alle mailto-Links erkannt und verschlüsselt. | ||
| Beim Aufruf der Seite werden alle geschützten E-Mailadressen und mailto-Links mittels __JavaScript__ wieder entschlüsselt und in die ursprüngliche Form gebracht. __CSS-Styles__ sorgen dafür, dass die geschützten E-Mailadressen auf der Website richtig angezeigt werden, also mit `@` statt Einhorn. Damit fällt der Wechsel von verschlüsselt nach unverschlüsselt nicht auf, und auch in Umgebungen ohne JavaScript wird eine verschlüsselte Adresse richtig dargestellt. | ||
| __Bitte beachten__: Diese Methode benötigt für die Einhorn-Markup Methode __jQuery__ für die JavaScript-Funktionalität! | ||
| ### Sichere Methoden (empfohlen) | ||
|
|
||
| 2. __ROT13 JavaScript Verschlüsselung__: Um die Email-Adressen zu schützen, wird die E-Mailadresse durch ein JavaScript ersetzt, das die E-Mailadresse ins Dokument schreibt. Zur Verschleierung wird die Technik "ROT13 Encryption" angewendet. | ||
| 1. __XOR Verschlüsselung (Standard)__: Diese moderne Methode verwendet XOR-Verschlüsselung mit einem festen Schlüssel, um E-Mailadressen sicher zu verschleiern. Die verschlüsselten Daten werden in `data-*` Attributen gespeichert und automatisch per JavaScript entschlüsselt. Diese Methode ist exponentiell sicherer als die veralteten ROT13-Verfahren und bietet zuverlässigen Schutz vor Spam-Bots. | ||
| __Vorteile__: Starke Verschlüsselung, kein jQuery erforderlich, unterstützt mailto-Parameter wie `?subject=Betreff&body=Nachricht` | ||
|
|
||
| 2. __XOR Verschlüsselung mit dynamischem Schlüssel__: Diese besonders sichere Variante generiert für jede Seite/jeden Artikel einen anderen Verschlüsselungsschlüssel basierend auf der Artikel-ID. Dadurch wird die gleiche E-Mailadresse auf verschiedenen Seiten unterschiedlich verschlüsselt, was die Sicherheit maximiert. | ||
| __Vorteile__: Höchste Sicherheitsstufe, kontextabhängige Verschlüsselung, kein jQuery erforderlich, unterstützt mailto-Parameter | ||
|
|
||
| ### Veraltete Methoden (unsicher, nicht empfohlen) | ||
|
|
||
| 3. __ROT13 Einhorn-Markup__ ⚠️ __Veraltet, unsicher__: Diese Methode findet alle E-Mailadressen und ersetzt deren `@` durch spezielles Einhorn-Markup: `<span class="unicorn"><span>_at_</span></span>`. ROT13 ist ein einfacher Caesar-Cipher, der von modernen Tools leicht geknackt werden kann. | ||
| __Bitte beachten__: Diese Methode benötigt __jQuery__ für die JavaScript-Funktionalität! jQuery wird seit Version 2.0 des Addons nicht mehr benötigt. | ||
|
|
||
| 4. __ROT13 JavaScript Verschlüsselung__ ⚠️ __Veraltet, unsicher__: Diese Methode ersetzt E-Mailadressen durch JavaScript-Code mit ROT13-Verschlüsselung. ROT13 bietet keinen ausreichenden Schutz mehr gegen moderne Spam-Bots. | ||
| __Bitte beachten__: Diese Methode macht alle E-Mailadresse ohne klickbaren Link klickbar! | ||
|
|
||
| 3. __CSS Methode ohne JavaScript__: Um die Email-Adressen zu schützen, wird die Technik "CSS display:none" angewendet. | ||
| 5. __CSS Methode ohne JavaScript__: Diese Methode verwendet "CSS display:none" zur Verschleierung und funktioniert ohne JavaScript. | ||
| __Bitte beachten__: diese Methode entfernt den mailto-Link und verwandelt Adresse in name[at]domain.tld. Die Adresse ist damit nicht mehr klickbar. | ||
|
|
||
| 4. __ROT13 JavaScript Verschlüsselung mit CSS Methode__: Um die Email-Adressen zu schützen, werden die Techniken "CSS display:none" und "ROT13 Encryption" angewendet. Die CSS Methode kommt im `<noscript>` Tag zum Einsatz, falls JavaScript im Browser des Besuchers deaktiviert ist. | ||
| 6. __ROT13 JavaScript Verschlüsselung mit CSS Methode__ ⚠️ __Veraltet, unsicher__: Kombiniert ROT13-Verschlüsselung mit CSS-Fallback für deaktiviertes JavaScript. | ||
| __Bitte beachten__: Diese Methode macht alle E-Mailadresse ohne klickbaren Link klickbar! | ||
| __Bitte beachten__: diese Methode entfernt bei deaktiviertem JavaScript den mailto-Link und verwandelt Adresse in name[at]domain.tld. Die Adresse ist damit nicht mehr klickbar. | ||
|
|
||
| ### Unterstützung für mailto-Parameter | ||
|
|
||
| Alle Verschlüsselungsmethoden unterstützen jetzt vollständig mailto-Links mit Parametern: | ||
| - __Einfache Parameter__: `[email protected]?subject=Anfrage` | ||
| - __Mehrere Parameter__: `[email protected]?subject=Hilfe&body=Bitte_kontaktieren_Sie_mich` | ||
| - __URL-kodierte Parameter__: `[email protected]?subject=Anfrage%20f%C3%BCr%20ein%20U-Boot` | ||
|
|
||
| Die Parameter bleiben nach der Entschlüsselung vollständig erhalten und funktionsfähig. | ||
|
|
||
| ## Installation | ||
|
|
||
| Das Addon ist nach Aktivierung gleich funktionsfähig, und du brauchst keine weiteren Einstellungen vorzunehmen. Die benötigten Styles und Scripte werden automatisch geladen. | ||
| Das Addon ist nach Aktivierung gleich funktionsfähig und verwendet standardmäßig die sichere __XOR Verschlüsselung__. Du brauchst keine weiteren Einstellungen vorzunehmen. Die benötigten Styles und Scripte werden automatisch geladen. | ||
|
|
||
| ⚠️ __Wichtiger Sicherheitshinweis__: Falls du eine ältere Installation aktualisierst, die noch die veralteten ROT13-Methoden verwendet, solltest du in der Konfiguration auf eine der neuen XOR-Methoden wechseln, um die Sicherheit deiner E-Mailadressen zu gewährleisten. | ||
|
|
||
| Solltest du das benötigte CSS oder JavaScript manuell einbinden wollen, musst du in der Konfiguration das automatische Laden deaktivieren. | ||
|
|
||
| ### Keine jQuery-Abhängigkeit mehr | ||
|
|
||
| Ab Version 2.0 des Addons wird __kein jQuery mehr benötigt__. Das JavaScript verwendet moderne Vanilla-DOM-APIs und ist mit allen aktuellen Browsern kompatibel. Bestehende Installationen funktionieren weiterhin ohne Änderungen. | ||
|
|
||
| ### Hinweise zur __ROT13 Einhorn-Markup__ Methode: CSS und JavaScript manuell einbinden | ||
|
|
||
| Du kannst die Styles und Scripte auf zwei Arten einbinden: Entweder du lädst die Files, die das Addon bereitstellt, oder du kopierst deren Inhalte in deine bestehenden CSS- und JavaScript-Files. | ||
|
|
||
| #### a) Dateien laden | ||
| ### Automatische JavaScript-Einbindung | ||
|
|
||
| Das Addon lädt __automatisch__ das benötigte JavaScript für alle Verschlüsselungsmethoden, die eine clientseitige Entschlüsselung benötigen: | ||
|
|
||
| - XOR Verschlüsselung (einfach und dynamisch) | ||
| - ROT13 Einhorn-Markup | ||
| - ROT13 JavaScript Verschlüsselung | ||
| - ROT13 JavaScript mit CSS Fallback | ||
|
|
||
| Das JavaScript wird automatisch vor dem schließenden `</body>` Tag eingefügt. Du musst __nichts manuell einbinden__. | ||
|
|
||
| ### Hinweise zur manuellen Einbindung (optional) | ||
|
|
||
| Falls du das automatische Laden deaktivieren möchtest, kannst du die Styles und Scripte manuell einbinden. Du kannst die Files, die das Addon bereitstellt, laden oder deren Inhalte in deine bestehenden CSS- und JavaScript-Files kopieren. | ||
|
|
||
| #### a) Dateien manuell laden (falls automatisches Laden deaktiviert) | ||
|
|
||
| __CSS__ im `<head>` deiner Website einfügen: | ||
|
|
||
|
|
@@ -59,9 +94,9 @@ __JavaScript__ am besten am Ende deiner Website vorm schließenden `</body>` ein | |
| ?> | ||
| ``` | ||
|
|
||
| #### b) Inhalte kopieren | ||
| #### b) Inhalte in eigene Dateien kopieren (falls automatisches Laden deaktiviert) | ||
|
|
||
| Kopiere die Inhalte der CSS-Datei und der JS-Datei jeweils in deine Sourcen: | ||
| Falls du das automatische Laden deaktiviert hast, kannst du die Inhalte der CSS- und JS-Datei in deine eigenen Sourcen kopieren: | ||
|
|
||
| assets/emailobfuscator.css | ||
| assets/emailobfuscator.js | ||
|
|
@@ -70,6 +105,27 @@ Kopiere die Inhalte der CSS-Datei und der JS-Datei jeweils in deine Sourcen: | |
| Bei Variante a) oben ist dies nicht notwendig. | ||
|
|
||
|
|
||
| ## Sicherheit und Migration | ||
|
|
||
| ### Warum XOR statt ROT13? | ||
|
|
||
| Die neuen XOR-Verschlüsselungsmethoden bieten exponentiell bessere Sicherheit als die veralteten ROT13-Verfahren: | ||
|
|
||
| - __ROT13__ ist ein einfacher Caesar-Cipher, der von modernen Tools und Spam-Bots leicht automatisch geknackt werden kann | ||
| - __XOR-Verschlüsselung__ mit Base64-URL-Safe-Kodierung ist deutlich komplexer und widerstandsfähiger gegen automatisierte Angriffe | ||
| - __Dynamische Schlüssel__ (XOR dynamic) machen die Entschlüsselung ohne Kontext praktisch unmöglich | ||
|
|
||
| ### Migration von alten Methoden | ||
|
|
||
| Wenn du eine bestehende Installation verwendest: | ||
|
|
||
| 1. Gehe zur Email-Verschlüsselung Konfigurationsseite im REDAXO Backend | ||
| 2. Wähle __"XOR Verschlüsselung (Sicher, empfohlen)"__ für Standardsicherheit | ||
| 3. Oder wähle __"XOR Verschlüsselung mit dynamischem Schlüssel (Sehr sicher, empfohlen)"__ für maximale Sicherheit | ||
| 4. Speichere die Konfiguration | ||
|
|
||
| Keine Code-Änderungen, Template-Updates oder jQuery-Installation erforderlich. Alle bestehenden mailto-Links mit Parametern funktionieren sofort korrekt. | ||
|
|
||
| ## Sonstiges | ||
|
|
||
| ### Verschlüsselung bestimmter E-Mailadressen verhindern | ||
|
|
@@ -86,4 +142,21 @@ Bei Variante a) oben ist dies nicht notwendig. | |
|
|
||
| Das Addon filtert _alle_ E-Mailadressen im Code anhand eines Musters und verschlüsselt diese. In manchen Situationen ist das nicht unbedingt gewollt, z. B. wenn E-Mailadressen als HTML-Attribute oder in Formularen verwendet werden. Dort werden vom System natürlich die reinen, unverschlüsselten Adressen erwartet, und leider kann das Addon solche Umgebungen nicht eigenständig erkennen. | ||
|
|
||
| ⚠️ Beachte bitte, dass du in manchen Umgebungen die E-Mailverschlüsselung unterbinden solltest, entweder durch Ausschließen bestimmter Templates oder Artikel in der Konfiguration, oder aber durch ein manuelles Whitelisting von Adressen wie im Abschnitt oben beschrieben. | ||
| ⚠️ Beachte bitte, dass du in manchen Umgebungen die E-Mailverschlüsselung unterbinden solltest, entweder durch Ausschließen bestimmter Templates oder Artikel in der Konfiguration, oder aber durch ein manuelles Whitelisting von Adressen wie im Abschnitt oben beschrieben. | ||
|
|
||
| ### Unterstützung für komplexe mailto-Parameter | ||
|
|
||
| Das Addon unterstützt jetzt vollständig komplexe mailto-Links mit allen gängigen Parametern: | ||
|
|
||
| ```html | ||
| <!-- Einfache Betreff-Zeile --> | ||
| <a href="mailto:[email protected]?subject=Anfrage">Kontakt</a> | ||
|
|
||
| <!-- Betreff und Nachricht --> | ||
| <a href="mailto:[email protected]?subject=Hilfe%20benötigt&body=Bitte%20kontaktieren%20Sie%20mich">Support</a> | ||
|
|
||
| <!-- Mehrere Empfänger und Parameter --> | ||
| <a href="mailto:[email protected],[email protected]?subject=Angebot&[email protected]&body=Liebe%20Damen%20und%20Herren">Angebot anfordern</a> | ||
| ``` | ||
|
|
||
| Alle Parameter bleiben nach der Verschlüsselung und Entschlüsselung vollständig funktionsfähig. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,34 +1,123 @@ | ||
| window.onload = function () { | ||
| if (typeof jQuery !== 'undefined') { | ||
| $(function () { | ||
| decryptEmailaddresses(); | ||
| }); | ||
| } else { | ||
| console.warn('Email obfuscator addon requires jQuery.'); | ||
| } | ||
| }; | ||
| // Initialize when DOM is ready | ||
| if (document.readyState === 'loading') { | ||
| document.addEventListener('DOMContentLoaded', function() { | ||
| decryptEmailaddresses(); | ||
| deobfuscateXorEmails(); | ||
| }); | ||
| } else { | ||
| decryptEmailaddresses(); | ||
| deobfuscateXorEmails(); | ||
| } | ||
|
|
||
| function decryptEmailaddresses() { | ||
| // Ersetze E-Mailadressen | ||
| $('span.unicorn').each(function () { | ||
| $(this).replaceWith('@'); | ||
| var unicornSpans = document.querySelectorAll('span.unicorn'); | ||
| Array.prototype.forEach.call(unicornSpans, function(span) { | ||
| span.outerHTML = '@'; | ||
| }); | ||
|
|
||
| // Ersetze mailto-Links | ||
| $('a[href^="javascript:decryptUnicorn"]').each(function () { | ||
|
|
||
| var emailLinks = document.querySelectorAll('a[href^="javascript:decryptUnicorn"]'); | ||
| Array.prototype.forEach.call(emailLinks, function(link) { | ||
| // Selektiere Einhorn-Werte | ||
| var emails = $(this).attr('href').match(/\((.*)\)/)[1]; | ||
| var emails = link.href.match(/\((.*)\)/)[1]; | ||
|
|
||
| emails = emails | ||
| // ROT13-Transformation | ||
| .replace(/[a-z]/gi, function (s) { | ||
| return String.fromCharCode(s.charCodeAt(0) + (s.toLowerCase() < 'n' ? 13 : -13)); | ||
| }) | ||
| // Ersetze # durch @ | ||
| // Ersetze | durch @ | ||
| .replace(/\|/g, '@'); | ||
|
|
||
| // Ersetze Einhörner | ||
| $(this).attr('href', 'mailto:' + emails); | ||
| link.href = 'mailto:' + emails; | ||
| }); | ||
| } | ||
|
|
||
| // XOR decryption functions | ||
| function base64UrlDecode(str) { | ||
| str = (str + "===").slice(0, str.length + (str.length % 4)); | ||
| return atob(str.replace(/-/g, "+").replace(/_/g, "/")); | ||
| } | ||
|
|
||
| function xorDecrypt(encrypted, key) { | ||
| var result = ""; | ||
| for (var i = 0; i < encrypted.length; i++) { | ||
| result += String.fromCharCode(encrypted.charCodeAt(i) ^ key.charCodeAt(i % key.length)); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| function decryptEmailData(encryptedData, method, context) { | ||
| var key; | ||
| if (method === "xor-simple") { | ||
| key = "EmailObfuscatorKey2024"; | ||
| } else { | ||
| // Generate dynamic key using a simple hash | ||
| var baseKey = "EmailObfuscator"; | ||
| var fullString = baseKey + (context || ""); | ||
| var hash = 0; | ||
| for (var i = 0; i < fullString.length; i++) { | ||
| var char = fullString.charCodeAt(i); | ||
| hash = ((hash << 5) - hash) + char; | ||
| hash = hash >>> 0; // Convert to unsigned 32-bit integer | ||
| } | ||
| // Convert hash to hex and take first 16 characters | ||
| var hashHex = (hash >>> 0).toString(16); | ||
| key = (hashHex + hashHex + hashHex + hashHex).substring(0, 16); | ||
| } | ||
|
|
||
| var decoded = base64UrlDecode(encryptedData); | ||
| return xorDecrypt(decoded, key); | ||
| } | ||
|
|
||
| function deobfuscateXorEmails() { | ||
| var obfuscatedElements = document.querySelectorAll(".email-obfuscated"); | ||
|
|
||
| Array.prototype.forEach.call(obfuscatedElements, function(element) { | ||
| var method = element.getAttribute("data-method"); | ||
| var context = element.getAttribute("data-context") || ""; | ||
| var encryptedEmail = element.getAttribute("data-email"); | ||
| var encryptedText = element.getAttribute("data-text"); | ||
| var encryptedAttributes = element.getAttribute("data-attributes"); | ||
|
|
||
| try { | ||
| var email = decryptEmailData(encryptedEmail, method, context); | ||
| var text = decryptEmailData(encryptedText, method, context); | ||
| var attributes = encryptedAttributes ? decryptEmailData(encryptedAttributes, method, context) : ""; | ||
|
|
||
| var link = document.createElement("a"); | ||
| link.href = "mailto:" + email; | ||
| link.textContent = text; | ||
|
|
||
| if (attributes) { | ||
| // Simple attribute parsing | ||
| var attrParts = attributes.split(" "); | ||
| Array.prototype.forEach.call(attrParts, function(attr) { | ||
| var eqIndex = attr.indexOf("="); | ||
| if (eqIndex > 0) { | ||
| var attrName = attr.substring(0, eqIndex); | ||
| var attrValue = attr.substring(eqIndex + 1).replace(/["\']/g, ""); | ||
| if (attrName && attrValue && attrName !== "href") { | ||
| link.setAttribute(attrName, attrValue); | ||
| } | ||
| } | ||
skerbis marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // Robust attribute parsing using regex | ||
| var attrRegex = /([^\s=]+)\s*=\s*(['"])(.*?)\2|([^\s=]+)\s*=\s*([^\s"']+)/g; | ||
| var match; | ||
| while ((match = attrRegex.exec(attributes)) !== null) { | ||
| var attrName = match[1] || match[4]; | ||
| var attrValue = match[3] || match[5]; | ||
| if (attrName && attrValue && attrName !== "href") { | ||
| link.setAttribute(attrName, attrValue); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| element.parentNode.replaceChild(link, element); | ||
| } catch (e) { | ||
| console.warn("Failed to deobfuscate email:", e); | ||
| } | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hash calculation in JavaScript uses
hash >>> 0but the corresponding PHP code uses($hash << 32) >> 32. These operations are not equivalent and will produce different hash values, causing decryption to fail. The PHP code should use$hash & 0xFFFFFFFFto match the JavaScript behavior.Copilot uses AI. Check for mistakes.