-
Notifications
You must be signed in to change notification settings - Fork 233
Description
Issue Summary
The MCP Go SDK's default session ID generation mechanism does not comply with the MCP specification's security best practices for session management.
Current Implementation Analysis
1. Session ID Generation Function
// go-sdk-main/mcp/util.go:22-30
func randText() string {
// ⌈log₃₂ 2¹²⁸⌉ = 26 chars
src := make([]byte, 26)
rand.Read(src)
for i := range src {
src[i] = base32alphabet[src[i]%32]
}
return string(src)
}
// go-sdk-main/mcp/util.go:20
const base32alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"2. Default Assignment Logic
// go-sdk-main/mcp/server.go:129-131
if opts.GetSessionID == nil {
opts.GetSessionID = randText
}3. Actual Call Path
// go-sdk-main/mcp/streamable.go:221-225
if sessionID == "" {
// In stateless mode, sessionID may be nonempty even if there's no
// existing transport.
sessionID = server.opts.GetSessionID()
}Complete Call Chain
HTTP Request → ServeHTTP()
↓
Check Mcp-Session-Id header
↓
If empty → server.opts.GetSessionID()
↓
Call randText() function
↓
Generate 26-character Base32 random string
Generated Session ID Characteristics
Format: 26-character Base32 encoded string
Character Set: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
Length: 26 characters
Example: ABCDEFGHIJKLMNOPQRSTUVWXYZ or 2ABCDEFGHIJKLMNOPQRSTUVWX
MCP Specification Requirement
According to the MCP specification's security best practices:
MCP servers SHOULD bind session IDs to user-specific information.
When storing or transmitting session-related data (e.g., in a queue), combine the session ID with information unique to the authorized user, such as their internal user ID. Use a key format like
<user_id>:<session_id>. This ensures that even if an attacker guesses a session ID, they cannot impersonate another user as the user ID is derived from the user token and not provided by the client.
Compliance Analysis
Current Implementation:
- No user ID binding
- Does not follow
<user_id>:<session_id>format
Specification Requirement:
- Format:
<user_id>:<session_id> - Example:
user123:ABCDEFGHIJKLMNOPQRSTUVWXYZ
Recommendation
** One Possible Solutions**
- Modify Default Implementation:
func (s *Server) generateSecureSessionID(userID string) string {
sessionID := randText()
return fmt.Sprintf("%s:%s", userID, sessionID)
}- Add Security Configuration Options:
type ServerOptions struct {
// Existing fields...
// New: Secure session ID generator
SecureSessionIDGenerator func(userID string) string
// New: User ID extractor from auth context
UserIDExtractor func(ctx context.Context) (string, error)
}