JavaScript-Zugangsschutz für Spaßvögel
In den Weiten des Netzes stößt man immer wieder auf kreative Seitenbetreiber, die ihre Netzseiten mit Hilfe von JS-Skripten gegen fremden Zugriff schützen wollen. Die meisten Lösungen fallen unter "security by obscurity", haben also ein Sicherheitsniveau knapp über dem direkten Anzeigen der "versteckten" Inhalte, aber weit unter dem eines Kaugummiautomaten.
Im einfachsten Fall steht das Paßwort oder die gesuchte Seite
direkt im
Quelltext oder es hilft ein alert();
an der richtigen Stelle, um
Sicherheitvorgaukelfunktionen zu umgehen. Nur in wenigen Fällen muß man wirklich
nachdenken und Algorithmen nachvollziehen, um an die gewünschten Daten zu
gelangen. Schade eigentlich.
Am häufigsten wird aus dem Paßwort der Name der "versteckten" Seite generiert. Wobei "generiert" oftmals stark übertrieben ist und das Paßwort einfach als Seitenadresse wiederverwendet wird. Es muß wohl am proprietären JScript-Encode, zu dem es natürlich ein passendes Decode gibt, liegen, daß es zu den sichersten Varianten gehört:
Description: NoPass is a password security system that can be used to protect web pages and files. As it is impossible to obtain the password from the source code, NoPass is very secure for a JavaScript of its kind. Possibly the most secure JavaScript password protection available. [...]
Mit ein paar unnützen Sicherheitvorgaukel- bzw. Quelltextfüllfunktionen kann man solch einen Quatsch auch für echtes(!) Geld (ver-)kaufen.
Vor kurzem1 bin ich allerdings auf ein besonders niedliches Exemplar (Site Protector 5.x)2 gestoßen3, das auf der Autorenseite mit:
How well are the pages protected?
They aren't completely protected because it's only client-side javaScript-Protection. But it's still very well protected in the sense that everything is encrypted and the password isn't included directly. Many people have tried to break this protection - all have failed :-)
beworben wird. Der Smiley deutet schon in die richtige Richtung.
Netterweise gibt es gleich eine Demoseite, um sich den Quelltext anzusehen:
<SCRIPT LANGUAGE="JavaScript"><!-- var sp_trys = 3;var sp_alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-:/._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";var sp_a1=new Array(); var sp_a2=new Array(); sp_a1[0]=11; sp_a2[0]="_h.h_gDxnypwfwuB.mfvpB.ysxundCfm_quvm"; function sp() {var pass="";var revisit=false;if (! revisit) {pass = prompt("Please enter your password.","");}if(pass==null || pass=="") {location.href='../../Components/demos/access.html';}else {if (revisit) {h1=pass.substring(0,pass.indexOf("|"));h2=pass.substring(pass.indexOf("|")+1);} else {h1=sp_makehash(pass,3); h2=sp_makehash(pass,10)+" ";}for (var pos=0;pos<sp_a1.length;pos++) if (sp_a1[pos]==h1) break;if (pos==sp_a1.length) {sp_trys--;if (sp_trys > 0) {if (confirm("\nThe password is invalid. Try again?")) sp();else location.href='../../Components/demos/access.html';} else location.href='../../Components/demos/access.html';return;}var page=""; var hp=0;for (var i=0;i<sp_a2[pos].length;i++) {letter=sp_a2[pos].substring(i,i+1);a=sp_alpha.indexOf(letter,0);if (a>=0) {a-=(h2.substring(hp,hp+1)*1);hp++; if (hp==h2.length-1) hp=0;if (a<0) a+=68;page+=sp_alpha.substring(a,a+1);} else { page+=letter; }}location.href=page;}} function sp_makehash(pw,mult) {hash=0;for (i=0;i<pw.length;i++) {letter=pw.substring(i,i+1);c=sp_alpha.indexOf(letter,0)+1;hash=(hash*mult+c)%27;}return(hash);} //SiteProtector 5.x by Ingo Fischer (http://www.apollon.de) --></SCRIPT>
Nach dem Formatieren mit vim:
<script type="text/javascript"> var sp_trys = 3; var sp_alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-:/._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var sp_a1=new Array(); var sp_a2=new Array(); sp_a1[0]=11; sp_a2[0]="_h.h_gDxnypwfwuB.mfvpB.ysxundCfm_quvm"; function sp() { var pass=""; var revisit=false; if (! revisit) { pass = prompt("Please enter your password.",""); } if(pass==null || pass=="") { location.href='../../Components/demos/access.html'; } else { if (revisit) { h1=pass.substring(0,pass.indexOf("|")); h2=pass.substring(pass.indexOf("|")+1); } else { h1=sp_makehash(pass,3); h2=sp_makehash(pass,10)+" "; } for (var pos=0;pos<sp_a1.length;pos++) if (sp_a1[pos]==h1) break; if (pos==sp_a1.length) { sp_trys--; if (sp_trys > 0) { if (confirm("\nThe password is invalid. Try again?")) sp(); else location.href='../../Components/demos/access.html'; } else location.href='../../Components/demos/access.html';return; } var page=""; var hp=0; for (var i=0;i<sp_a2[pos].length;i++) { letter=sp_a2[pos].substring(i,i+1); a=sp_alpha.indexOf(letter,0); if (a>=0) { a-=(h2.substring(hp,hp+1)*1); hp++; if (hp==h2.length-1) hp=0; if (a<0) a+=68; page+=sp_alpha.substring(a,a+1); } else { page+=letter; } } location.href=page; } } function sp_makehash(pw,mult) { hash=0; for (i=0;i<pw.length;i++) { letter=pw.substring(i,i+1); c=sp_alpha.indexOf(letter,0)+1; hash=(hash*mult+c)%27; } return(hash); } //SiteProtector 5.x by Ingo Fischer (http://www.apollon.de) --> </script>
Wenn man sich die Variableninitialisierung in der sp()
-Funktion
anschaut und dann alles wegwirft, was man in der Funktion nicht braucht, erhält
man:
function sp() { h1=sp_makehash(pass,3); h2=sp_makehash(pass,10)+" "; for (var pos=0;pos<sp_a1.length;pos++) if (sp_a1[pos]==h1) break; var page=""; var hp=0; for (var i=0;i<sp_a2[pos].length;i++) { letter=sp_a2[pos].substring(i,i+1); a=sp_alpha.indexOf(letter,0); if (a>=0) { a-=(h2.substring(hp,hp+1)*1); hp++; if (hp==h2.length-1) hp=0; if (a<0) a+=68; page+=sp_alpha.substring(a,a+1); } else { page+=letter; } } location.href=page; }
Die for
-Schleife wird wohl benutzt, um mehrere Seiten mit
verschiedenen Kennwörtern zu versehen. Die Hash-Werte(!) der Paßwörter dienen
über pos
als Index für die "verschlüsselten" Seitennamen.
In diesem Fall gibt es allerdings nur eine Seite, also können wir
pos
mit 0
initialisieren und h1
links
liegen lassen.
Wenigstens hat der Autor nicht die Klartexte verwendet und die Hash-Berechnung über den zweiten Parameter verändert.
Wir schreiben um:
function sp() { h2=sp_makehash(pass,10)+" "; pos=0; var page=""; var hp=0; for (var i=0;i<sp_a2[pos].length;i++) { letter=sp_a2[pos].substring(i,i+1); a=sp_alpha.indexOf(letter,0); if (a>=0) { a-=(h2.substring(hp,hp+1)*1); hp++; if (hp==h2.length-1) hp=0; if (a<0) a+=68; page+=sp_alpha.substring(a,a+1); } else { page+=letter; } } location.href=page; }
Kennwortverkrüppeln
An dieser Stelle wird es im Normalfall etwas haarig, allerdings hilft uns der Autor weiter. Will er etwa selbst Zugang zu den Seiten, wird er von Geheimdiensten bezahlt oder implementiert er gar Abhörvorgaben der Regierung? Die Hash-Funktion erweist sich als vollkommen ungenügend. Das Modulo am Ende sorgt dafür, daß nur 27 verschiedene Werte zurückgegeben werden. Wer also "Heinz" und "Karin" als Kennwörter wählt, kann wegen des obigen Hash-Index keine zwei verschiedenen Seiten pseudoschützen. Egal, wie gut man das Paßwort wählt, es wird immer auf maximal zwei Stellen gestutzt.
function sp_makehash(pw,mult) { hash=0; for (i=0;i<pw.length;i++) { letter=pw.substring(i,i+1); c=sp_alpha.indexOf(letter,0)+1; hash=(hash*mult+c)%27; } return(hash); }
Lassen wir uns doch einfach alle möglichen Werte zurückgeben:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" /> <script type="text/javascript"> var sp_alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-:/._abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; var sp_a1=new Array(); var sp_a2=new Array(); sp_a1[0]=11; sp_a2[0]="_h.h_gDxnypwfwuB.mfvpB.ysxundCfm_quvm"; var siteurl="http://www.apollon.de/Components/demos/"; function sp() { var h2=""; var pos=0; for (var th2=0;th2<27;th2++) { var page=""; var hp=0; h2 = th2 + " "; for (var i=0;i<sp_a2[pos].length;i++) { letter=sp_a2[pos].substring(i,i+1); a=sp_alpha.indexOf(letter,0); if (a>=0) { a-=(h2.substring(hp,hp+1)*1); hp++; if (hp==h2.length-1) hp=0; if (a<0) a+=68; page+=sp_alpha.substring(a,a+1); } else { page+=letter; } } document.write(th2+'. <a href="'+siteurl+page+'">'+siteurl+page+'</a><br />'); } } sp(); //SiteProtector 5.x by Ingo Fischer (http://www.apollon.de) </script> </head><body></body></html>
Das liefert uns:
0. http://www.apollon.de/Components/demos/_h.h_gDxnypwfwuB.mfvpB.ysxundCfm_quvm 1. http://www.apollon.de/Components/demos/.g/g.fCwmxovevtA/leuoA/xrwtmcBel.ptul 2. http://www.apollon.de/Components/demos//f:f/eBvlwnudusz:kdtnz:wqvslbAdk/ostk 3. http://www.apollon.de/Components/demos/:e-e:dAukvmtctry-jcsmy-vpurkazcj:nrsj 4. http://www.apollon.de/Components/demos/-d9d-cztjulsbsqx9ibrlx9uotqj_ybi-mqri 5. http://www.apollon.de/Components/demos/9c8c9bysitkrarpw8haqkw8tnspi.xah9lpqh 6. http://www.apollon.de/Components/demos/8b7b8axrhsjq_qov7g_pjv7smroh/w_g8kopg 7. http://www.apollon.de/Components/demos/7a6a7_wqgrip.pnu6f.oiu6rlqng:v.f7jnof 8. http://www.apollon.de/Components/demos/6_5_6.vpfqho/omt5e/nht5qkpmf-u/e6imne 9. http://www.apollon.de/Components/demos/5.4.5/uoepgn:nls4d:mgs4pjole9t:d5hlmd 10. http://www.apollon.de/Components/demos/.h/h.gCxmyowewtB/mevoB/yrxtncCem.qtvl 11. http://www.apollon.de/Components/demos/.g/g.fCwmxovevtA/leuoA/xrwtmcBel.ptul 12. http://www.apollon.de/Components/demos/.f/f.eCvmwoueutz/ketoz/wrvtlcAek.ottl 13. http://www.apollon.de/Components/demos/.e/e.dCumvotetty/jesoy/vrutkczej.ntsl 14. http://www.apollon.de/Components/demos/.d/d.cCtmuosestx/ierox/urttjcyei.mtrl 15. http://www.apollon.de/Components/demos/.c/c.bCsmtorertw/heqow/trsticxeh.ltql 16. http://www.apollon.de/Components/demos/.b/b.aCrmsoqeqtv/gepov/srrthcweg.ktpl 17. http://www.apollon.de/Components/demos/.a/a._Cqmropeptu/feoou/rrqtgcvef.jtol 18. http://www.apollon.de/Components/demos/._/_..Cpmqooeott/eenot/qrptfcuee.itnl 19. http://www.apollon.de/Components/demos/../../Components/demos/protected.html 20. http://www.apollon.de/Components/demos//h:h/gBxlynwdwsB:mdvnB:yqxsnbCdm/qsvk 21. http://www.apollon.de/Components/demos//g:g/fBwlxnvdvsA:ldunA:xqwsmbBdl/psuk 22. http://www.apollon.de/Components/demos//f:f/eBvlwnudusz:kdtnz:wqvslbAdk/ostk 23. http://www.apollon.de/Components/demos//e:e/dBulvntdtsy:jdsny:vquskbzdj/nssk 24. http://www.apollon.de/Components/demos//d:d/cBtlunsdssx:idrnx:uqtsjbydi/msrk 25. http://www.apollon.de/Components/demos//c:c/bBsltnrdrsw:hdqnw:tqssibxdh/lsqk 26. http://www.apollon.de/Components/demos//b:b/aBrlsnqdqsv:gdpnv:sqrshbwdg/kspk
Eine der anderen Varianten wäre besser gewesen...
In allen Fällen, die ich gesehen habe, war der url lesbar, fing mit "../../" an und endete mit ".htm[l]" - den NOF-Webdesignern sei Dank. Allerdings können die Bezeichner im Skript variieren.
Paßwort gefällig?
Die Zeilennummer in unserer Ausgabe gibt den zweiten Hash-Wert
h2
an. Damit haben wir alles, was unser Paßwort ausmacht - zwei
maximal zweistellige Hash-Werte (bzw. einer, wenn man nur die Seite wissen
will).
Einfach eine beliebige lange Liste nehmen, die Hash-Paare berechnen, sortieren:
h2,h1 (Kennwort) --+--+----------------- 0, 0 (Wunschtroll) [...] 19,11 (Bananenhure) 19,11 (Ikonoklast) 19,11 (Luegenschwein) 19,11 (Promillepapst) 19,11 (Schnepfe) 19,11 (Schwabbelhure) [...] 26,26 (Zwiederwurzen)
und ein passendes Paar herausnehmen. Ein (11,19)-Paar wäre "Schnepfe", also brauchen wir auch die Lösung "apollon" von der Demoseite nicht mehr. Obwohl - haben wir die jemals gebraucht? ;)
Anmerkung
Falls Ihr doch noch Lust habt und Euch trotz Lösung den Algorithmus genauer ansehen wollt, achtet mal darauf, wie sehr sich die Verschlüsselungsqualität in Abhängigkeit vom Paßwort ändert oder wie ein "?" kodiert wird. (Wenn man hier überhaupt von Verschlüsselung sprechen kann.)
(0,0)-Kennwort "Wunschtroll":
site : ../../Components/demos/protected.html crypt: ../../Components/demos/protected.html
- Man möge mir verzeihen, daß ich mit den html-uglifiern wie Fusion nicht vertraut bin. Ich mache das per Hand. ;)
- Dieses "Programm" wurde vor Jahren im Zusammenhang mit Netobjects Fusion verwendet und wird heute nicht mehr weiterentwickelt. Einige private und Vereinsseiten nutzen heute noch diesen "Schutz", was an der Werbung auf diversen Supportseiten liegen kann. Einen Fehlerbericht gab es anscheinend nie.
- Jeder hat so seine Freuden - andere schreiben Zahlen in 9x9-Gitter.