diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2012-03-09 01:58:19 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2012-03-09 02:33:11 +0100 |
commit | 3f6797c21bb4c967266ca2227111409e8f069692 (patch) | |
tree | 9f4c486a31a73f82b8a52fc08391fc0f4e3eb460 | |
download | secure.js-master.tar.xz secure.js-master.zip |
-rw-r--r-- | README.txt | 25 | ||||
-rw-r--r-- | secure.js | 60 | ||||
-rw-r--r-- | test.html | 17 |
3 files changed, 102 insertions, 0 deletions
diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..9450028 --- /dev/null +++ b/README.txt @@ -0,0 +1,25 @@ +============================= += secure.js = += = += by zx2c4 = += Jason Donenfeld = += Jason@zx2c4.com = +============================= + +Sometimes you want to provide a javascript service to somebody, +but you only want it to run in an HTTPS context, because the +information that your script will help gather from the page is +somewhat sensitive. + +One way of enforcing HTTPS is to check that location.protocol is +"https:", but this is extremely limited becuase it does not +account for the possibility that your script may be included +along side other scripts from other servers that are not loaded +over HTTPS. When this happens, the security of your site is +defeated, and man-in-the-middle attacks become practical. + +secure.js solves this issue by monitoring the DOM for changes and +continiously checking whether or not any external resources have +been added that are non-HTTPS. + +Suggestions and improvements are welcome. diff --git a/secure.js b/secure.js new file mode 100644 index 0000000..c420811 --- /dev/null +++ b/secure.js @@ -0,0 +1,60 @@ +(function() { + var secureJS = { + destroyPage: function() { + this.checkAndDestroy = function() {}; + if (window.stop) + window.stop(); + //TODO: Figure out how to halt all existing concurrent and async scripts, and clear all timers. + alert("This is a nasty alert box. Aren't JavaScript alerts so 1995? Yes, and so is plaintext HTTP.\n\nYou screwed up; your page isn't secure. Get it together. Fix things."); + window.location = "https://en.wikipedia.org/wiki/HTTP_Secure"; + throw "Insecure site."; + }, + checkPageProtocol: function() { + return window.location.protocol == "https:"; + }, + checkCollectionProtocol: function(collection, attribute) { + for (var i = 0; i < collection.length; ++i) { + var location = collection[i].getAttribute(attribute); + if (location == null) + continue; + // I actually have never read the URI RFC, so I'm sure there are special + // cases that really should be taken into account that I neglect here. + //TODO: Don't be a fool; do this properly. + var firstSlash = location.indexOf("/"); + var firstDot = location.indexOf("/"); + var protocolDelim = location.indexOf("://"); + if (protocolDelim != -1 && + (firstSlash == -1 || protocolDelim < firstSlash) && + (firstDot == -1 || protocolDelim < firstDot) && + location.indexOf("https") != 0) + return false; + } + return true; + }, + isPageSecure: function() { + return this.checkPageProtocol() && + this.checkCollectionProtocol(document.getElementsByTagName("script"), "src") && + this.checkCollectionProtocol(document.getElementsByTagName("link"), "rel"); + //TODO: What else is missing here? Embeds, objects, applets, probably a bunch of other info leaks. + // It might be nice to check that document.cookie is empty too, to check enforcement of httpOnly flag. + }, + checkAndDestroy: function() { + if (!this.isPageSecure()) + this.destroyPage(); + } + }; + + document.addEventListener("DOMContentLoaded", function() { secureJS.checkAndDestroy(); }, true); + document.addEventListener("DOMNodeInserted", function() { secureJS.checkAndDestroy(); }, true); + document.addEventListener("DOMNodeInsertedIntoDocument", function() { secureJS.checkAndDestroy(); }, true); + document.addEventListener("DOMAttrModified", function() { secureJS.checkAndDestroy(); }, true); + document.addEventListener("DOMElementNameChanged", function() { secureJS.checkAndDestroy(); }, true); + document.addEventListener("DOMContentLoaded", function() { secureJS.checkAndDestroy(); }, true); + var timer = function() { + secureJS.checkAndDestroy(); + //TODO: 500ms? Good? Bad? Polling this much or even at all isn't strictly neccessary, + // because of DOMNodeInserted, but just to be safe... What do you think? + window.setTimeout(timer, 500); + }; + timer(); +})(); diff --git a/test.html b/test.html new file mode 100644 index 0000000..0216f53 --- /dev/null +++ b/test.html @@ -0,0 +1,17 @@ +<html> +<head> +<script src="secure.js"></script> +<script> +setTimeout(function() { + alert("bout to insert"); + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'http://www.zx2c4.com/somescript.js'; + document.getElementsByTagName("head")[0].appendChild(script) +}, 3000); +</script> +</head> +<body> +Will you see this text? +</body> +</html> |