# How go-sql-driver/mysql Secures LOAD DATA LOCAL INFILE Requests

> Secure your LOAD DATA LOCAL INFILE requests with go-sql-driver/mysql. Discover its deny-by-default policy, explicit file registration, and secure reader abstractions.

- Repository: [Go SQL Drivers/mysql](https://github.com/go-sql-driver/mysql)
- Tags: security
- Published: 2026-03-02

---

**The go-sql-driver/mysql implements a deny-by-default policy for `LOAD DATA LOCAL INFILE` that requires explicit file registration, supports secure reader abstractions, and optionally allows all files via a DSN parameter.**

The `go-sql-driver/mysql` package provides a pure Go driver for MySQL that must carefully handle the `LOAD DATA LOCAL INFILE` command, which instructs the client to read local files and send them to the server. Without proper safeguards, this feature could allow a malicious server to exfiltrate arbitrary files from the client machine. The driver mitigates this risk through a three-layer security model defined in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go) and [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) that puts explicit control in the hands of the application developer.

## Three-Layer Security Model

### File Allow-List Registration

The primary defense mechanism is the **file allow-list** managed through the `RegisterLocalFile` and `DeregisterLocalFile` functions. The driver maintains an internal `fileRegister` map (lines 36-52 in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go)) that stores absolute paths of files explicitly permitted for upload.

When the MySQL server requests a local file, the driver's `okHandler.handleInFileRequest` method checks this map under a read lock. If the file path is not present in the registry, the request is rejected with a clear error: `"local file '<path>' is not registered"`.

### Secure Reader Handlers

For scenarios where data does not exist as a static file on disk, the driver supports **reader handlers** via `RegisterReaderHandler` and `DeregisterReaderHandler` (lines 68-78 in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go)). Applications register named handler functions that return `io.Reader` implementations.

To use a registered reader, the SQL statement must reference it with the `Reader::<name>` prefix. This abstraction prevents direct filesystem access while allowing streaming from memory, network sources, or compressed archives.

### DSN-Level Override

The `allowAllFiles` parameter in the Data Source Name (DSN) provides a global override. When set to `true` in `Config.AllowAllFiles` (defined in [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) lines 62-69 and parsed at lines 84-88), it disables the allow-list check and permits any local file to be sent.

This setting defaults to `false`, enforcing the principle of least privilege. It should only be enabled in controlled environments where the server is fully trusted.

## Request Handling and Enforcement Logic

The enforcement logic resides in `okHandler.handleInFileRequest` in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go) (lines 96-132). When the server issues a `LOCAL INFILE` request, the handler executes three distinct checks:

1. **Reader Path Detection**: If the requested name begins with `Reader::` (optionally prefixed with a slash), the handler treats it as a reader request and looks up the name in the `readerRegister` map.

2. **File Path Validation**: For standard file paths, the handler trims surrounding quotes and checks the `fileRegister` map. Only if `cfg.AllowAllFiles` evaluates to true will the driver proceed with unregistered files.

3. **Resource Opening**: Valid files are opened with `os.Open`, while valid readers are invoked to return an `io.Reader`. The driver then streams data in chunks respecting MySQL packet size limits.

## Thread Safety and Concurrency Protection

All registration structures are protected by `sync.RWMutex` to ensure safe concurrent access. The `fileRegister` and `readerRegister` maps are effectively read-only during request handling, preventing race conditions that could inadvertently expose files during concurrent connection operations.

## Practical Implementation Examples

### Registering a Specific File

```go
// Register the file before opening the connection
mysql.RegisterLocalFile("/home/gopher/data.csv")

db, _ := sql.Open("mysql", "user:pw@tcp(127.0.0.1:3306)/mydb")
_, err := db.Exec("LOAD DATA LOCAL INFILE '/home/gopher/data.csv' INTO TABLE mytable")

```

### Using a Custom Reader

```go
mysql.RegisterReaderHandler("csvdata", func() io.Reader {
    // Could be a gzip.Reader, bytes.Buffer, etc.
    return strings.NewReader("col1,col2\nval1,val2\n")
})

// Reference with Reader:: prefix
_, err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::csvdata' INTO TABLE mytable")

```

### Allowing All Files via DSN

```go
// Use with extreme caution - disables allow-list protection
dsn := "user:pw@tcp(127.0.0.1:3306)/mydb?allowAllFiles=true"
db, _ := sql.Open("mysql", dsn)
_, err := db.Exec("LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE mytable")

```

## Summary

- **Deny-by-default**: No local files can be transmitted unless explicitly registered via `RegisterLocalFile` or `allowAllFiles` is enabled in the DSN.
- **Explicit registration**: The `fileRegister` map in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go) maintains the allow-list of absolute paths validated by `handleInFileRequest`.
- **Reader abstraction**: `RegisterReaderHandler` enables secure data streaming without direct filesystem access using the `Reader::` prefix.
- **DSN control**: The `allowAllFiles` parameter in `Config.AllowAllFiles` ([`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) lines 62-69) provides a global toggle that defaults to secure `false`.
- **Thread safety**: `sync.RWMutex` protects registration maps during concurrent access across multiple connections.
- **Clear errors**: Unregistered resources produce explicit error messages indicating whether a local file or reader is not registered.

## Frequently Asked Questions

### What happens if I try to load a file that hasn't been registered?

The driver rejects the request and returns an error stating `"local file '<path>' is not registered"`. This occurs in `okHandler.handleInFileRequest` in [`infile.go`](https://github.com/go-sql-driver/mysql/blob/main/infile.go) when the path is missing from the `fileRegister` map and the `AllowAllFiles` configuration is false.

### Can I use wildcards or directories in RegisterLocalFile?

No, `RegisterLocalFile` requires explicit absolute file paths. The driver does not support directory traversal or pattern matching in the allow-list; each file must be registered individually to maintain strict control over what data can be exfiltrated from the client.

### Is the allowAllFiles setting secure for production use?

Generally no. According to the source code in [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go), `allowAllFiles=true` disables the primary security mechanism and should only be used when connecting to fully trusted servers. In production environments, prefer explicit file registration or reader handlers to minimize attack surface.

### How do I stream data from memory instead of disk?

Register a reader handler using `RegisterReaderHandler` with a unique name, then reference it in your SQL with the `Reader::name` syntax. The handler must return an `io.Reader`, allowing you to stream from `bytes.Buffer`, `strings.Reader`, or custom implementations without the driver touching the filesystem.