flipped-aurora/gin-vue-admin · warning

非法的key

Error message

非法的key

What it means

This validation error is returned by the internal localPath helper when the key contains path-traversal sequences ('..') or characters illegal in file names (\ / : * ? " < > |). It is a security guard preventing callers from addressing files outside the configured local store path.

Source

Thrown at server/utils/upload/local.go:115

	err = os.Remove(p)
	if err != nil {
		return errors.New("文件删除失败: " + err.Error())
	}

	return nil
}

// localPath 校验 key 并拼接出本地存储的绝对路径,复用 DeleteFile 中的路径穿越防护逻辑。
func (*Local) localPath(ctx context.Context, key string) (string, error) {
	// 检查 key 是否为空
	if key == "" {
		return "", errors.New("key不能为空")
	}

	// 验证 key 是否包含非法字符或尝试访问存储路径之外的文件
	if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
		return "", errors.New("非法的key")
	}

	return filepath.Join(global.GVA_CONFIG.Local.StorePath, key), nil
}

// Exists 检查本地文件是否存在,"不存在"统一降级为 (false, nil)。
func (l *Local) Exists(ctx context.Context, key string) (bool, error) {
	p, err := l.localPath(ctx, key)
	if err != nil {
		return false, err
	}

	info, err := os.Stat(p)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("function os.Stat() failed")

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Pass the bare filename key exactly as returned by UploadFile (flat, slash-free filename), not a path or a foreign backend's object key.
  2. Sanitize/normalize keys at ingestion: strip directories and reject illegal characters before persisting them.
  3. If you must support hierarchical keys, extend localPath to safely join and re-verify the result stays within StorePath (filepath.Clean + prefix check) instead of blanket rejection.
  4. Never construct keys from raw user input; derive them from the stored record's filename field produced by UploadFile.

Example fix

// before
key := objectURL // "https://cdn/x/a.png" or "sub/dir/a.png" from MinIO
local.DeleteFile(ctx, key) // 非法的key
// after
key := filepath.Base(strings.ReplaceAll(objectURL, "\\", "/")) // "a.png"
local.DeleteFile(ctx, key)
Defensive patterns

Strategy: validation

Validate before calling

func isSafeLocalKey(key string) bool {
    if strings.TrimSpace(key) == "" {
        return false
    }
    if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
        return false
    }
    return true
}
// usage: if !isSafeLocalKey(key) { return errors.New("invalid key") }

Type guard

type SafeKey string
func newSafeKey(raw string) (SafeKey, bool) {
    if strings.Contains(raw, "..") || strings.ContainsAny(raw, `\/:*?"<>|`) {
        return "", false
    }
    return SafeKey(raw), true
}

Try / catch

err := local.DeleteFile(ctx, key)
if err != nil && err.Error() == "非法的key" {
    return fmt.Errorf("key %q is not a local storage key (slashes/illegal chars)", key)
}

Prevention

When it happens

Trigger: Calling DeleteFile or Exists with a key such as "../secret.txt", "a/b.png", or any key containing backslashes or Windows-reserved characters — usually keys produced by another storage backend (e.g. MinIO/S3 keys with slashes) being passed to the local adapter.

Common situations: Mixing storage backends: S3/MinIO object keys use '/' separators and are rejected by the local adapter; user-supplied filenames reaching the delete path unsanitized; attacker-crafted keys probing for path traversal.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/b1dbc37c25dafcdf. Report an issue: GitHub.