Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add OriginPatterns to AcceptOptions#198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
nhooyr merged 1 commit intomasterfromaccept
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 47 additions & 26 deletionsaccept.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,10 +9,11 @@ import (
"errors"
"fmt"
"io"
"log"
"net/http"
"net/textproto"
"net/url"
"strconv"
"path/filepath"
"strings"

"nhooyr.io/websocket/internal/errd"
Expand All@@ -25,18 +26,27 @@ type AcceptOptions struct {
// reject it, close the connection when c.Subprotocol() == "".
Subprotocols []string

// InsecureSkipVerify disables Accept's origin verification behaviour. By default,
// the connection will only be accepted if the request origin is equal to the request
// host.
// InsecureSkipVerify is used to disable Accept's origin verification behaviour.
//
// This is only required if you want javascript served from a different domain
// to access your WebSocket server.
// Deprecated: Use OriginPatterns with a match all pattern of * instead to control
// origin authorization yourself.
InsecureSkipVerify bool

// OriginPatterns lists the host patterns for authorized origins.
// The request host is always authorized.
// Use this to enable cross origin WebSockets.
//
// i.e javascript running on example.com wants to access a WebSocket server at chat.example.com.
// In such a case, example.com is the origin and chat.example.com is the request host.
// One would set this field to []string{"example.com"} to authorize example.com to connect.
//
// See https://stackoverflow.com/a/37837709/4283659
// Each pattern is matched case insensitively against the request origin host
// with filepath.Match.
// See https://golang.org/pkg/path/filepath/#Match
//
// Please ensure you understand the ramifications of enabling this.
// If used incorrectly your WebSocket server will be open to CSRF attacks.
InsecureSkipVerify bool
OriginPatterns []string

// CompressionMode controls the compression mode.
// Defaults to CompressionNoContextTakeover.
Expand DownExpand Up@@ -77,8 +87,12 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
}

if !opts.InsecureSkipVerify {
err = authenticateOrigin(r)
err = authenticateOrigin(r, opts.OriginPatterns)
if err != nil {
if errors.Is(err, filepath.ErrBadPattern) {
log.Printf("websocket: %v", err)
err = errors.New(http.StatusText(http.StatusForbidden))
}
http.Error(w, err.Error(), http.StatusForbidden)
return nil, err
}
Expand DownExpand Up@@ -165,18 +179,35 @@ func verifyClientRequest(w http.ResponseWriter, r *http.Request) (errCode int, _
return 0, nil
}

func authenticateOrigin(r *http.Request) error {
func authenticateOrigin(r *http.Request, originHosts []string) error {
origin := r.Header.Get("Origin")
if origin != "" {
u, err := url.Parse(origin)
if origin == "" {
return nil
}

u, err := url.Parse(origin)
if err != nil {
return fmt.Errorf("failed to parse Origin header %q: %w", origin, err)
}

if strings.EqualFold(r.Host, u.Host) {
return nil
}

for _, hostPattern := range originHosts {
matched, err := match(hostPattern, u.Host)
if err != nil {
return fmt.Errorf("failed to parseOrigin header %q: %w",origin, err)
return fmt.Errorf("failed to parsefilepath pattern %q: %w",hostPattern, err)
}
if!strings.EqualFold(u.Host, r.Host) {
returnfmt.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
ifmatched {
returnnil
}
}
return nil
return fmt.Errorf("request Origin %q is not authorized for Host %q", origin, r.Host)
}

func match(pattern, s string) (bool, error) {
return filepath.Match(strings.ToLower(pattern), strings.ToLower(s))
}

func selectSubprotocol(r *http.Request, subprotocols []string) string {
Expand DownExpand Up@@ -235,16 +266,6 @@ func acceptDeflate(w http.ResponseWriter, ext websocketExtension, mode Compressi
return copts, nil
}

// parseExtensionParameter parses the value in the extension parameter p.
func parseExtensionParameter(p string) (int, bool) {
ps := strings.Split(p, "=")
if len(ps) == 1 {
return 0, false
}
i, e := strconv.Atoi(strings.Trim(ps[1], `"`))
return i, e == nil
}

func acceptWebkitDeflate(w http.ResponseWriter, ext websocketExtension, mode CompressionMode) (*compressionOptions, error) {
copts := mode.opts()
// The peer must explicitly request it.
Expand Down
31 changes: 26 additions & 5 deletionsaccept_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -244,10 +244,11 @@ func Test_authenticateOrigin(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
origin string
host string
success bool
name string
origin string
host string
originPatterns []string
success bool
}{
{
name: "none",
Expand DownExpand Up@@ -278,6 +279,26 @@ func Test_authenticateOrigin(t *testing.T) {
host: "example.com",
success: true,
},
{
name: "originPatterns",
origin: "https://two.examplE.com",
host: "example.com",
originPatterns: []string{
"*.example.com",
"bar.com",
},
success: true,
},
{
name: "originPatternsUnauthorized",
origin: "https://two.examplE.com",
host: "example.com",
originPatterns: []string{
"exam3.com",
"bar.com",
},
success: false,
},
}

for _, tc := range testCases {
Expand All@@ -288,7 +309,7 @@ func Test_authenticateOrigin(t *testing.T) {
r := httptest.NewRequest("GET", "http://"+tc.host+"/", nil)
r.Header.Set("Origin", tc.origin)

err := authenticateOrigin(r)
err := authenticateOrigin(r, tc.originPatterns)
if tc.success {
assert.Success(t, err)
} else {
Expand Down
12 changes: 1 addition & 11 deletionsexample_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,7 +6,6 @@ import (
"context"
"log"
"net/http"
"net/url"
"time"

"nhooyr.io/websocket"
Expand DownExpand Up@@ -121,17 +120,8 @@ func Example_writeOnly() {
// from the origin example.com.
func Example_crossOrigin() {
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if origin != "" {
u, err := url.Parse(origin)
if err != nil || u.Host != "example.com" {
http.Error(w, "bad origin header", http.StatusForbidden)
return
}
}

c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
InsecureSkipVerify: true,
OriginPatterns: []string{"example.com"},
})
if err != nil {
log.Println(err)
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp