Sessions: Drittanbieter-Shops wenden Optionen nicht korrekt auf Codecs an.

Erstellt am 10. Juli 2015  ·  17Kommentare  ·  Quelle: gorilla/sessions

Wir haben dieses Paket gerade aktualisiert und es setzt Cookies nicht richtig. Es setzt Dinge wie _ga=GA1.1.922831813.14264788986 anstelle der regulären base64-codierten Cookie-Daten. Haben Sie eine Idee, was los ist oder wann das kaputt ging, damit wir zu einer funktionierenden Version zurückkehren können? Vielen Dank!

bug stale

Hilfreichster Kommentar

Beachten Sie, dass dies auch die meisten Store-Implementierungen betrifft, da alle Securecookie zum Speichern der ID in ihrem Back-End-Store verwenden.

redistore hat die Hälfte einer korrekten Implementierung, wenn Sie SetMaxAge manuell aufrufen, wendet dies jedoch nicht direkt für den Standardfall an. Zufälligerweise ist die Standardeinstellung (30 Tage) die gleiche wie die von Securecookie.

Auf den ersten Blick scheint es, dass _alle_ anderen Speicher betroffen sind – das Festlegen eines MaxAge über die Options-Struktur wird nicht auf die zugrunde liegenden Securecookie-Instanzen angewendet, die von CodecsFromPairs generiert werden. Die meisten Endbenutzer wären nicht auf diesen Fehler gestoßen, da ich vermute, dass die meisten Benutzer Ablaufdaten _weniger_ setzen als das, was Securecookie implizit in den HMAC einbettet.

FWIW: Ich glaube auch, dass Cookie-Abläufe in "ferner Zukunft" nicht großartig sind, es sei denn, Sie haben einen sehr speziellen Bedarf dafür.

Alle 17 Kommentare

Das Cookie "_ga" hat nichts mit Gorilla/Sessions zu tun - es ist ein Google Analytics
Plätzchen.

Wenn Gorilla/Sessions keine Cookies setzt (es gab keine Unterbrechungen)
Änderungen) können Sie den entsprechenden Code und die Ausgabe des Cookies-Bereichs posten
von Ihrem Browser? (Inspektor > Ressourcen > Cookies unter Chrome)

Am Freitag, 10. Juli 2015 um 07:12 markalpeter [email protected]
schrieb:

Wir haben dieses Paket gerade aktualisiert und es setzt Cookies nicht richtig. Es ist
Einstellungen wie _ga=GA1.1.922831813.14264788986 anstelle des regulären
base64-codierte Cookie-Daten. Irgendeine Ahnung was los ist oder wann das so kaputt ging
können wir zu einer funktionierenden Version zurückkehren? Vielen Dank!


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/gorilla/sessions/issues/48.

Hallo Matt,

Ich arbeite auch mit Mark an dem Projekt und kann dir ein paar mehr Details geben. Also haben wir das Paket einfach aktualisiert, weil das ursprüngliche Problem darin bestand, dass die Cookies alle nach 30 Tagen ablaufen, selbst wenn wir MaxAge auf eine wirklich große Zahl gesetzt haben. Hier ist der Authentifizierungscode, der gerade live ist und uns Probleme bereitet:

Konstante (
AUTH_SESSION_NAME = "Authentifizierungssitzung"
)

var (
config = jconfig.LoadConfig (global.CONFIG() + "securecookie.json" )
authKey = []byte(config.GetString("authorization_key"))
EncryptionKey = []byte(config.GetString("encryption_key"))
session = session.NewCookieStore(authKey,verschlüsselungKey)
)

func-init() {

apiRouter.HandleFunc("/auth/"   , Authenticate      )
apiRouter.HandleFunc("/deauth/"  , Deauthenticate   )
apiRouter.HandleFunc("/reauth/" , ReAuthenticate    )

// register complex data types for saving in sessions
gob.Register(&models.Device{})
gob.Register(&models.Manager{})
gob.Register(&models.SalesRep{})

// modify the options of the session store so that the auth cookie never expires (this is set so it expires in 200 years...)
session.Options.MaxAge = 6307200000

}

