Sessions: Сторонние магазины неправильно применяют параметры к кодекам.

Созданный на 10 июл. 2015  ·  17Комментарии  ·  Источник: gorilla/sessions

Мы только что обновили этот пакет, и он неправильно устанавливает файлы cookie. Он устанавливает такие вещи, как _ga = GA1.1.922831813.14264788986 вместо обычных данных cookie в кодировке base64. Есть идеи, что происходит или когда это сломалось, чтобы мы могли вернуться к рабочей версии? Спасибо!

bug stale

Самый полезный комментарий

Обратите внимание, что это также влияет на большинство реализаций хранилищ, поскольку все они используют securecookie для сохранения идентификатора в своем внутреннем хранилище.

redistore имеет половину правильной реализации, если вы вручную вызываете SetMaxAge но не применяете это напрямую для случая по умолчанию. Так уж случилось, что значение по умолчанию (30 дней) такое же, как у securecookie.

При быстром взгляде кажется, что это касается _все_ других хранилищ - установка MaxAge через структуру Options не применяется к базовым экземплярам securecookie, созданным из CodecsFromPairs. Большинство конечных пользователей не столкнулись бы с этой ошибкой, поскольку я подозреваю, что большинство пользователей устанавливают даты истечения срока действия _ меньше_, чем то, что securecookie неявно встраивается в HMAC.

FWIW: Я также считаю, что срок действия cookie «далекого будущего» не очень хорош, если в нем нет особой потребности.

Все 17 Комментарий

Файл cookie "_ga" не имеет отношения к gorilla / sessions - это Google Analytics.
куки.

Если gorilla / sessions не устанавливает куки (не было никаких нарушений
изменения) можете ли вы опубликовать соответствующий код и вывод панели файлов cookie
из вашего браузера? (инспектор> ресурсы> файлы cookie в Chrome)

Пт, 10 июля 2015 г., 7:12. Marksalpeter [email protected]
написал:

Мы только что обновили этот пакет, и он неправильно устанавливает файлы cookie. Это
установка таких вещей, как _ga = GA1.1.922831813.14264788986 вместо обычного
Данные cookie в кодировке base64. Любая идея, что происходит или когда это сломалось так
мы можем вернуться к рабочей версии? Спасибо!

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/gorilla/sessions/issues/48.

Привет, Мэтт,

Я также работаю над проектом с Марком и могу рассказать вам более подробную информацию. Поэтому мы просто обновили пакет, потому что исходная проблема заключалась в том, что срок действия всех файлов cookie истекал через 30 дней, даже если мы установили для MaxAge какое-то действительно большое значение. Вот код аутентификации, который сейчас активен и вызывает у нас проблемы:

const (
AUTH_SESSION_NAME = "сеанс аутентификации"
)

var (
config = jconfig.LoadConfig (global.CONFIG () + "securecookie.json")
authKey = [] байт (config.GetString ("authorization_key"))
encryptionKey = [] байт (config.GetString ("encryption_key"))
session = sessions.NewCookieStore (authKey, encryptionKey)
)

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

}

// этот обработчик предоставляет разрешения в системе для устройств и менеджеров
// если запрашивающий уже авторизован, он отобразит их информацию. Чтобы авторизовать другого
// аккаунт, сначала клиент должен деаутентифицироваться.
//
// Параметры
// Пользователь:
//
// примечание: данные, возвращаемые этой функцией аутентификации, гарантируют только точность
// при первой аутентификации пользователя. в противном случае он будет отображать данные, кэшированные в их сеансе
//
func Authenticate (ответ http.ResponseWriter, запрос * 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)
    }

}

}

И вот что мы получаем в логах через 30 дней:

Cookie устройства: authentication-session = _KaJDVq4lIdQgeHiHcnSMw1IEPDyg3-9XEIBBPxw ==; Путь = /; Истекает = Пн, 15 мая 2215 20:47:49 UTC; Максимальный возраст = 6307200000

2015/07/10 15:56:04 /routers/api/Auth.go:70: securecookie: истек срок действия метки времени

Срок действия файлов cookie истекает через 30 дней независимо от того, что мы установили для MaxAge. Любая помощь вам будет огромна для нас. Спасибо!

