protect-scr
Version:
Comprehensive client-side security protection for React applications against screenshots, printing, and unauthorized access
165 lines • 7.16 kB
JavaScript
export class ScreenshotProtector {
constructor(config) {
this.isActive = false;
this.eventListeners = [];
this.config = config;
}
start() {
if (this.isActive)
return;
this.isActive = true;
this.setupVisibilityDetection();
this.setupPrintScreenDetection();
this.setupMobileScreenshotDetection();
this.setupBrowserScreenshotDetection();
}
stop() {
if (!this.isActive)
return;
this.isActive = false;
this.eventListeners.forEach(removeListener => removeListener());
this.eventListeners = [];
// Remove blur effect if applied
document.body.style.filter = '';
}
setupVisibilityDetection() {
const visibilityHandler = () => {
if (document.hidden && this.config.blurOnFocusLoss) {
// Blur content when window loses focus (potential screenshot)
document.body.style.filter = 'blur(10px)';
document.body.style.transition = 'filter 0.1s';
}
else {
document.body.style.filter = '';
}
};
document.addEventListener('visibilitychange', visibilityHandler);
this.eventListeners.push(() => {
document.removeEventListener('visibilitychange', visibilityHandler);
});
// Also blur on window blur/focus
const blurHandler = () => {
var _a, _b;
if (this.config.blurOnFocusLoss) {
document.body.style.filter = 'blur(10px)';
}
(_b = (_a = this.config).onAttempt) === null || _b === void 0 ? void 0 : _b.call(_a);
};
const focusHandler = () => {
document.body.style.filter = '';
};
window.addEventListener('blur', blurHandler);
window.addEventListener('focus', focusHandler);
this.eventListeners.push(() => {
window.removeEventListener('blur', blurHandler);
window.removeEventListener('focus', focusHandler);
});
}
setupPrintScreenDetection() {
const keyHandler = (e) => {
var _a, _b, _c, _d;
// Detect Print Screen key
if (e.key === 'PrintScreen' || e.keyCode === 44) {
e.preventDefault();
e.stopPropagation();
// Clear clipboard to prevent screenshot saving
if (navigator.clipboard) {
navigator.clipboard.writeText('').catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = '';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
});
}
(_b = (_a = this.config).onAttempt) === null || _b === void 0 ? void 0 : _b.call(_a);
alert('Screenshots are not allowed for security reasons.');
return false;
}
// Detect Alt+Print Screen (active window screenshot)
if (e.altKey && (e.key === 'PrintScreen' || e.keyCode === 44)) {
e.preventDefault();
e.stopPropagation();
(_d = (_c = this.config).onAttempt) === null || _d === void 0 ? void 0 : _d.call(_c);
alert('Screenshots are not allowed for security reasons.');
return false;
}
};
document.addEventListener('keydown', keyHandler, true);
document.addEventListener('keyup', keyHandler, true);
this.eventListeners.push(() => {
document.removeEventListener('keydown', keyHandler, true);
document.removeEventListener('keyup', keyHandler, true);
});
}
setupMobileScreenshotDetection() {
// Detect mobile screenshot gestures (iOS and Android)
let touchStartTime = 0;
let simultaneousTouch = false;
const touchStartHandler = (e) => {
touchStartTime = Date.now();
if (e.touches.length > 1) {
simultaneousTouch = true;
}
};
const touchEndHandler = (e) => {
var _a, _b;
const touchDuration = Date.now() - touchStartTime;
// iOS screenshot: Home + Power button (simultaneous touch + quick release)
// Android screenshot: Power + Volume Down (similar pattern)
if (simultaneousTouch && touchDuration < 200) {
(_b = (_a = this.config).onAttempt) === null || _b === void 0 ? void 0 : _b.call(_a);
// Blur content briefly
if (this.config.blurOnFocusLoss) {
document.body.style.filter = 'blur(20px)';
setTimeout(() => {
document.body.style.filter = '';
}, 1000);
}
}
simultaneousTouch = false;
};
document.addEventListener('touchstart', touchStartHandler, { passive: true });
document.addEventListener('touchend', touchEndHandler, { passive: true });
this.eventListeners.push(() => {
document.removeEventListener('touchstart', touchStartHandler);
document.removeEventListener('touchend', touchEndHandler);
});
}
setupBrowserScreenshotDetection() {
// Detect browser screenshot extensions and tools
const mediaHandler = (e) => {
var _a, _b;
// Detect screen capture API usage
e.preventDefault();
e.stopPropagation();
(_b = (_a = this.config).onAttempt) === null || _b === void 0 ? void 0 : _b.call(_a);
return false;
};
// Block getUserMedia access that might be used for screen capture
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
const originalGetDisplayMedia = navigator.mediaDevices.getDisplayMedia;
navigator.mediaDevices.getDisplayMedia = function () {
throw new Error('Screen capture is not allowed');
};
}
// Detect canvas toDataURL calls (used by many screenshot tools)
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function () {
throw new Error('Canvas data extraction is not allowed');
};
// Detect canvas toBlob calls
const originalToBlob = HTMLCanvasElement.prototype.toBlob;
HTMLCanvasElement.prototype.toBlob = function () {
throw new Error('Canvas data extraction is not allowed');
};
// Restore original methods on stop
this.eventListeners.push(() => {
HTMLCanvasElement.prototype.toDataURL = originalToDataURL;
HTMLCanvasElement.prototype.toBlob = originalToBlob;
});
}
}
//# sourceMappingURL=ScreenshotProtector.js.map