// dieser Handler gewährt Berechtigungen im System für Geräte und Manager
// Wenn der Anforderer bereits autorisiert ist, werden seine Informationen angezeigt. Um einen Defferent zu bevollmächtigen
// Konto muss sich der Client zuerst deauthentifizieren.
//
// Parameter
// Benutzer:
//
// Hinweis: Die von dieser Authentifizierungsfunktion zurückgegebenen Daten sind nur garantiert genau
// wenn sich der Benutzer zum ersten Mal authentifiziert. andernfalls werden in ihrer Sitzung zwischengespeicherte Daten angezeigt
//
func Authenticate(Antwort http.ResponseWriter, Anfrage *http.Request) {

log.Println("Authenticate")

// open the session
auth_session, err :=  session.Get(request, AUTH_SESSION_NAME)
if err != nil {
    log.Println("there was an error retreiving the session:", err)
    InternalServerError(response, request)
    return
}

// already authroized as a manager
if manager, ok := auth_session.Values["manager"].(*models.Manager); ok {
    log.Printf("already logged in as manager %d", manager.Id)
    Success(response, request, manager)

// already authrorized as a device
} else if device, ok := auth_session.Values["device"].(*models.Device); ok {
    log.Printf("already logged in as device %d", device.Id)
    Success(response, request, device)

// attempt to gain authroization
} else {            
    fp  := parsers.FormParser(request)  

    // login as a manager
    if username, password := fp.GetString("user", ""), fp.GetString("pass", ""); username != "" && password != ""  {

        db := database.Open()
        defer db.Close()


        if manager := db.LoginAsManager(username, password); manager != nil {
            auth_session.Values["manager"] = manager

            // manager session error
            if err := auth_session.Save(request, response); err != nil {
                log.Printf("manager could not save session %s !\n", err.Error())
                InternalServerError(response, request)

            // manager login in success
            } else {
                log.Printf("logged in as manager %d !\n", manager.Id)
                Success(response, request, manager)     
            }

        // manager login failed
        } else {
            log.Printf("manager credentials not valid!")
            Unauthroized(response, request)
        }

    // login as a salesrep
    } else if username, password := fp.GetString("username", ""), fp.GetString("password", ""); username != "" && password != "" {

        db := database.Open()
        defer db.Close()


        if sales_rep_id := db.LoginAsSalesRep(username, password); sales_rep_id > 0 {

            sales_reps := db.GetSalesRep(&models.SalesRep{ Id: sales_rep_id });

            auth_session.Values["sales_rep"] = sales_reps[0];

            // sales rep session error
            if err := auth_session.Save(request, response); err != nil {
                log.Printf("sales rep could not save session %s !\n", err.Error())
                InternalServerError(response, request)

            // sales rep login in success
            } else {
                log.Printf("logged in as sales rep %d !\n", manager.Id)
                Success(response, request, manager)     
            }

        // sales rep login failed
        } else {
            log.Printf("sales rep credentials not valid!")
            Unauthroized(response, request)
        }

    // login as a device
    } else if pin_code := fp.GetInt32("PinCode", -1); pin_code > 0 {

        db := database.Open()
        defer db.Close()

        if devices := db.GetDevice(&models.Device{ PinCode:pin_code }, nil); len(devices) > 0 {
            auth_session.Values["device"] = &devices[0]

            // device session error
            if err := auth_session.Save(request, response); err != nil {
                log.Printf("device could not save session %s !\n", err.Error())
                InternalServerError(response, request)

            // device login in success
            } else {
                log.Printf("logged in as device %d !\n", devices[0].Id)
                Success(response, request, devices[0])      
            }

        // device login failed
        } else {
            log.Printf("device credentials not valid!")
            Unauthroized(response, request)
        }


    // no valid credentials were provided   
    } else {
        log.Println("no valid deivce or manager credentials")
        BadRequest(response, request)
    }

}

}

Und das bekommen wir nach 30 Tagen in den Logs:

Geräte-Cookie: authentication-session=_KaJDVq4lIdQgeHiHcnSMw1IEPDyg3-9XEIBBPxw==; Pfad=/; Läuft ab=Mo, 15. Mai 2215 20:47:49 UTC; Max-Alter=6307200000

10.07.2015 15:56:04 /routers/api/Auth.go:70: Securecookie: Zeitstempel abgelaufen

Die Cookies scheinen alle nach 30 Tagen abzulaufen, unabhängig davon, was wir MaxAge eingestellt haben. Jede Hilfe ist für uns riesig. Vielen Dank!

Okay, ich habe ein minimales Demoprogramm erstellt, bei dem ich MaxAge für den gesamten Store auf 2 Monate (86400 * 60) gesetzt habe:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/sessions"
)

var store = sessions.NewCookieStore([]byte("some-appropriately-auth-key"))

func SomeHandler(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "example")
    if err != nil {
        http.Error(w, "No good!", 500)
        return
    }

    session.Values["gorilla"] = "sessions!"
    err = session.Save(r, w)
    if err != nil {
        http.Error(w, "No good!", 500)
        return
    }

    fmt.Fprintf(w, "%v", session.Values["gorilla"])
}

func main() {
    store.Options = &sessions.Options{
        MaxAge: 86400 * 60, // 2 months
    }

    http.HandleFunc("/", SomeHandler)
    log.Fatal(http.ListenAndServe(":8002", nil))
}

Dies gibt mir die Ablaufzeit, die ich erwarten würde:

Expiry time test

Wenn ich meine Uhr manuell um ~33 Tage vorstelle, bekomme ich auch den expired timestamp Fehler von securecookie - was falsch ist.

Löschen der Cookies im Browser, Zurücksetzen der Uhr auf jetzt() und Erzwingen von MaxAge auf den zugrunde liegenden *securecookie.SecureCookie folgt:

    store.Options = &sessions.Options{
        MaxAge: 86400 * 60, // 2 months
    }
    for _, s := range store.Codecs {
        if cookie, ok := s.(*securecookie.SecureCookie); ok {
            cookie.MaxAge(86400 * 90)
        }
    }

Dies gibt mir immer noch ein Browser-Cookie mit einem Verfallsdatum von +2 Monaten in der Zukunft, da das Feld maxAge jedes Codecs keinen Einfluss darauf hat, was der Browser sieht, wenn wir http.Cookie schreiben. Wenn Sie meine Uhr jedoch auf +33 Tage in die Zukunft stellen, wird der Fehler "Abgelaufener Zeitstempel" nicht generiert.

Also habe ich etwas gegraben:

  • Unser Fehler „securecookie: abgelaufener Zeitstempel“ wird generiert von https://github.com/gorilla/securecookie/blob/master/securecookie.go#L244 -L246 generated
  • Diese Zeile legt das MaxAge für die CookieStore-Erstellung fest: https://github.com/gorilla/sessions/blob/master/store.go#L54 -L56
  • Durch Aufrufen von CodecsFromPairs vor dem Anwenden der Optionen werden die s.MaxAge in jedem *securecookie.SecureCookie (als Teil des Codec-Slice) auf den Standardwert von securecookie festgelegt: 86400 * 30 .
  • Wenn wir unsere sessions.CookieStore.Codecs überschreiten (wie im obigen Snippet) und das MaxAge für jede Securecookie-Instanz auf this setzen, umgehen wir dies.