Хорошо, я смоделировал минимальную демонстрационную программу, где я установил MaxAge для всего магазина на 2 месяца (86400 * 60):

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))
}

Это дает мне срок годности, который я ожидал:

Expiry time test

Установка моих часов вручную на ~ 33 дня также приводит к ошибке expired timestamp из securecookie что является ошибкой.

Очистка файлов cookie в браузере, возврат часов назад к текущему моменту () и последующее принудительное добавление MaxAge к базовому *securecookie.SecureCookie помощью следующего:

    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)
        }
    }

Это по-прежнему дает мне cookie браузера со сроком действия +2 месяца в будущем, потому что поле maxAge каждого кодека не влияет на то, что видит браузер, когда мы записываем http.Cookie . Однако установка моих часов на +33 дня в будущем не приводит к ошибке с истекшим сроком действия.

Итак, я покопался:

  • Наша ошибка «securecookie: expired timestamp» генерируется из https://github.com/gorilla/securecookie/blob/master/securecookie.go#L244 -L246
  • Эта строка устанавливает MaxAge при создании CookieStore: https://github.com/gorilla/sessions/blob/master/store.go#L54 -L56
  • Вызывая CodecsFromPairs перед применением параметров, он исправляет s.MaxAge в каждом *securecookie.SecureCookie (как часть фрагмента кодека) на безопасное cookie по умолчанию: 86400 * 30 .
  • Если мы превысим наши sessions.CookieStore.Codecs (согласно приведенному выше фрагменту) и установим MaxAge для каждого экземпляра securecookie this, мы обойдем это.

TL; DR : Основная причина заключается в том, что http.Cookie записанное в браузер, получает метку времени, которую мы установили в наших параметрах, но HMAC для базового экземпляра securecookie представляет собой конкатенацию даты | значения | mac - где дата _default_ MaxAge, который наши Параметры не исправляют. Проверка HMAC впоследствии не выполняется, когда мы достигаем> 30 дней.

Исправление будет заключаться в том, что sessions.NewCookieStore будет выбирать кодеки, которые он создает, и применять Options.MaxAge к каждому экземпляру *securecookie.SecureCookie чтобы он мог исправить несоответствие между истечением срока действия cookie и проверкой метки времени HMAC.

cc / @kisielk, чтобы проверить мои выводы, прежде чем я

PS: Вне связи с этим, лучше всего открывать один пул БД в вашей программе, будь то через глобальный или явно передавая указатель. Открытие и закрытие пула по запросу снижает производительность. *sql.DB безопасен для одновременного доступа.

Обратите внимание, что это также влияет на большинство реализаций хранилищ, поскольку все они используют securecookie для сохранения идентификатора в своем внутреннем хранилище.

redistore имеет половину правильной реализации, если вы вручную вызываете SetMaxAge но не применяете это напрямую для случая по умолчанию. Так уж случилось, что значение по умолчанию (30 дней) такое же, как у securecookie.

При быстром взгляде кажется, что это касается _все_ других хранилищ - установка MaxAge через структуру Options не применяется к базовым экземплярам securecookie, созданным из CodecsFromPairs. Большинство конечных пользователей не столкнулись бы с этой ошибкой, поскольку я подозреваю, что большинство пользователей устанавливают даты истечения срока действия _ меньше_, чем то, что securecookie неявно встраивается в HMAC.

FWIW: Я также считаю, что срок действия cookie «далекого будущего» не очень хорош, если в нем нет особой потребности.

Я также хотел бы отметить, что текущая отметка времени полностью не зашифрована в текущей реализации. Теоретически я мог бы base64 декодировать cookie, изменить метку времени на текущий день, base64 закодировать cookie, и тогда свойство MaxAge не будет иметь никакого значения, даже если вы исправите проблему options.MaxAge.

Да, файл cookie «далекого будущего» был предназначен только для тестирования, у нас сейчас MaxAge установлен на 0 на нашем производственном сервере. Мы обнаружили проблему на выходных и написали исправление, похожее, но немного менее краткое, чем ваше.

Спасибо, что изучили проблему!

@wbaron - без проблем. Скоро мы исправим это в исходном пакете и
Я свяжусь с вами, как только закончите, чтобы вам не приходилось поддерживать собственную вилку (если вы
не хочу).

