Skip to content

Commit

Permalink
feat: add HTTP request flow validator
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Aug 25, 2020
1 parent 08dd582 commit 1a6e847
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
46 changes: 46 additions & 0 deletions selfservice/flow/request.go
@@ -0,0 +1,46 @@
package flow

import (
"net/http"

"github.com/justinas/nosurf"
"github.com/ory/herodot"
"github.com/pkg/errors"

"github.com/ory/kratos/x"
)

var ErrOriginHeaderNeedsBrowserFlow = herodot.ErrBadRequest.
WithReasonf(`The HTTP Request Header included the "Origin" key, indicating that this request was made as part of an AJAX request in a Browser. The flow however was initiated as an API request. To prevent potential misuse and mitigate several attack vectors including CSRF, the request has been blocked. Please consult the documentation.`)
var ErrCookieHeaderNeedsBrowserFlow = herodot.ErrBadRequest.
WithReasonf(`The HTTP Request Header included the "Cookie" key, indicating that this request was made by a Browser. The flow however was initiated as an API request. To prevent potential misuse and mitigate several attack vectors including CSRF, the request has been blocked. Please consult the documentation.`)

func VerifyRequest(
r *http.Request,
flowType Type,
generator func(r *http.Request) string,
actual string,
) error {
switch flowType {
case TypeAPI:
// API Based flows to not require anti-CSRF tokens because we can not leverage a session, making this
// endpoint pointless.

// Let's ensure that no-one mistakenly makes an AJAX request using the API flow.
if r.Header.Get("Origin") != "" {
return errors.WithStack(ErrOriginHeaderNeedsBrowserFlow)
}

if len(r.Cookies()) > 0 {
return errors.WithStack(ErrCookieHeaderNeedsBrowserFlow)
}

return nil
default:
if !nosurf.VerifyToken(generator(r), actual) {
return x.ErrInvalidCSRFToken
}
}

return nil
}
22 changes: 22 additions & 0 deletions selfservice/flow/request_test.go
@@ -0,0 +1,22 @@
package flow

import (
"net/http"
"testing"

"github.com/stretchr/testify/require"

"github.com/ory/kratos/x"
)

func TestVerifyRequest(t *testing.T) {
require.EqualError(t, VerifyRequest(&http.Request{}, TypeBrowser, x.FakeCSRFTokenGenerator, "not_csrf_token"), x.ErrInvalidCSRFToken.Error())
require.NoError(t, VerifyRequest(&http.Request{}, TypeBrowser, x.FakeCSRFTokenGenerator, x.FakeCSRFToken), nil)
require.NoError(t, VerifyRequest(&http.Request{}, TypeAPI, x.FakeCSRFTokenGenerator, ""))
require.EqualError(t, VerifyRequest(&http.Request{
Header: http.Header{"Origin":{"https://www.ory.sh"}},
}, TypeAPI, x.FakeCSRFTokenGenerator, ""), ErrOriginHeaderNeedsBrowserFlow.Error())
require.EqualError(t, VerifyRequest(&http.Request{
Header: http.Header{"Cookie":{"cookie=ory"}},
}, TypeAPI, x.FakeCSRFTokenGenerator, ""), ErrCookieHeaderNeedsBrowserFlow.Error())
}

0 comments on commit 1a6e847

Please sign in to comment.