iPhone Webapp-Links nicht im Safari öffnen
iOS bietet euch eine nette Funktion mit der ihr Webseiten als Verknüpfung auf eurem Homescreen ablegen könnt. Wenn ihr eure Seite darauf noch etwas vorbereitet, könnt ihr sogar euer eigenes Logo hinterlegen und auch die Webseite später im Fullscreen Modus laufen lassen.
Dazu müsst ihr lediglich ein paar Ressourcen hinterlegen:
<!-- Setzen der Icons für die verschiedenen Auflösungen -->
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/Content/grafik/icons/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/Content/grafik/icons/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/Content/grafik/icons/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="/Content/grafik/icons/apple-touch-icon-57-precomposed.png">
<!-- sorgt dafür dass die App im Vollbild ausgeführt wird -->
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- Farbe der Statuszeile -->
<meta name="apple-mobile-web-app-status-bar-style" content="black">
Safari erkennt dann diese Tags und zeigt euer Icon und führt die Seite dann auch im Vollbildmodus aus. Ein weiteres Goodie ist, dass die Webseite als "App" läuft und im Taskmanager sogar ein eigenes Icon bekommt.
Hier am Beispiel meiner kleinen F1-Tippspiel Seite:
Der Haken
Ein kleines Problem bleibt dann aber noch. Sobald ihr einen Link auf die Seite setzt und der Benutzer diesen antippt wird der Link ein einer neuen Safari Instanz geöffnet. Das Liegt daran, dass iOS euere Seite als App betrachtet und alle Links werden in iOS vom Safari behandelt.
Zum Glück kann man dieses Verhalten sehr einfach mit etwas Javascript umgehen:
<script>(function(a,b,c){if(c in b&&b[c]){var d,e=a.location,f=/^(a|html)$/i;a.addEventListener("click",function(a){d=a.target;while(!f.test(d.nodeName))d=d.parentNode;"href"in d&&(d.href.indexOf("http")||~d.href.indexOf(e.host))&&(a.preventDefault(),e.href=d.href)},!1)}})(document,window.navigator,"standalone")</script>
Weitere Infos und Quelle zu der Lösung findet ihr bei Stackoverflow.
Nun werden alle Links in eurer "App" geöffnet und alles funktioniert wie gewohnt
App.net – der Neue auf dem Schulhof
Ich konnte meine Neugier doch nicht unter Kontrolle halten und habe mich auch bei dem neuen Twitter Konkurrenten registriert. Hier berichte ich von meinen ersten Erfahrungen und meinem Eindruck von dem Dienst.
Twitter mit Geschäftsmodell
Es ist DAS Problem, das Twitter, Facebook und alle anderen Dienste haben, wie mache ich Geld damit? Dieses Problem scheint bei App.net sich gar nicht zu stellen, denn jeder Nutzer muss eine jährliche oder monatliche Gebühr zahlen um den Dienst nutzen zu können.
Das finde ich persönlich sehr gut, denn das löst ziemlich viele Probleme, nicht nur aus der Sicht der Betreiber und Entwickler.
Das Feeling
Wenn man App.net nutzt erinnert das einen an die Anfänge von Twitter, als alles noch neu war und Twitter noch auf das Feedback der Nutzer hörte und zb. Features aus der Community übernahm wie die RT-Funktion.
Man merkt deutlich dass alle noch mit den Möglichkeiten spielen, alles ist sauber und aufgeräumt, keine (oder kaum) SEO-Agenturen und Bots die einem sofort followen, wenn man das falsche Buzzword twittert postet. Aber das wird sicherlich auch noch folgen
Vorteile von App.net
Bereits nach kurzer Nutzung, fällt auf, dass es auf App.net zivilisierter zugeht. Ok, das könnte auch daran liegen, dass es erst 23,000 Nutzer gibt, aber ich denke, dass es an dem Bezahlsystem liegt. Denn bei Twitter kann sich jeder 10x Accounts machen, mit denen er dann unnötigen Spam verbreiten oder irgendwelche Bots laufen lassen kann.
Wenn man für jeden Account, der dann auch an eine Kreditkarte gebunden ist Geld zahlen muss wird sich jeder sicherlich fragen ob das wirklich nötig ist und der Aufwand sich überhaupt lohnt.
Ich denke die echten Vorteile werden sich erst später mit der Zeit herauskristallisieren. Und wenn Twitter weiterhin so aggressiv die Dritthersteller absägt und immer mehr Werbung in die Timeline schaltet werden die Leute scharenweise überlaufen.
Die Unterschiede
Das Erste was auffällt ist natürlich das Zeichenlimit von 256 Zeichen. Dies verdoppelt fast den Wert von Twitter mit 140 erlaubten Zeichen. Hier bin ich mir ehrlich gesagt nicht sicher ob das wirklich besser ist. (Auch wenn es definitiv die nerdigere Zahl ist
)
Meine Erfahrung hat jedenfalls gezeigt, dass sich Menschen mehr Gedanken machen wenn sie sich kurz fassen müssen und das ist genau das was Twitter so besonders macht. Man hat alle Infos schnell auf einen schnellen Blick.
Weiterhin fällt auch die sehr minimalistische Oberfläche auf, die größtenteils auf Farben verzichtet. Auch wenn es nicht schlecht aussieht, denke ich dass sich hier demnächst vieles ändern wird, da das Ganze noch in den Kinderschuhen steckt.
Alles andere hat wirklich sehr viel Ähnlichkeit mit Twitter, nicht nur optisch auch funktionell:
Man sieht Funktionen wie "Repost" und "Favorite", was so ziemlich den Funktionen bei Twitter entspricht. Alle Posts die eine Antwort haben kann kann man auch in einer Konversation darstellen. Noch erkennt das System nicht die geposteten Links zu Bildern und Videos wie Twitter es tut, aber ich denke mal es ist nur eine Frage der Zeit.
Clients
Im Vergleich zu Twitter will App.Net Dritthersteller dafür belohnen Software für ihre Plattform zu erstellen. So gibt es schon diverse Clients für alle Systeme. Es macht sogar der Running-Gag die Runde, dass App.Net mehr Apps hat als User
Auf dem iPhone gibt es schon diverse Clients die alle im Schnitt 4€ kosten. Auch Browser Plugins und Standalone Programme sind vorhanden. Hier geht es definitiv in die richtige Richtung.
Was ist nicht so toll
Wenn man Twitter kennt gibt es leider nicht viel neues zu entdecken, ein paar Sachen gibt es aber, die man nicht einfach aus Twitter übernehmen kann, und das ist hauptsächlich der Name.
Der Dienst hat leider einen ziemlich nichtssagenden Namen, unter App.Net kann man sich alles mögliche vorstellen. Wie nennt man die Posts die man veröffentlicht? "Tweets" oder "Twittern" sind Begriffe die sich inzwischen richtig eingebürgert haben. Mit so etwas kann App.Net leider nicht dienen.
Wie nennt man nun so ein Statusupdate bei App.Net? Keine Ahnung, manche sprechen von Posts, andere sagen Updates - etwas einheitliches muss her, aber der Name lässt leider nichts schlüssiges zu, denn "Apps" würde man sicherlich falsch verstehen
Fazit
Bisher hat man mit App.Net ein Twitter ohne Werbung - nicht mehr und nicht weniger. Potential ist definitiv vorhanden und die Entwicklung wird sicherlich rasch vorangehen, denn der Dienst wächst mit knapp 10% pro Tag.
Weiter ins Detail gehen kann ich jetzt noch nicht, da ich einfach zu frisch auf dem System bin, aber das werde ich später gerne noch nachholen
Seid ihr auch auf App.Net unterwegs? Sagt mir was ihr davon haltet und fügt mich hinzu wenn ihr mögt
-> @bigbasti
iOS6 Smart App Banner auf eurer Seite nutzen
Sicherlich kennt ihr diese hässlichen Popups auf größeren Seiten wie Welt.de die auch eine (Bezahl-)App im Appstore haben. Diese Popups sind nicht nur unschön, sondern nerven auch extrem!
In iOS6 will Apple das Ganze nun etwas verschönern und bietet den Webseiten an ein Banner über dem Seiteninhalt anzuzeigen, das die gewünschte App inklusive ein paar Informationen wie Bewertung und Hersteller enthält.
Wie das aussehen kann seht ihr in dem oberen Screenshot des Safari unter iOS6. Statt einem Popup zeigt ted.com den neuen Banner an.
Implementierung
Wenn ihr eure Seite auch mit einer App verknüpfen wollt könnt ihr das ziemlich einfach gestalten:
<meta name="apple-itunes-app" content="app-id=%APPID%, app-argument=%APPPARAMETER%">
Ihr bindet einfach den oberen Meta-Tag in eurem Header ein und der mobile Safari erledigt den Rest. Ihr habt hier dabei zwei Parameter zu befüllen, wobei der zweite optional ist.
Die App-ID ist die Id der App im Appstore. Diese findet ihr in der URL der App im iTunes Store:
http://itunes.apple.com/de/app/doodle-jump-achtung-hochste/id307727765?mt=8
Dabei gebt ihr nur die Zahl an (ohne "id").
Der zweite Parameter wird der App (falls die verlinkte App bereits installiert ist) übergeben. So könnt ihr den Besucher nahtlos in die App wechseln lassen und automatisch an die Stelle in der App springen auf der der Besucher auf der Webseite war.
Da iOS6 sich wenn es bald raus ist ziemlich schnell verbreiten wird lohnt es sich sicher schon mal die Header anzupassen
GlassFish: Unnoetige HTTP-Response Header entfernen
In dem letzten Artikel habe ich beschrieben, wie ihr unter ASP.NET MVC Anwendungen die verräterischen HTTP Header ausblenden könnt, nun können wir auch mal auf den GlassFish schauen.
Wie ihr in dem oberen Bild erkennen könnt sendet der GlassFish hier seinen Namen, die Version und auch das verwendete Framework mit. Das wollen wir natürlich nicht haben.
Fangen wir mit dem Server-Header an. Dieser ist recht einfach zu entfernen durch das Setzen eines VM-Parameters in der GlassFish Adminoberfläche.
- Startet die Admin console
- Wählt Configuration ->JVM Settings
- Wählt oben JVM Options
- Klickst auf Add JVM Option
- In der neue textfeld tragt ihr ein: -Dproduct.name=""
- Klickt auf Save und startet den GlassFish neu
Auch wenn das nun etwas komisch aussieht, haben wir so den Server-Header entfernt. Dabei verhält sich der GlassFish unter verschiedenen Umständen anders. Manchmal zeigt er den Header so an wie in dem Bild, manchmal lässt er ihn auch komplett weg. (Wenn ihr hier mehr wisst hinterlasst bitte ein Kommentar).
X-Powered-By header
- Startet die Admin console
- Geht zu Configuration -> Network Config -> Network Listeners
- Wählt http-listener-2 aus und wählt dann oben "HTTP"
- Deaktiviert den Hacken bei "XPowered By" (Siehe Bild)
- Speichert und startet den GlassFish neu
Leider ist die Sache damit noch nicht gegessen, da dies scheinbar nur den "X-Powered-By: Servlet/3.0" Header entfernt den "X-Powered-By: JSP/2.1" muss man nochmal wo anders entfernen.
- Navigiert zu domains/domain1/config
- öffnet die Datei default-web.xml
- sucht nach xpoweredBy und setzt diesen Setting auf "false" (siehe Bild)
- Speichert und startet den GlassFish neu
Nun sieht die Sache wieder bessern aus:
Wenn ihr noch einen Tipp habt wie man den Server-Header ganz weg bekommt wäre ich für einen Kommentar dankbar!
ASP.NET MVC Security Teil 2: Unnoetige HTTP Response-Header entfernen
In dieser Reihe von Posts möchte ich Wege zum Absichern einer MVC Anwendung zeigen, die man gerne mal vergisst oder als Neueinsteiger nicht gleich auf dem Schirm hat.
Teil 2: Unnötige HTTP Response-Header entfernen
Wenn wir im Internet surfen werden oft Informationen über uns ohne unser Zutun bei jedem Request an den Server übertragen. Dies geschieht über die HTTP-Header. So wird z.B. die Adresse der Seite auf der wir einen Link geklickt haben in dem Referrer-Header an den Server übertragen.
Aber auch die Server selbst sind in dieser Sache oft sehr gesprächig und verraten mehr über sich als wirklich nötig ist.
In dem Bild sieht man (rot eingerahmt) Informationen die der Server über unsere Anwendung nach außen gibt, die eigentlich lieber geheim bleiben sollten.
Warum ist das gefährlich?
Die Informationen, die hier angezeigt werden verraten die Version des Servers, der Laufzeitumgebung und die Version des Frameworks, das wir benutzen. In diesem Fall ASP.NET MVC 3.
Falls nun bekannt wird, dass der Server oder ASP.NET in dieser Version eine Sicherheitslücke aufweist, sieht ein potentieller Angreifer sofort, dass unsere Seite für eine Attacke anfällig ist.
Es gibt sogar extra Tools, die das Web nach Webseiten durchsuchen, die eine Serverversion aufweisen, die als unsicher eingestuft wird oder für die es bekannte Lücken gibt. Diese Tools nutzen unter Anderem dann auch diese Informationen um zu prüfen ob es sich lohnt bei dieser Seite einen Angriff zu starten.
Was nun?
Im Grunde ist die Aufgabe sehr einfach, wir müssen diese unnötigen Serverheader ausblenden. Wenn die Webseite dann keine anderen Frameworkspezifischen Merkmale aufweist (wie z.B. einen Viewstate) ist es für einen Besucher praktisch unmöglich herauszufinden, welcher Server und welches Framework im Backend laufen.
Die Umsetzung
Leider ist ASP.NET in dieser Hinsicht noch nicht so richtig benutzerfreundlich, denn um all diese Header zu entfernen müssen wir an einigen verschiedenen Stellen Änderungen vornehmen. Gehen wir mal Schritt für Schritt alles durch.
1. ASP.NET & MVC Header
Diesen Header können wir recht einfach entfernen, indem wir in der global.asax die nötige Property setzen:
protected void Application_Start() { MvcHandler.DisableMvcResponseHeader = true; //<- Hier AreaRegistration.RegisterAllAreas(); Database.SetInitializer(new F1DBInitializer()); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }Dies hat schon eine Gute Wirkung auf unsere Response Header:
Die Version von ASP.NET und des MVC Frameworks wird nun nicht mehr angezeigt. Nun zu den anderen.
2. X-Powered-By Header
Um diesen Header zu entfernen müssen wir unsere web.config ergänzen (Quelle):
<?xml version="1.0"?> <configuration> <system.web> <httpRuntime enableVersionHeader="false" /> ... </system.web> <system.webServer> ... <httpProtocol> <customHeaders> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> </system.webServer> ... </configuration>Dann verabschiedet sich auch dieser Header.
3. Einer geht noch: Der Server-Header
Dies geht leider nicht so einfach durch das Setzen eines Configparameters, hier müssen wir selbst Hand anlegen und diesen Header aus der Response löschen. Dazu schreiben wir uns ein eigenes HttpModul (Quelle):
public class RemoveServerHeaderModule : IHttpModule { public void Init(HttpApplication context) { context.PreSendRequestHeaders += OnPreSendRequestHeaders; } public void Dispose() { } void OnPreSendRequestHeaders(object sender, EventArgs e) { HttpContext.Current.Response.Headers.Remove("Server"); } }Anschließend müssen wir das neue Modul über die web.config registrieren (Quelle):
<?xml version="1.0"?> <configuration> ... <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules runAllManagedModulesForAllRequests="true"> <add name="RemoveServerHeaderModule" type="SecurityTipp.RemoveServerHeaderModule"/> </modules> ... </system.webServer> </configuration>Das Ergebnis:
Nun haben wir unser Ziel erreicht und alle Plaudertaschen aus der Response entfernt
![]()
Weitergedacht
Hier könnte man nun ansetzen und sogar versuchen den potentiellen Angreifer in die Irre zu führen, indem man einen modifizierten Server-Header zurückgibt der auf eine Ganz andere Serversoftware verweist, Apache oder sowas
![]()
Das Schöne hierbei ist weiterhin, dass wir alle diese Änderungen innerhalb unserer App machen können und nicht im IIS rumfummeln müssen. Einerseits weil man da viel kaputtkonfigurieren kann, andererseits weil man nicht immer Zugriff auf den IIS hat, wenn man z.B. nur ein Webspace hat.
Diese Schritte sollten bei jeder Anwendung durchgeführt werden, egal ob MVC, ASP.NET oder PHP, gebt den Besuchern nicht mehr Informationen als sie unbedingt benötigen!
ASP.NET MVC Security Teil 1: Das AntiForgeryToken nutzen
In dieser Reihe von Posts möchte ich Wege zum Absichern einer MVC Anwendung zeigen, die man gerne mal vergisst oder als Neueinsteiger nicht gleich auf dem Schirm hat.
Teil 1: Nutzen des AntiForgeryToken bei POST Requests
POST Requests sind gefährlich, da diese im Normalfall dafür verwendet werden dem Server Informationen mitzuteilen. Die Informationen sollte man natürlich prüfen, auf Form und Inhalt, aber noch wichtiger ist es, zu wissen wer uns diese Daten sendet. (Cross-Site-Attacken verhindern)
Wie funktioniert das?
Mit dem AntiForgeryToken können wir an dieser Stelle sichergehen, dass nur Formulare am Server angenommen werden, die der Server selbst erzeugt hat.
Dabei erzeugt der Server beim Generieren des Formulars einen Token und speichert diesen in einem versteckten Input-Feld (__RequestVerificationToken), des Weiteren wird auch ein Cookie namens "__RequestVerificationToken" mit dem gleichen Token angelegt, sodass beim Absenden des Formulars sowohl der Cookie als auch das versteckte Feld wieder zurück an den Server übertragen wird.
Wenn der Server diesen Request empfängt prüft er diesen Token aus dem versteckten Feld gegen das Token aus dem Cookie. Gibt es hier keine Übereinstimmung wird der Request abgewiesen.
Wie wird das implementiert?
Als Erstes muss das Token in das Formular gesetzt werden und das Cookie erzeugt werden. Diese Aufgabe übernimmt für und der kleine Html-Helper aus MVC.
<div class="myForm"> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <!-- REST DES FORMULARS --> } </div>Das ging ja einfach, nun müssen wir dafür sorgen, dass dieses Token beim Verarbeiten des Requests auch geprüft wird.
[ValidateAntiForgeryToken] public ActionResult UpdateStuff() { ... }Mehr ist das nicht, ein einfaches Attribut genügt, und unsere MVC Anwendung ist mit nur zwei Zeilen sicherer geworden.
Wenn Ihr nun einen Blick in euer gerendertes Formular werft, werdet ihr etwas in der Art finden:
<input name="__RequestVerificationToken" type="hidden" value="4PhvBw7EO5I3O4zw2cdYkaEfSJycy1Ugi8AOT6glW6IqZNnVKXTzL52LgQGNMviOhYTxAOQH3iZRtehUoHKRprcHXa8YNel6RzVmKjGAEi0W0duRpnwJu9EpR7ybAoZX9yqeiP3Pyng/ma1t/k6QhM2bbcoBgkou5y0KXEj8Ut8=" />Gibt es Einschränkungen?
- Da hier ein Cookie gesetzt wird müssen natürlich Cookies aktiviert sein da der Server sonst alle Requests abweisen wird.
- Das Ganze funktioniert nur mit POST Requests, was auch logisch ist, da GET Requests nur zum Abfragen von Informationen genutzt werden sollten.
Alternativen?
Wenn man keine Cookies nutzen will kann man ein ähnliches Verhalten hervorrufen, indem man prüft ob der eingehende Request ein Referrer-Header besitzt der auf unsere eigene Domain + URL verweist.
Dies ist natürlich nicht wirklich Sicher, da es leicht zu fälschen ist und außerdem haben manche Browser Anonymisierungsfunktionen die diese Header-Felder nicht mitsenden, also ist hier Vorsicht geboten.
GMail: Punkte im Benutzernamen werden ignoriert
Heute habe ich eine EMail bekommen, die ganz klar nicht an mich gerichtet war, denn ich kannte weder den Absender und auch der Betreff brachte nichts zum Klingeln.
Als guter Mensch der ich auch bin
wollte ich die Mail natürlich an den eigentlichen Empfänger weiterleiten, denn seine Adresse stand unten als Ausgabe eines HTML-Formulars in der Mail mit drin.
Als ich die Adresse gesehen habe wurde mir auch klar, warum die bei mir angekommen ist, denn diese war:
big.basti@gouglemail.com
Ich dachte die korrekte Adresse wäre big.basti@googlemail.com und habe die Mail auch mit einer kurzen Erklärung dorthin weitergeleitet. Zu meiner Überraschung landete diese mail prompt wieder in meinem Posteingang.
Was ist denn nun los?
Nun habe ich mal genauer hingeschaut:
Dann ist mir auch der kleine Hinweis im Bild aufgefallen. Anscheinend weiß Google schon dass diese Verwechselung häufiger passiert. und hinter dem Link der sich hinter "Learn more" verbirgt bringt dann auch Licht ins dunkle.
There are three common reasons why Gmail users think they're receiving someone else's mail. Please select the description that matches your situation below.
Your address is similar but has more or fewer dots (.) or different capitalization.
Sometimes you may receive a message sent to an address that looks like yours but has a different number or arrangement of periods. While we know it might be unnerving if you think someone else's mail is being routed to your account, don't worry: both of these addresses are yours.
Gmail doesn't recognize dots as characters within usernames, you can add or remove the dots from a Gmail address without changing the actual destination address; they'll all go to your inbox, and only yours. In short:
- homerjsimpson@gmail.com = hom.er.j.sim.ps.on@gmail.com
- homerjsimpson@gmail.com = HOMERJSIMPSON@gmail.com
- homerjsimpson@gmail.com = Homer.J.Simpson@gmail.com
All these addresses belong to the same person. You can see this if you try to sign in with your username, but adding or removing a dot from it. You'll still go to your account.
Nun machts auch Sinn. Aber im Hintergrund scheint noch viel mehr zu passieren, denn man kann auch ein paar Buchstaben drehen oder weglassen und die Mails kommen dann trotzdem immer noch an - zumindest ist mir das schon häufiger passiert.
Auf jeden Fall ist das gut zu wissen, denn beim ersten Mal verwirrt es einen schon ganz schön
TL;DR
Google ignoriert Punkte "." im Benutzernamen, daher kommt die Mail dennoch an.
ASP.NET MVC: HTML5 Elemente mit jQuery Fallback nutzen
Soo, nachdem ich die Überschrift mit all den hippen Begriffen vollgepackt habe die es in Sachen Web momentan so gibt möchte ich euch kurz erklären worum es in diesem Artikel gehen soll.
Ihr werdet sicher mitbekommen haben, dass HTML5 auf dem Vormarsch ist und auch wenn es immer noch nicht offiziell fertig ist findet man immer häufiger den HTML5-Header <!DOCTYPE html> auf diversen Seiten.
Eins der meiner Meinung nach nützlichsten Features die wir mit HTML5 erhalten sind die neuen Input-Typen. Denn bisher hatten wir keine Wahl und mussten type="text" benutzen!
Die Eingabefelder vom Typ Text konnten natürlich alles enthalten, sind aber nicht gerade Benutzerfreundlich wenn man etwas komplexere Daten eingeben soll wie zum Beispiel ein Datum.
In HTML5 wurden deswegen spezifische Typen für solche immer wieder einkehrende Eingaben eingeführt. Eine von ihnen ist "Date":
Birthday: <input type="date" name="bday" />Der große Vorteil hier ist, dass die Browser nun eine eigene Implementierung für diesen Typ von Eingabefeldern machen können. Falls der Browser nun also diesen neuen Eingabe Typ unterstützt blendet er automatisch einem (mehr oder weniger) schönen Date-Picker ein in dem man bequem das Datum wählen kann:
Ein weiterer großer Vorteil ist, dass auch die mobilen Browser wie z.B. auf dem iPhone diese neuen Elemente ebenfalls unterstützen und einen passenden Picker einblenden:
Wie ihr in dem ersten Screenshot sehen könnt unterstützt der FireFox (zumindest zum Zeitpunkt des Screenshots) den neuen Typ nicht. In diesem Fall blendet er ein gewöhnliches Textfeld ein wo wir nun gezwungen sind das Datum auf die gewohnte (unbequeme) Weise einzutragen.
Übrigens, ich glaube ich muss euch nicht sagen warum der Internet Explorer hier nicht mit aufgeführt ist oder?
![]()
Und genau das ist das Problem um das es in diesem Blogpost geht. Wenn wir die tollen neuen Elemente nutzen gehen wir das Risiko ein, dass Nutzer die einen älteren Browser nutzen nicht in den Genuss einer bequemen Eingabe kommen.
Wir könnten auch komplett auf das HTML5 Element verzichten und direkt alles mit jQuery machen, welches auch einen Date-Picker bietet, aber das ist auch nicht das gelbe vom Ei, denn dann wären die Mobilen Nutzer in Nachteil, da dort die Browser diese Elemente für gewöhnlich unterstützen und eine gewohnte Oberfläche für die Eingabe bieten.
Die Optimale Lösung ist offensichtlich eine Mischung beider Welten. Wenn der Benutzer die Seite mit einem kompatiblen Browser aufruft soll der Date-Picker des Browsers genutzt werden, wenn der Browser aber veraltet ist soll stattdessen der jQuery Fallback greifen und der JavaScript Date-Picker (siehe links) genutzt werden.
Zu unserem Glück macht uns ASP.NET MVC die Umsetzung dieses Plans sehr leicht, da es an den nötigen Stellen sehr einfach erweitert werden kann.
Schauen wir uns mal ein kleines Beispiel an. Wir haben eine Klasse Person, und wollen auch dessen Geburtstag speichern, hier wollen wir die tolle neue Funktionalität nutzen. Implementieren wir das Ganze jedoch erstmal wie gewohnt:
public class Person { public int ID { get; set; } public String Name { get; set; } public DateTime Geburtstag { get; set; } }Wenn wir die Model-Klasse haben können wir das MVC Scaffolding nutzen um für uns eine View zum Erstellen neuer Personen anzulegen:
Schauen wir uns nun doch mal an was da für uns tolles generiert wurde:
<div class="editor-field"> @Html.EditorFor(model => model.Geburtstag) @Html.ValidationMessageFor(model => model.Geburtstag) </div>Auffällig hierbei ist, dass hier @Html.EditorFor genutzt wird und nicht etwa @Html.TextBoxFor! Der Hintergrund ist der, dass MVC bei der Generierung versuchen wird ein passendes Element für den angegebenen Datentyp (DateTime) zu bestimmen, aber da MVC standardmäßig nur den Typ Text kennt wird hier eigentlich immer ein Input-Element vom Typ text generiert.
Hier kommt uns die Erweiterbarkeit von MVC zu gute, denn wir können ganz einfach NuGet nutzen um uns die nötige Funktionalität zu verschaffen. Wir installieren das Packet MvcHtml5Templates.
Nachdem NuGet alles erledigt hat werdet ihr feststellen, dass ihr ein paar neue Dateien in eurem Views/Shared Verzeichnis habt (siehe Bild links). Und wie ihr schon an dem Namen erkennen könnt sind das die neuen Input-Typen aus HTML5.
Diese Templates machen im Grunde nichts anderes als die MVC-Eigenen zu überschreiben und versehen die mit dem passendem Type-Attribut.
Hier sind euch keine Grenzen gesetzt ihr könnt natürlich auch eire eigenen Templates definieren mit euren eigenen Typen. (Auch wenn man das eher selten benötigt)
Das Tolle: mehr müssen wir nicht machen. MVC wird nun zur Laufzeit statt einem Text ein Date Input-Element für uns anlegen. Beachtet, dass das nur geht wenn wir in der View die allgemeine Funktion @Html.EditorFor nutzen und keinen Spezifischen Typ angeben.
Schauen wir mal in den Sourcecode der zur Laufzeit für das Geburtstagsfeld generiert wird:
<input class="text-box single-line" data-val="true" data-val-required="Das Feld 'Geburtstag' ist erforderlich" id="Geburtstag" name="Geburtstag" type="datetime" value=""; />Wie ihr seht wird nun der Korrekte Typ, nämlich datetime verwendet. Wenn wir die Seite nun also mit einem kompatiblen Browser aufrufen können wir ganz bequem das Datum wählen.
Leider ist der Opera Browser momentan wohl der einzige Desktop Browser der uns hier einen Benutzerfreundlichen Dialog einblendet (siehe erstes Bild oben), somit müssen wir dafür sorgen, dass die Benutzer mit anderen oder alten Browsern nicht benachteiligt werden.
Auch für diese Problematik bringt MVC bereits alles mit was nötig ist sie zu lösen. Werfen wir doch mal einen Blick in unseren Scripts Ordner finden wir alle nötigen jQuery und jQuery UI Skripte, dazu kommt noch die modernizr Bibliothek die uns auch zu Gute kommen wird.
Die jQuery Bibliotheken liefern und die nötige Funktionalität die wir benötigen um diesen hübschen Date-Picker einzublenden.
Die modernizr Bibliothek dagegen hilft uns herauszufinden ob der Browser, den der Benutzer momentan verwendet die gewünschten HTML5 Features unterstützt.
Nun müssen wir also bei unseren Formularen prüfen, ob der Browser die nötigen Funktionen kennt und bei Bedarf die jQuery Klassen einbinden. Das Ganze ist dank der Einfachheit von Modernizr und jQuery ziemlich simpel.
Wechseln wir zu unserer View und fügen die Referenzen auf die nötigen Skripte und CSS Dateien ein:
<script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.js")" type="text/javascript"></script> <link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />Unten in der View fügen wir nun eine Modernizr Abfrage ein und prüfen, ob der Browser das DateTime-Feld unterstützt, und wenn nicht lassen wir jQuery das Ganze für uns regeln:
<script type="text/javascript"> $(function () { if (!Modernizr.inputtypes.date) { $("input[type='datetime']").datepicker(); } }); </script>Mit dem Befehl $("input[type='datetime']").datepicker(); werden alle Input-Felder vom Typ DateTime durch den jQuery Picker ersetzt. Coole Sache!
Kleiner Hinweis am Rande: Das ist nicht wirklich best practice da die Skripte auch geladen werden wenn sie gar nicht benötigt werden (der Browser kennt das HTML5 Feld) ich habe das hier der Einfachheit geopfert.
Und das wars auch schon! Alle Browser die das Feld unterstützen blenden nun ihren Picker ein, ansonsten wird der jQuery Fallback genutzt.
Wir müssen uns auch nicht mit irgendwelchen UserAgent-Prüfungen herumschlagen und lassen das alles Modernizr erledigen, der das intern über JavaScript prüft und somit immer aktuell ist. Das heißt dass wenn irgendwann der IE plötzlich die neuen Typen kennt werden diese auch funktionieren!
Genauso könnt ihr auch bei all den anderen neuen HTML5 Typen vorgehen und euren Usern ein bestmögliches Bedienerlebnis bereiten
![]()
SSL Teil 4: Serverseitige Authentifizierung mit Java
Nachdem wir in den letzen Teilen mehr die Theorie durchgenommen haben, möchte ich jetzt zu dem praktischen Teil kommen und euch zeigen wie man diese Theorie in Code umsetzen kann am Beispiel Java. (.NET wird auch noch folgen)
Wir wollen einfach einsteigen und implementieren erst mal nur die eine Serverseitige Authentifizierung, also muss der Server sich uns gegenüber ausweisen und wir sind in der Pflicht sein Zertifikat anzunehmen oder abzulehnen.
Wie in Teil 1 erklärt benötigen wir also zunächst einen Ort wo wir die Zertifikate sichern, denen wir vertrauen. Hier können wir (da wir in Java unterwegs sind) nicht die Windows-eigenen nehmen sondern müssen die so genannten Keystores von Java Nutzen.
Ein Keystore, wie der Name schon sagt ist nichts anderes als ein Speicher für Zertifikate. Wenn ihr Java installiert dann legt Java bereits einen Keystore für euch an, diesen findet ihr dann in eurem Java Verzeichnis Java\jre6\lib\security\cacerts die Datei „cacerts“ enthält hier bereits ähnlich wie bei Windows bereits die CA-Zertifikate von den großen Zertifizierungsstellen.
Es wir desweiterhin in Java unterschieden zwischen Keystore und Truststore, beides sind im Grunde Keystores und unterscheiden sich nur im Namen und in ihrem Inhalt. Eine einzige Datei kann auch beide beinhalten, ist aber eher unüblich, da man die eigenen (privaten) Zertifikate von den CA-Zertifikaten separieren will.
So enthalten die Keystores für gewöhnlich die eigenen Privaten Schlüssel und die eigenen von anderen CAs signierte Zertifikate, die bei Verbindungen als Serverzertifikate (Public Keys) genutzt werden. Der Truststore ist das Gegenstück und enthält die Zertifikate denen man vertraut. So müssen wir die Zertifikate die wir vom Server beim Verbindungsaufbau erhalten gegen einen Truststore prüfen.
Fangen wir mit unserem Code an, wir wollen einen Client erstellen, der eine Socket-basierte Verbindung zu einem Server über SSL aufbaut und das vom Server zurückgegebene Zertifikat prüft.
Java ist auf diesem Gebiet sehr entgegenkommend und bietet für fast alle Anwendungsfälle fertige APIs und Klassen die uns fast die ganze Arbeit abnehmen.
Um den Prozess besser aufzeigen zu können schreiben wir uns eine Eigene SSLSocketFactory die für uns den Aufbau der Verbindung übernimmt und unserem Programm im Prinzip ein fertiges Socket liefert.
Dazu erstellen wir eine neue Klasse und lassen diese von SSLSocketFactory erben. Da das eine abstrakte Klasse ist müssen eir einige Methoden implementieren. Wenn das erledigt ist sieht unsere Klasse schon so aus:
/** * Stellt eine SSLSocketFactory bereit die einen angegebenen TrustStore nutzt * @author Sebastian Gross */ public class ServerSSLAuthSocketFactory extends SSLSocketFactory{ @Override public String[] getDefaultCipherSuites() { } @Override public String[] getSupportedCipherSuites() { } @Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { } @Override public Socket createSocket(String destinationAddress, int destinationPort) throws IOException, UnknownHostException { } @Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { } @Override public Socket createSocket(InetAddress ia, int i) throws IOException { } @Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { } }Nun müssen wir diesen Methoden Leben einhauchen, aber keine Angst das ist nicht so schlimm wie es aussieht, denn die einzige Änderung die wir machen müssen ist es der SSLSocktFactory beizubringen mit unserem Truststore zu arbeiten, der Rest der Funktionalität soll unberührt bleiben.
Fügen wir also einen Konstruktor zu unserer Klasse hinzu der das Initialisieren unserer Internen SSLSocketFactory mit unserem Truststore übernimmt.
/** Delegate {@link SSLSocketFactory}. */ private transient SSLSocketFactory factory; private File caFile; private KeyStore ks; private SSLContext context; private TrustManagerFactory tmf; private X509TrustManager defTrustManager; private SavingTrustManager tm; /** * Neue Truststore-basierte SSL-SocketFactory * @param truststoreLocation Vollqualifizierter Pfad zum Truststore oder leer lassen für Java-Default-Truststore * @param truststorePass Passwort für den Truststore. Wenn leer wird "changeit" ala Passwort benutzt. */ public ServerSSLAuthSocketFactory(String truststoreLocation, String truststorePass){ super(); //Wenn ein Pfad zum TrustStore angegeben wurde diesen benutzen if(!truststoreLocation.isEmpty()){ caFile = new File(truststoreLocation); }else{ //Pfad zum default Java TrustStore finden caFile = new File("cacerts"); if (!caFile.exists() || !caFile.isFile()) { char SEP = File.separatorChar; File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security"); caFile = new File(dir, "cacerts"); if (!caFile.exists() || !caFile.isFile()) { caFile = new File(dir, "cacerts"); } } } //Prüfen, ob ein TrustStore Password angegeben wurde String truststorePassword = truststorePass.isEmpty() ? "changeit" : truststorePass; //TrustStore öffnen try { InputStream in = new FileInputStream(caFile); ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, truststorePassword.toCharArray()); in.close(); } catch (KeyStoreException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex){ Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (CertificateException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); }catch (IOException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } try { context = SSLContext.getInstance("TLS"); tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); defTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; context.init(null, new TrustManager[] {defTrustManager}, null); factory = context.getSocketFactory(); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (KeyStoreException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (KeyManagementException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } }Das sieht auf den ersten Blick recht komplex aus, ist aber im Grunde recht simpel.
- In den Zeilen 23 - 28 prüfen wir ob der Konstruktor einen Pfad zu einem Truststore übergeben bekommen hat, und wenn dies nicht der Fall ist, wird der Pfad zu dem Standard-Java-Truststore in eurem JRE-Verzeichnis ermittelt.
- Direkt danach in Zeile 41 prüfen wir ob ein Passwort übergeben wurde. Wenn keins übergeben wurde wird "changeit" genutzt. "changeit" ist das Defaultpasswort für den Standard-Java-Truststore.
- In Zeile 45 - 48 öffnen wir den Keystore und laden dessen Inhalt in unsere Kokale KeyStore Variable.
- Das Wichtigste geschieht nun in Zeile 67 - 72 hier holen wir uns einen Verweis auf den SSLContext (67) und erstellen uns mit Hilfe unseres KeyStores einene TrustManagerFactory(68-69). Anschließend lassen wir die TrustManagerFactory für uns einen neuen X509TrustManager "produzieren" der dann den Verweis auf unseren eigenen Keystore trägt. (70)
- Nun sagen wir dem SSLContext, dass es unseren Trustmanager nutzen soll (71) für die darüber laufende Verbindungen und erstellen uns eine SSLSocketFactory (72)
Nun haben wir intern eine SocketFactory, die unseren eigenen (über den Konstruktor angegebenen) Keystore nutzt. Der Rest der Klasse die wir erstellt haben dient hierbei eigentlich nur als Wrapper und Durchreiche, der Wichtigste Teil war der Konstruktor.
Die restlichen Methoden können nun also alle Anfragen direkt an die produzierte SSLSocketFactory weiterleiten. So sieht dann die fertige Klasse aus:
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * Stellt eine SSLSocketFactory bereit die einen angegebenen TrustStore nutzt * @author Sebastian Gross */ public class ServerSSLAuthSocketFactory extends SSLSocketFactory{ /** Delegate {@link SSLSocketFactory}. */ private transient SSLSocketFactory factory; private File caFile; private KeyStore ks; private SSLContext context; private TrustManagerFactory tmf; private X509TrustManager defTrustManager; private SavingTrustManager tm; /** * Neue Truststore-basierte SSL-SocketFactory * @param truststoreLocation Vollqualifizierter Pfad zum * Truststore oder leer lassen für Java-Default-Truststore * @param truststorePass Passwort für den Truststore. * Wenn leer wird "changeit" ala Passwort benutzt. */ public ServerSSLAuthSocketFactory(String truststoreLocation, String truststorePass){ super(); //Wenn ein Pfad zum TrustStore angegeben wurde diesen benutzen if(!truststoreLocation.isEmpty()){ caFile = new File(truststoreLocation); }else{ //Pfad zum default Java TrustStore finden caFile = new File("cacerts"); if (!caFile.exists() || !caFile.isFile()) { char SEP = File.separatorChar; File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security"); caFile = new File(dir, "cacerts"); if (!caFile.exists() || !caFile.isFile()) { caFile = new File(dir, "cacerts"); } } } //Prüfen, ob ein TrustStore Password angegeben wurde String truststorePassword = truststorePass.isEmpty() ? "changeit" : truststorePass; //TrustStore öffnen try { InputStream in = new FileInputStream(caFile); ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, truststorePassword.toCharArray()); in.close(); } catch (KeyStoreException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex){ Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (CertificateException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); }catch (IOException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } try { context = SSLContext.getInstance("TLS"); tmf = TrustManagerFactory.getInstance(TrustManagerFactory .getDefaultAlgorithm()); tmf.init(ks); defTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; context.init(null, new TrustManager[] {defTrustManager}, null); factory = context.getSocketFactory(); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (KeyStoreException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } catch (KeyManagementException ex) { Logger.getLogger(ServerSSLAuthSocketFactory.class.getName()) .log(Level.SEVERE, null, ex); } } @Override public String[] getDefaultCipherSuites() { return factory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return factory.getSupportedCipherSuites(); } @Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { return factory.createSocket(socket, string, i, bln); } @Override public Socket createSocket(String destinationAddress, int destinationPort) throws IOException, UnknownHostException { return (SSLSocket)factory.createSocket(destinationAddress, destinationPort); } @Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { return factory.createSocket(string, i, ia, i1); } @Override public Socket createSocket(InetAddress ia, int i) throws IOException { return factory.createSocket(ia, i); } @Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { return factory.createSocket(ia, i, ia1, i1); } }Da wir nun eine Factory haben die uns Sockets liefert, die auf unseren Truststore zugreift, können wir diese nun nutzen um eine Verbindung zu SSL Servern aufzubauen. Das Schöne hierbei ist, dass Java an sehr vielen Stellen SocketFactories nutzt, so können wir fast in allen Szenarien unsere Klasse einsetzen.
So können wir unsere SSLFactory nun ganz simpel testen:
public static void main (String [] args) throws IOException{ int port = 443; // default https port String host = "httpsurl"; try { SSLSocketFactory factory = (SSLSocketFactory) new ServerSSLAuthSocketFactory("",""); SSLSocket socket = (SSLSocket) factory.createSocket(host, port); // enable all the suites String[] supported = socket.getSupportedCipherSuites(); socket.setEnabledCipherSuites(supported); Writer out = new OutputStreamWriter(socket.getOutputStream()); // https requires the full URL in the GET line out.write("GET https://" + host + "/ HTTP/1.1\r\n"); out.write("Host: " + host + "\r\n"); out.write("\r\n"); out.flush(); // read response BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); // read the header String s; while (!(s = in.readLine()).equals("")) { System.out.println(s); } System.out.println(); // read the length String contentLength = in.readLine(); int length = Integer.MAX_VALUE; try { length = Integer.parseInt(contentLength.trim(), 16); } catch (NumberFormatException ex) { // This server doesn't send the content-length // in the first line of the response body } System.out.println(contentLength); int c; int i = 0; while ((c = in.read()) != -1 && i++ < length) { System.out.write(c); } System.out.println(); out.close(); in.close(); socket.close(); } catch (IOException ex) { System.err.println(ex); } }Tragt in Zeile 4 einfach den gewünschten SSL-Host ein wie z.B. Google oder Facebook und startet das kleine Programm. Falls ihr keine Parameter beim Erzeugen der Factory (Zeile 7 und 8) angegeben habt solltet ihr auch keine Probleme beim Verbinden haben und wenn das Programm durchgelaufen ist seht ihr die Antwort des Servers im HTML Format.
Falls ihr doch einen eigenen Truststore eingetragen habt müsst ihr sicherstellen, dass dieser den Public Key des Servers zu dem ihr euch verbinden wollt beinhaltet, sonst wird die Verbindung nicht erfolgen.
Teil 1: Serverseitige Authentifizierung und Zertifikate
Teil 2: Beidseitige Authentifizierung
Teil 3: Der SSL Handshake
Teil 4: Serverseitige Authentifizierung mit Java
SSL Teil 1: Serverseitige Authentifizierung und Zertifikate
SSL ist aktiv
SSL (Secure Socket Layer) sollte wohl den meisten ein Begriff sein, das ist dieses lustige kleine "s" was manchmal hinter dem "http" in der Adresszeile des Browsers auftaucht (siehe Bild). In dieser Serie möchte ich euch erklären wie SSL in der Theorie funktioniert und wie ihr es in euren Applikationen nutzen könnt.
Viele Bekannte Webportale wie Facebook und Google bieten alle ihre Dienste inzwischen komplett über SSL an. Wenn ihr diese Technologie nutzen wollt, müsst ihr verstehen wie diese Funktioniert und was da eigentlich im Hintergrund abläuft.
Dieses kleine "s" hat eine große Wirkung und sorgt dafür, dass die gesamte Kommunikation mit dem Server in verschlüsselter Form abläuft und niemand sehen kann was ihr da so treibt.
In 99% der Fälle sprechen wir hier über SSL über Serverseitige Authentifizierung, was soviel heißt wie der Server muss uns beweisen, dass er der ist für den er sich ausgibt. So wie wir unseren Ausweis vorzeigen müssen wenn wir ein Paket bei der Post abholen müssen weil der Postbote wiedermal zu faul war zu klingeln... Wir müssen der Post beweisen, dass wir der sind für den wir uns ausgeben.
Wie weist sich der Server denn nun aus? Einen Ausweis hat er nicht, aber er hat etwas sehr ähnliches, nämlich ein Zertifikat, das von einer dritten Stelle, nämlich einer CA (Certificate Authority - auf deutsch Zertifizierungsstelle) signiert wurde.
Wenn wir das auf unser Ausweisbeispiel anwenden sieht es ca so aus: Jeder kann sich ein Blatt Papier mit einem Foto drucken wo sein Name draufsteht, aber euer Paket bei der Post werdet ihr damit nicht bekommen, da die Post der Herkunft des "Ausweises" nicht traut, es muss also von anderen Person (der die Post vertraut), die nicht ihr selbst seid, bestätigt werden, dass das wirklich ein echter Ausweis ist und dass das wirklich ihr seid. Im Falle eines Ausweises würde das wohl das Einwohnermeldeamt machen o.ä. diese würden euch einen Ausweis austellen dem andere vertrauen, da sie wissen, dass es aus einer vertrauenswürdigen Quelle kommt.
Wenn ihr euch also zu einem Server verbindet, der SSL nutzen will wird dieser euch also zunächst sein Zertifikat senden (seinen Ausweis) und nun seid ihr in der Pflicht zu entscheiden, ob ihr diesem Zertifikat (und somit dem Server) vertraut oder nicht.
Die (Vertrauens)-Prüfung übernimmt für euch meistens der Browser selbst. Jeder Browser kommt von Haus aus mit einer Liste von CAs denen er blind vertraut. Diese Liste könnt ihr auch einsehen, sie ist häufig irgendwo in den Einstellungen des Browsers vergraben. Im Firefox findet ihr diese unter Einstellungen -> Erweitert -> Verschlüsselung ->Zertifikate anzeigen -> Zertifizierungstellen. Das sieht dann ca so aus:
Das bedeutet, wenn ein Server dem Browser ein Zertifikat schickt, dass von einer dieser CAs signiert wurde, vertraut der Browser dem Server und baut die Verbindung zu ihm auf.
Was passiert denn nun wenn der Server uns ein unsigniertes Zertifikat schickt, oder ein Zertifikat dass von einer CA signiert wurde die dem Browser unbekannt ist?
Dann wird uns der Browser fragen, ob wir uns wirklich sicher sind, dass wir uns sicher sind, dass wir diese Seite wirklich aufrufen wollen. Dieses Fenster habt ihr (im FireFox) vielleicht auch schon gesehen:
Nun müsst ihr also selbst entscheiden, ob ihr diesem Zertifikat vertraut. Hier sollte man aufpassen, denn wenn es um wichtige Sachen geht wie Online Banking sollte man von dieser Seite die Finger lasse, da es sich eventuell um ein Phishing versuch handeln könnte - diese Meldung ist niemals ein gutes Zeichen.
Wenn ihr hier das Zertifikat also akzeptiert, merkt der FireFox sich das und fragt bei der nächsten Verbindung nicht mehr nach da es für ihn ab nun ein vertrauenswürdiges Zertifikat ist.
Aber nicht nur die Browser haben einen solchen Zertifikate-Speicher, das Betriebssystem selbst hat ebenfalls eine Liste mit CAs denen es Vertrauen schänkt. Diese Liste kann in den Systemeinstellungen gefunden werden und wird sich häufig mit der des Browsers großenteils überschneiden. Unter Windows findest ihr diese unter Systemsteuerung -> Netzwerk und Internet ->Internetoptionen -> Inhalte -> Zertifikate -> Vertrauenswürdige Stammzertifizierungsstellen
Was steht eigentlich in so einem Zertifikat? Gucken wir doch mal rein:
Jedes Zertifikat enthält diese Informationen:
- Anwendung (Ist es ein Server oder ein Clientzertifikat?)
- Wer hat das Zertifikat ausgestellt?
- Für wen wurde dieses Zertifikat ausgestellt
- Wielang ist das Zertifikat gültig?
- Die Signierungskette aller CAs die dieses Zertifikat signiert haben
An Hand dieser Informationen wird entschieden, ob das Zertifikat akzeptiert wird oder nicht.
Zertifikate sind hierbei nichts besonderes, da jeder sich selbst Zertifikate ausstellen kann, es gibts dutzende Tools im Netz die euch Zertifikate generieren können, wie z.B. das kleine Javaprogramm "keytool" dass automatisch mit einem Java SDK mitinstalliert wird. Das Wichtige an einem Zertifikat ist immer seine Signatur, denn erst diese macht ein Zertifikat wertvoll und für andere Vertrauenswürdig.
Teil 1: Serverseitige Authentifizierung und Zertifikate
Teil 2: Beidseitige Authentifizierung
Teil 3: Der SSL Handshake
Teil 4: Serverseitige Authentifizierung mit Java
























