fix: verify file id in URL matches the one inside the access token

We're using the file id from the access token, so changing the file id
in the URL shouldn't matter. However, the file id must represent a
single file.
It will also help to detect unwanted changes in the URL.
This commit is contained in:
Juan Pablo Villafáñez
2024-08-13 15:04:19 +02:00
parent bb78b16079
commit 9e7d570c79
4 changed files with 32 additions and 14 deletions

View File

@@ -3,7 +3,6 @@ package connector
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
@@ -20,11 +19,11 @@ import (
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/google/uuid"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/connector/fileinfo"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware"
"github.com/rs/zerolog"
)
@@ -1172,10 +1171,7 @@ func (f *FileConnector) generateWOPISrc(ctx context.Context, wopiContext middlew
}
// get the reference
resourceId := wopiContext.FileReference.GetResourceId()
c := sha256.New()
c.Write([]byte(storagespace.FormatResourceID(resourceId)))
fileRef := hex.EncodeToString(c.Sum(nil))
fileRef := helpers.HashResourceId(wopiContext.FileReference.GetResourceId())
// generate the URL for the WOPI app to access the new created file
wopiSrcURL, err := url.Parse(f.cfg.Wopi.WopiSrc)

View File

@@ -1,8 +1,13 @@
package helpers
import (
"crypto/sha256"
"encoding/hex"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
)
@@ -25,3 +30,11 @@ func GetCS3apiClient(cfg *config.Config, forceNew bool) (gatewayv1beta1.GatewayA
}
return client, err
}
// HashResourceId builds a urlsafe and stable file reference that can be used for proxy routing,
// so that all sessions on one file end on the same office server
func HashResourceId(resourceId *providerv1beta1.ResourceId) string {
c := sha256.New()
c.Write([]byte(storagespace.FormatResourceID(resourceId)))
return hex.EncodeToString(c.Sum(nil))
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
@@ -12,6 +13,7 @@ import (
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/golang-jwt/jwt/v4"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/rs/zerolog"
"google.golang.org/grpc/metadata"
)
@@ -45,6 +47,8 @@ type WopiContext struct {
// * A contextual zerologger containing information about the request
// and the WopiContext
func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handler {
// compile a regexp here to extract the fileid from the URL
fileIDregexp := regexp.MustCompile(`^/wopi/files/([0-9a-f]{64})(/.*)?$`)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
accessToken := r.URL.Query().Get("access_token")
if accessToken == "" {
@@ -89,7 +93,7 @@ func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handl
// we might need to check https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/common-headers
// although some headers might not be sent depending on the client.
logger := zerolog.Ctx(ctx)
ctx = logger.With().
wopiLogger := logger.With().
Str("WopiSessionId", r.Header.Get("X-WOPI-SessionId")).
Str("WopiOverride", r.Header.Get("X-WOPI-Override")).
Str("WopiProof", r.Header.Get("X-WOPI-Proof")).
@@ -98,7 +102,16 @@ func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handl
Str("FileReference", claims.WopiContext.FileReference.String()).
Str("ViewMode", claims.WopiContext.ViewMode.String()).
Str("Requester", claims.WopiContext.User.GetId().String()).
Logger().WithContext(ctx)
Logger()
ctx = wopiLogger.WithContext(ctx)
hashedRef := helpers.HashResourceId(claims.WopiContext.FileReference.GetResourceId())
matches := fileIDregexp.FindStringSubmatch(r.URL.Path)
if len(matches) < 2 || matches[1] != hashedRef {
wopiLogger.Error().Msg("file reference in the URL doesn't match the one inside the access token")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r.WithContext(ctx))
})

View File

@@ -2,8 +2,6 @@ package service
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"path"
@@ -19,6 +17,7 @@ import (
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware"
)
@@ -83,10 +82,7 @@ func (s *Service) OpenInApp(
// build a urlsafe and stable file reference that can be used for proxy routing,
// so that all sessions on one file end on the same office server
c := sha256.New()
c.Write([]byte(req.GetResourceInfo().GetId().GetStorageId() + "$" + req.GetResourceInfo().GetId().GetSpaceId() + "!" + req.GetResourceInfo().GetId().GetOpaqueId()))
fileRef := hex.EncodeToString(c.Sum(nil))
fileRef := helpers.HashResourceId(req.GetResourceInfo().GetId())
// get the file extension to use the right wopi app url
fileExt := path.Ext(req.GetResourceInfo().GetPath())