TL;DR : Die Hauptursache ist, dass das in den Browser geschriebene http.Cookie den Zeitstempel erhält, den wir in unseren Optionen festgelegt haben, aber der HMAC für die zugrunde liegende Securecookie-Instanz ist eine Verkettung von date|value|mac – wobei date - ist das _default_ MaxAge, das unsere Optionen nicht beheben. Die HMAC-Validierung schlägt anschließend fehl, sobald wir > 30 Tage erreichen.

Der Fix besteht darin, dass sessions.NewCookieStore über die erstellten Codecs reicht und Options.MaxAge auf jede *securecookie.SecureCookie Instanz anwendet, um die Nichtübereinstimmung zwischen dem Cookie-Ablauf und der HMAC-Zeitstempelvalidierung zu korrigieren.

cc/ @kisielk , um meine Ergebnisse zu validieren, bevor ich einen Fix

PS: Unabhängig davon ist es am besten, einen einzelnen DB-Pool in Ihrem Programm zu öffnen, sei es über einen globalen oder durch explizites Übergeben eines Zeigers. Das Öffnen und Schließen des Pools pro Anfrage hat einen Leistungseinbruch. *sql.DB ist für den gleichzeitigen Zugriff sicher.

Beachten Sie, dass dies auch die meisten Store-Implementierungen betrifft, da alle Securecookie zum Speichern der ID in ihrem Back-End-Store verwenden.

redistore hat die Hälfte einer korrekten Implementierung, wenn Sie SetMaxAge manuell aufrufen, wendet dies jedoch nicht direkt für den Standardfall an. Zufälligerweise ist die Standardeinstellung (30 Tage) die gleiche wie die von Securecookie.

Auf den ersten Blick scheint es, dass _alle_ anderen Speicher betroffen sind – das Festlegen eines MaxAge über die Options-Struktur wird nicht auf die zugrunde liegenden Securecookie-Instanzen angewendet, die von CodecsFromPairs generiert werden. Die meisten Endbenutzer wären nicht auf diesen Fehler gestoßen, da ich vermute, dass die meisten Benutzer Ablaufdaten _weniger_ setzen als das, was Securecookie implizit in den HMAC einbettet.

FWIW: Ich glaube auch, dass Cookie-Abläufe in "ferner Zukunft" nicht großartig sind, es sei denn, Sie haben einen sehr speziellen Bedarf dafür.

Außerdem möchte ich darauf hinweisen, dass der aktuelle Zeitstempel in der aktuellen Implementierung komplett unverschlüsselt ist. Theoretisch könnte ich das Cookie mit base64 decodieren, den Zeitstempel auf den aktuellen Tag ändern, das Cookie mit base64 codieren, und dann würde die MaxAge-Eigenschaft überhaupt keine Rolle spielen, selbst wenn Sie das options.MaxAge-Problem behoben haben.

Ja, das "Far Future"-Cookie war nur für einen Test, wir haben das MaxAge jetzt auf unserem Produktionsserver auf 0 gesetzt. Wir haben das Problem am Wochenende gefunden und eine ähnliche Lösung geschrieben, die jedoch etwas weniger prägnant ist als Ihre.

Danke, dass Sie sich mit dem Thema beschäftigt haben!

@wbaron - keine Probleme. Wir werden es bald im Upstream-Paket beheben und
Ich werde dich einmal anpingen, damit du nicht deine eigene Gabel pflegen musst (wenn du
will nicht).