@marksalpeter - Вы можете указать где? securecookie MACs
имя | дата | значение перед кодировкой base64 для строкового представления. Если
вы декодируете на стороне клиента, вы не можете изменить там метку времени (это
MAC'ed - на самом деле вы ничего не можете изменить), и если вы попытались,
cookie должен не пройти валидацию при декодировании (запросе), так как MAC не сработает
для правильного декодирования по ключу hash / auth. Ссылка:
https://github.com/gorilla/securecookie/blob/master/securecookie.go#L185 -L191

Во вторник, 14 июля 2015 г., в 22:40 wbaron [email protected] написал:

Да, файл cookie «далекого будущего» был предназначен только для тестирования, у нас установлен MaxAge на
0 на нашем производственном сервере. Мы обнаружили проблему на выходных и
написал исправление, подобное вашему, но с меньшей краткостью.

Спасибо, что изучили проблему!

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/gorilla/sessions/issues/48#issuecomment -121257995.

@elithrar моя ошибка. когда я декодирую куки, я получаю [timestamp] | [MAC'ed info], я просто предположил, что временная метка перед максимальной информацией была тем, что вы использовали.

@elithrar мне нравится то, что вы предлагаете. Жалко, что его приходится применять к каждому магазину индивидуально: /

@kisielk - Определенно обидно. Я не хотел прикасаться к третьему лицу
магазинов, но не было чистого способа эффективно объединить сторонние магазины
Поля Options.MaxAge с методом s.MaxAge в securecookie.

Я подумываю о предоставлении функции func CodecMaxAge(codecs []Codec, age int) []Codec в securecookie (обратная сторона: не может быть методом на
интерфейс + добавляет к общедоступному API), который внутренне выполняет тип
assertion + вызывает s.MaxAge (age) для каждого кодека. Это сохраняет избыточность (цикл
над фрагментом введите assert для типа cookie) код из сторонних хранилищ
так как им просто нужно будет вызвать securecookie.CodecMaxAge(mystore.Codecs, mystore.opts.MaxAge) в их функции NewXXXXStore чтобы установить
базовое поле s.maxAge.

Открыт для лучших идей, но это кажется самым простым.

Пт, 17 июля 2015 г., 8:03 Камиль Кисиэль [email protected]
написал:

@elithrar https://github.com/elithrar то, что вы предлагаете, звучит хорошо для
меня. Жалко, что его приходится применять к каждому магазину индивидуально: /

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/gorilla/sessions/issues/48#issuecomment -122133977.

Да, я думаю, все в порядке. Я пошлю несколько PR в другие магазины, чтобы использовать их, как только вы получите сдачу здесь.

Для отслеживания:

  • [x] [горилла / сеансы] (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) - Болт
  • [] [github.com/srinathgs/couchbasestore]] (https://github.com/srinathgs/couchbasestore) - Couchbase
  • [] [github.com/denizeren/dynamostore]] (https://github.com/denizeren/dynamostore) - Dynamodb на 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 в GAE
  • [x] [github.com/kidstuff/mongostore visible(https://github.com/kidstuff/mongostore) - MongoDB
  • [] [github.com/srinathgs/mysqlstore visible(https://github.com/srinathgs/mysqlstore) - MySQL
  • [x] [github.com/antonlindstrom/pgstore visible(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) - Риак
  • [] [github.com/michaeljs1990/sqlitestore] (https://github.com/michaeljs1990/sqlitestore) - SQLite

Я буду отслеживать их, как только PR будут объединены.

Эта проблема была автоматически помечена как устаревшая, поскольку для нее не было недавних обновлений. Он будет автоматически закрыт через несколько дней.

Рассматривая эту проблему после обнаружения ее из другого пакета, не похоже, что ее следует закрывать ... список @elithrar все еще требует еще 10 проверок, если эта информация не устарела.

Он должен быть точным: но не все магазины активно обслуживаются или имеют PR.

Эта проблема была автоматически помечена как устаревшая, поскольку для нее не было недавних обновлений. Он будет автоматически закрыт через несколько дней.

Эта проблема была автоматически помечена как устаревшая, поскольку для нее не было недавних обновлений. Он будет автоматически закрыт через несколько дней.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги