Matthias: AJAX Kennwortüberprüfung von http über ein Remotescript zu https

Hallo zusammen!
Ich habe mal eine kleine Frage zu der ich mir einen Lösungsansatz überlegt habe, den ich aber nicht 100% überzeugend finde.

Folgende (häufig auftretende) Sachlage.
Ich bastel gerade an einer Seite, bei der sich Mitglieder anmelden können. Die Seite soll, damit es flotter geht, ungesichert - also über http laufen. Die Anmeldung soll aber natürlich über eine gesicherte Verbindung laufen, also https.
Der normale Weg - also über Formular - ist natürlich kein Problem.
Ich möchte diesen Punkt aber - wie den Rest der Seite auch - komplett über AJAX steuern, das Formular wird also nur als Fallback beibehalten.

So einfach geht das mit AJAX ja aber nicht. Darum kam mir folgende Idee:
Ich lege mir in der https-Umgebung eine ASP-Seite an, der ich den Content-Type text/javascript mitgebe und das Caching verbiete.
Diese ASP-Seite rufe ich von der http-Seite aus dynamisch auf, etwa so:

function doValidate(usr,pwd){  
var ajaxScript = document.createElement('script');  
ajaxScript.type = 'text/javascript';  
ajaxScript.src = 'https://sichererServer/script.asp?usr=' + encodeURIComponent(usr) + '&pwd=' + encodeURIComponent(pwd);  
document.getElementsByTagName("head")[0].appendChild(ajaxScript);  
}

In der aufgerufenen ASP-Datei prüfe ich zunächst den HTTP_REFERER, der in dem Moment ja meine http-Seite sein muß. Wenn das stimmt, dann prüfe ich die übergebenen Daten und (wenn die Daten passen) generiere den passenden Javascript-Code, der u.A. die Benutzerdaten auf der Seite einträgt.

Nun bin ich mir nicht sicher ob

  • die Daten die ich sende auch schon verschlüsselt sind. Normalerweise - wenn ich es richtig verstanden habe - sind sie es.
  • das die beste Art und Weise ist, dieses Problem zu lösen. Vielleicht hat jemand eine viel einfachere Idee...

Vielen Dank
Matthias

  1. Lieber Matthias,

    In der aufgerufenen ASP-Datei prüfe ich zunächst den HTTP_REFERER, der in dem Moment ja meine http-Seite sein muß.

    nö, muss er nicht (manipulier- oder deaktivierbar). Ende Deines Konzeptes.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Hallo Felix!
      Das ist schon klar. Den Referer prüfe ich aber eigentlich immer. Damit fange ich schon mal einiges ab. Zusätzlich wird bei 3 Fehlversuchen  innerhalb 15 Minuten die IP-Adresse für 60 Minuten gesperrt.
      Das das nicht "die Sicherheit" ist ist klar, aber es ist besser als gar nichts - und mir fällt nichts besseres ein.

      Die wichtigere Frage ist aber, was passiert mit dem Script in https? Sind die per get übermittelten Daten (und natürlich das, was das Script zurückgibt) schon verschlüsselt, oder nicht?

      Gruß
      Matthias

      Lieber Matthias,

      In der aufgerufenen ASP-Datei prüfe ich zunächst den HTTP_REFERER, der in dem Moment ja meine http-Seite sein muß.

      nö, muss er nicht (manipulier- oder deaktivierbar). Ende Deines Konzeptes.

      Liebe Grüße,

      Felix Riesterer.

      1. Hi,

        bitte zitiere sinnvoll, kein TOFU!

        Das ist schon klar. Den Referer prüfe ich aber eigentlich immer. Damit fange ich schon mal einiges ab.

        Den Quark wuerde ich mir sparen.
        Wenn ich wo "angreifen" wollte, dann wuerde ich als erstes dafuer sorgen, den "erwarteten" Referrer mitzusenden - eben, um solche Pseudo-Sperren zu umgehen.

        Zusätzlich wird bei 3 Fehlversuchen  innerhalb 15 Minuten die IP-Adresse für 60 Minuten gesperrt.

        Holsch' mir halt 'ne neue.

        Die wichtigere Frage ist aber, was passiert mit dem Script in https? Sind die per get übermittelten Daten (und natürlich das, was das Script zurückgibt) schon verschlüsselt, oder nicht?

        Client und Server handeln die sichere Verbindung aus, noch bevor GET ins Spiel kommt.

        MfG ChrisB

        --
        „This is the author's opinion, not necessarily that of Starbucks.“
        1. Hey Chris!

          Wenn ich wo "angreifen" wollte, dann wuerde ich als erstes dafuer sorgen, den "erwarteten" Referrer mitzusenden - eben, um solche Pseudo-Sperren zu umgehen.

          Es schadet aber auch nicht, oder?

          Zusätzlich wird bei 3 Fehlversuchen  innerhalb 15 Minuten die IP-Adresse für 60 Minuten gesperrt.

          Holsch' mir halt 'ne neue.

          Da hast du allerdings nicht ganz unrecht. Liegt aber wohl auch daran, dass ich den Satz nur halb geschrieben habe ;-) Ich sperre die IP-Adresse UND den Benutzernamen. Damit umschiffe ich Brute-Force-Angriffe recht gut.
          Die Kennwörter werden automatisch generiert (Zahlen, Buchstaben (groß und klein) sowie einige Sonderzeichen, 8-11stellig).
          Desweiteren bekommt der Benutzer keine Nachricht darüber, dass er gesperrt wurde. Es gibt immer, egal ob der Benutzername bzw. das Kennwort falsch sind bzw. das Konto / die IP-Adresse gesperrt ist nur die Nachricht "Anmeldeinformationen können nicht validiert werden. Bitte wenden sie sich an ..."

          Client und Server handeln die sichere Verbindung aus, noch bevor GET ins Spiel kommt.

          Na, dann lag ich doch relativ richtig! Damit ist meine eigentliche Kennwortübergabe ja schier :-)

          Vielen Dank - auch für die Hinweise bezüglich der Rahmenbedingungen!
          Ich werde mir auf jeden Fall nochmal Gedanken über die Referer etc. machen.

          Gruß
          Matthias

          1. Moin Moin!

            Ich sperre die IP-Adresse UND den Benutzernamen. Damit umschiffe ich Brute-Force-Angriffe recht gut.

            Das wird deine User freuen. Insbesondere wenn jemand auf die Idee kommt, einfach alle möglichen Namen durchzuprobieren.

            Dein Konzept ist extrem wackelig. Bist Du sicher, dass AJAX bei allen Benutzern funktioniert? Und hältst Du es wirklich für sinnvoll, Benutzernamen und Password per GET-Request zu übertragen? Spätestens wenn irgendjemand an Dein Access-Log herankommt, hast Du ein riesiges Problem.

            Hast Du übrigens mal nachgemessen, wie viel langsamer HTTPS gegenüber HTTP ist?

            Alexander

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
            1. Hallo Alexander

              Ich sperre die IP-Adresse UND den Benutzernamen. Damit umschiffe ich Brute-Force-Angriffe recht gut.

              Das wird deine User freuen. Insbesondere wenn jemand auf die Idee kommt, einfach alle möglichen Namen durchzuprobieren.

              Das Problem ist mir bewusst - wobei es natürlich etwas gelindert wird. Da der "jemand" keine Meldung bekommt und nach 3 Versuchen die IP-Adresse gesperrt wird kann er ja kein weiteres Konto angreifen - außer er ändert "auf Verdacht" seine IP-Adresse. Ich werde mir da aber noch verstärkt Gedanken drüber machen müssen :-(

              Dein Konzept ist extrem wackelig. Bist Du sicher, dass AJAX bei allen Benutzern funktioniert? Und hältst Du es wirklich für sinnvoll, Benutzernamen und Password per GET-Request zu übertragen? Spätestens wenn irgendjemand an Dein Access-Log herankommt, hast Du ein riesiges Problem.

              Das ist kein Problem. Der Fallback (falls kein AJAX geht) funktioniert über POST. Das Passwort wird allerdings nur als Hash übertragen. Das alleine bringt natürlich nicht viel ;-), darum habe ich da folgendes Konzept.
              Beim Ausführen der Abfrage ermittle ich zuerst die Client-IP-Adresse. Dann nehme ich das Kennwort + die IP und bilde daraus einen MD5. Den übertrage ich an den Server.
              Das übertragene Kennwort ist also an die aktuelle IP-Adresse gebunden.
              Natürlich hätte ich es lieber über POST, aber dann muß ich weg von AJAX.

              Mit den Log-Dateien habe ich keine Probleme. Ist ein eigener Server. Die Protokolle gehen in einen SQL Server. Da kann ich direkt per Trigger die Query´s ausfiltern.

              Hast Du übrigens mal nachgemessen, wie viel langsamer HTTPS gegenüber HTTP ist?

              Ich muß gestehen - nein. Habe gerade mal gegoogelt. Da gehen die Meinungen etwas auseinander - zwischen "etwas langsamer" bis 1:5.
              Nebenbei laufen allerdings mehrere Webseiten auf dem Server, die allerdings grob zu einem Projekt gehören. Um nun nicht für jede Seite ein Zertifikat zu kaufen nehme ich hier eines für einen Host mit einem allgemeingültigen Namen und lasse die jeweiligen Bereiche in virtuellen Verzeichnisen laufen.

              Gruß
              Matthias

              1. Hi,

                Das ist kein Problem. Der Fallback (falls kein AJAX geht) funktioniert über POST. Das Passwort wird allerdings nur als Hash übertragen. Das alleine bringt natürlich nicht viel ;-), darum habe ich da folgendes Konzept.
                Beim Ausführen der Abfrage ermittle ich zuerst die Client-IP-Adresse. Dann nehme ich das Kennwort + die IP und bilde daraus einen MD5. Den übertrage ich an den Server.

                Das setzt also wieder mindestens JavaScript voraus.

                Das übertragene Kennwort ist also an die aktuelle IP-Adresse gebunden.

                Das ist auch bloedsinnig - die kann von Request zu Request unterschiedlich sein.

                MfG ChrisB

                --
                „This is the author's opinion, not necessarily that of Starbucks.“
                1. Hallo Chris

                  Das ist kein Problem. Der Fallback (falls kein AJAX geht) funktioniert über POST. Das Passwort wird allerdings nur als Hash übertragen. Das alleine bringt natürlich nicht viel ;-), darum habe ich da folgendes Konzept.
                  Beim Ausführen der Abfrage ermittle ich zuerst die Client-IP-Adresse. Dann nehme ich das Kennwort + die IP und bilde daraus einen MD5. Den übertrage ich an den Server.

                  Das setzt also wieder mindestens JavaScript voraus.

                  Richtig, aber das stellt ja kein Problem dar. Wenn kein Javascript aktiviert ist greift der Fallback über Post. Und da sende ich das Passwort - notgedrungen - in Klartext (naja, über https :-)).

                  Das übertragene Kennwort ist also an die aktuelle IP-Adresse gebunden.

                  Das ist auch bloedsinnig - die kann von Request zu Request unterschiedlich sein.

                  Da die Abfrage der IP erst nach dem klicken auf den Senden-Button ausgeführt wird ist die Zeitdifferenz zwischen den beiden Requests sehr kurz. Natürlich schließt das die Möglichkeit nicht aus, dass die IP-Adresse genau in dieser Millisekunde wechselt - aber das wird mit an Sicherheit grenzender Wahrscheinlichkeit selten bis nie auftreten.

                  Ich möchte nur eine möglichst sichere Lösung erstellen, die auf den üblichen Browsern funktioniert.
                  Das schönste wäre natürlich ein Post über AJAX und ein normaler Formular-Fallback. Aber da spielt die same origin policy ja nicht mit.

                  Es will mir auch nicht klar werden, warum Protokoll- und Portänderungen dabei verboten sind. Wäre nur ein wechsel der Domain verboten wäre es meiner Meinung nach nicht wirklich unsicherer - dafür aber um vieles einfacher einzusetzen. Naja, leider ist es aber so...

                  Gruß
                  Matthias

                  1. Hi,

                    Das ist auch bloedsinnig - die kann von Request zu Request unterschiedlich sein.
                    Da die Abfrage der IP erst nach dem klicken auf den Senden-Button ausgeführt wird ist die Zeitdifferenz zwischen den beiden Requests sehr kurz. Natürlich schließt das die Möglichkeit nicht aus, dass die IP-Adresse genau in dieser Millisekunde wechselt - aber das wird mit an Sicherheit grenzender Wahrscheinlichkeit selten bis nie auftreten.

                    Doch - zum Beispiel bei AOL-Nutzern, deren Requests ueber eine Kaskade von Zwangsproxies geleitet werden.

                    MfG ChrisB

                    --
                    „This is the author's opinion, not necessarily that of Starbucks.“
                    1. Hallo Chris!

                      Doch - zum Beispiel bei AOL-Nutzern, deren Requests ueber eine Kaskade von Zwangsproxies geleitet werden.

                      Habe das gerade mal ergooglet. Das betrifft dort aber auch nur die Nutzer der AOL-Software. Gut, die möchte ich natürlich nicht ausgrenzen. Habe mir auf einem 2. Server gerade mal ein Sessionlog angeschaut, und nach AOL im Agenten gesucht. Da kam ich in diesem Jahr auf 560 Treffer - von 554.000 Einträgen. Das sind also knallharte 0.1%. Und auch bei denen muß es nicht zu Problemen kommen.
                      Laut AOL: When a member requests multiple documents for multiple URLs, each request may come from a different proxy server.

                      Ok, das ist trotzdem suboptimal - aber: Das Kennwort per get als Klartext oder einfachen Hash zu übertragen ist keine Alternative.

                      Das einzige was machbar wäre, ist ein Bindung an die aktuelle (Server-)Zeit. Ich kann natürlich das Kennwort + [hh:mm dd.mm.yy] zu einem Hash zusammenfassen, und danach in der Auswertung prüfen, ob der Hash zum Kennwort + jetzt bis jetzt -2 Minuten passt. Allerdings wäre das ein unsichererer Weg, denn in diesem Zeitfenster könnte ja jeder den Hash an des Server senden...

                      Wie würdest du es denn tun?

                      Gruß
                      Matthias

                      1. Ok, das ist trotzdem suboptimal - aber: Das Kennwort per get als Klartext oder einfachen Hash zu übertragen ist keine Alternative.

                        Welches Angriffsszenario genau soll durch das Gehashe überhaupt bekämpft werden?

                        Wenn du dem Script einen Salt für den Hash zur Verfügung stellst, sei es die IP-Adresse, sei es die Serverzeit oder sei es ein Zufallstoken: Wenn ich das angreifen will, hole ich mir halt einfach vor jedem Versuch einen Token ab.

                        Ich verhindere damit höchstens, dass eine fremde Website das Script direkt einbinden kann. Oder sehe ich das falsch? Dieses Szenario versuchst du ja auch mit der Referrer-Prüfung zu bekämpfen. Das sind aber alles letztlich ineffektive Methoden, die den Angriff nur ein wenig schwieriger gestalten. Effektiver wirkt die Sperrung des Nutzernamens und der IP. Allerdings kann ich auch dann von mehreren IPs einfach alle möglichen Nutzernamen durchspielen.

                        Gut, das sind aber Schwierigkeiten, mit denen jede Anmeldung im Web zu kämpfen hat, nicht allein dein spezifisches Konzept.

                        Mathias

                        1. Hallo Mathias

                          Welches Angriffsszenario genau soll durch das Gehashe überhaupt bekämpft werden?

                          Wenn du dem Script einen Salt für den Hash zur Verfügung stellst, sei es die IP-Adresse, sei es die Serverzeit oder sei es ein Zufallstoken: Wenn ich das angreifen will, hole ich mir halt einfach vor jedem Versuch einen Token ab.

                          Bei einem Zufallstoken wäre das so, in dem Moment wo ich das Kennwort mit der IP verbinde und daraus einen Hash bilde hilft es erstmal niemandem, diese Kommunikation abzuhören. Er benötigt schon die gleiche öffentliche IP-Adresse - das wird bei der Verwendung von Proxi´s natürlich aufgeweicht. Noch Idealer wäre natürlich den Hash aus Kennwort + IP + Uhrzeit zu bilden. Damit müsste der "Mann in der Mitte" schon in dem passenden Zeitfenster die öffentliche IP-Adresse erhalten um mit dem abgefangenen Hash was zu tun.

                          Ich verhindere damit höchstens, dass eine fremde Website das Script direkt einbinden kann. Oder sehe ich das falsch? Dieses Szenario versuchst du ja auch mit der Referrer-Prüfung zu bekämpfen.

                          Also grundsätzlich mache ich die Referrer-Prüfung um Fehlleitungen zu verhindern. Also z.B. Robots die sich nicht an die nofollow/noindex halten.

                          Das sind aber alles letztlich ineffektive Methoden, die den Angriff nur ein wenig schwieriger gestalten. Effektiver wirkt die Sperrung des Nutzernamens und der IP. Allerdings kann ich auch dann von mehreren IPs einfach alle möglichen Nutzernamen durchspielen.

                          Darum bekommt der "Angreifer" auch keine Meldung, warum die Anmeldung nicht funzt.

                          Gut, das sind aber Schwierigkeiten, mit denen jede Anmeldung im Web zu kämpfen hat, nicht allein dein spezifisches Konzept.

                          Da gehe ich von aus - aber gerade aus diesem Grund finde ich ist es die Diskussion wert. Meiner Meinung nach sind alle Schutzmechanismen, solange sie den Benutzer nicht stören, gut. Das sie überlistet werden können liegt natürlich in der Natur der Sache - hier ist dann nur die Frage mit welchem Aufwand der Angreifer zu Werke geht.

                          Gruß
                          Matthias

              2. Beim Ausführen der Abfrage ermittle ich zuerst die Client-IP-Adresse. Dann nehme ich das Kennwort + die IP und bilde daraus einen MD5. Den übertrage ich an den Server.
                Das übertragene Kennwort ist also an die aktuelle IP-Adresse gebunden.

                Vergiss die IP-Adresse und generiere serverseitig Zufallszahlen (Tokens). Die schreibst du ins JavaScript und speicherst sie gleichzeitig serverseitig. Bei der Authentifizierung bildest du aus einer Zufallszahl und dem Passwort einen Hash, serverseitig prüfst du die Übereinstimmung.

                Der Sinn davon ist aber gering. Bei SSL besteht soweit ich weiß nur ein kleines Problem beim Senden von vertraulichen Daten im GET-Request. Die URI sollte im Browser nicht gespeichert/gecacht werden. Aber vermutlich ist es mit Hash zuverlässiger.

                Mathias

              3. Moin Moin!

                Dein Konzept ist extrem wackelig. Bist Du sicher, dass AJAX bei allen Benutzern funktioniert? Und hältst Du es wirklich für sinnvoll, Benutzernamen und Password per GET-Request zu übertragen? Spätestens wenn irgendjemand an Dein Access-Log herankommt, hast Du ein riesiges Problem.
                Das übertragene Kennwort ist also an die aktuelle IP-Adresse gebunden.
                Natürlich hätte ich es lieber über POST, aber dann muß ich weg von AJAX.

                Woher kommt Dein Irrglaube, man könne bei AJAX keine POST-Requests ausführen?

                Mit den Log-Dateien habe ich keine Probleme. Ist ein eigener Server. Die Protokolle gehen in einen SQL Server. Da kann ich direkt per Trigger die Query´s ausfiltern.

                Und du bist sicher, dass der SQL-Server von außen nicht auslesbar ist? Wie liest Du denn Daten aus dem SQL-Server?

                (Wobei der SQL-Server ja auch noch so die eine oder andere Sicherheitslücke in der Vergangenheit hatte. Ich frag mich sowieso, wozu ein DB-Server per SQL-Statement beliebige Programme ausführen oder beliebig E-Mails verschicken können muß -- als Feature, nicht als Exploit. Da ich mit dem Monster zum Glück nicht mehr arbeiten muß, bin ich in Sachen Sicherheit des SQL-Servers nicht ganz auf dem Laufenden.)

                Hast Du übrigens mal nachgemessen, wie viel langsamer HTTPS gegenüber HTTP ist?
                Ich muß gestehen - nein. Habe gerade mal gegoogelt. Da gehen die Meinungen etwas auseinander - zwischen "etwas langsamer" bis 1:5.

                Messe es nach. Was Du in Google findest, wird mit Deinem Server mit an Sicherheit grenzender Wahrscheinlichkeit nicht übereinstimmen.

                Alexander

                --
                Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".