@marksalpeter - Können Sie
name|date|value vor der base64-Codierung für eine String-Darstellung. Wenn
Sie decodieren auf der Client-Seite, Sie können den Zeitstempel dort nicht ändern (es ist
MAC'ed - es gibt nichts, was Sie wirklich modifizieren können) und wenn Sie es versucht haben, die
Cookie sollte die Validierung beim Decodieren (Anfrage) fehlschlagen, da der MAC fehlschlagen würde
korrekt gegen den Hash/Auth-Schlüssel zu dekodieren. Ref:
https://github.com/gorilla/securecookie/blob/master/securecookie.go#L185 -L191

Am Dienstag, den 14. Juli 2015 um 22:40 Uhr schrieb wbaron [email protected] :

Ja, der "Far Future"-Cookie war nur für einen Test, wir haben das MaxAge auf eingestellt
0 jetzt auf unserem Produktionsserver. Wir haben das Problem am Wochenende gefunden und
einen ähnlichen Fix wie deiner geschrieben, mit etwas weniger Prägnanz.

Danke, dass Sie sich mit dem Thema beschäftigt haben!


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/gorilla/sessions/issues/48#issuecomment -121257995.

@elithrar mein Fehler. Wenn ich die Cookies entschlüssele, erhalte ich [Zeitstempel]|[MAC-Informationen] Ich habe einfach angenommen, dass der Zeitstempel vor der maximalen Info das war, was Sie verwendet haben.

@elithrar, was du vorschlägst, klingt für mich gut. Schade, dass es auf jeden Laden einzeln angewendet werden muss :/

@kisielk - Auf
Geschäfte, aber es gab keine saubere Möglichkeit, Geschäfte von Drittanbietern effektiv zu koppeln
Options.MaxAge-Felder mit der Methode s.MaxAge in securecookie.

Ich erwäge, eine func CodecMaxAge(codecs []Codec, age int) []Codec Funktion in Securecookie bereitzustellen (Nachteil: kann keine Methode auf einem sein
interface + fügt der öffentlichen API hinzu), die intern den Typ übernimmt
Assertion + Aufrufe an MaxAge(age) auf jedem Codec. Dies bleibt überflüssig (Schleife
über Slice, geben Sie Assert bis Cookie-Typ ein) Code aus den Drittanbieter-Stores
da sie nur securecookie.CodecMaxAge(mystore.Codecs, mystore.opts.MaxAge) in ihrer NewXXXXStore Funktion aufrufen müssen, um die
zugrunde liegendes s.maxAge-Feld.

Offen für bessere Ideen, aber dies scheint das einfachste zu sein.

Am Freitag, 17. Juli 2015 um 8:03 Uhr Kamil Kisiel [email protected]
schrieb:

@elithrar https://github.com/elithrar was du vorschlägst hört sich gut an
mich. Schade, dass es auf jeden Laden einzeln angewendet werden muss :/


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/gorilla/sessions/issues/48#issuecomment -122133977.

Ja, ich denke, ist in Ordnung. Ich werde einige PRs an die anderen Geschäfte senden, um sie zu verwenden, sobald Sie das Wechselgeld hier landen.

Für Tracking-Zwecke:

  • [x] [gorilla/sessions](https://github.com/gorilla/sessions)
  • [ ] [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
  • [ ] [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
  • [ ] [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
  • [ ] [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb auf AWS
  • [ ] [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
  • [ ] [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache auf GAE
  • [x] [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
  • [ ] [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
  • [x] [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
  • [ ] [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
  • [x] [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
  • [ ] [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
  • [ ] [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite

Ich werde diese nachverfolgen, sobald die PRs zusammengeführt wurden.

Dieses Problem wurde automatisch als veraltet markiert, da es kein aktuelles Update gesehen hat. Es wird in ein paar Tagen automatisch geschlossen.

Wenn man sich dieses Problem ansieht , nachdem man es von einem anderen Paket gefunden hat, sieht es nicht so aus, als ob es geschlossen werden sollte ... Die Checkliste von @elithrar benötigt noch weitere 10 Überprüfungen, es sei denn, diese Informationen sind veraltet.

Es sollte stimmen: aber nicht alle Geschäfte werden aktiv gepflegt oder haben eine PR eingereicht.

Dieses Problem wurde automatisch als veraltet markiert, da es kein aktuelles Update gesehen hat. Es wird in ein paar Tagen automatisch geschlossen.

Dieses Problem wurde automatisch als veraltet markiert, da es kein aktuelles Update gesehen hat. Es wird in ein paar Tagen automatisch geschlossen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen