# Critical Security Considerations When Deploying S-UI: A Production Hardening Guide

> Deploy S-UI securely. Learn critical hardening steps to protect user credentials, API tokens, and default admin access before production deployment.

- Repository: [Alireza Ahmadi/s-ui](https://github.com/alireza0/s-ui)
- Tags: best-practices
- Published: 2026-05-22

---

**S-UI stores user credentials and API tokens in plain text, ships with default admin credentials, and disables secure cookie flags by default, requiring immediate hardening before exposing the panel to the internet.**

S-UI is an advanced web panel built on Sing-Box for managing proxy configurations. While the repository provides a feature-rich REST API and web interface, it contains several intentional security shortcuts that make it unsuitable for production deployment without modification. This guide examines the critical security considerations when deploying S-UI, referencing specific source files and providing concrete hardening implementations.

## Authentication and Credential Storage Vulnerabilities

The authentication system contains multiple high-risk vulnerabilities centered around plain-text storage and weak defaults.

### Plain-Text Password Storage

In [`database/model/model.go`](https://github.com/alireza0/s-ui/blob/main/database/model/model.go), the `User` struct stores passwords as raw strings without hashing. Because the application uses SQLite by default, these credentials are written to disk in plain text, making them immediately accessible to anyone with file read access. The password verification logic in [`service/user.go`](https://github.com/alireza0/s-ui/blob/main/service/user.go) performs direct string comparison rather than cryptographic validation.

### Default Admin Credentials

The initialization logic in [`database/db.go`](https://github.com/alireza0/s-ui/blob/main/database/db.go) automatically creates a default admin account with credentials `admin/admin` if the database is empty. According to the repository README, these credentials are intended for initial setup, but the automatic creation logic means a fresh installation is immediately vulnerable to unauthorized access if discovered before configuration.

### Insecure Session Cookies

The session middleware in [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go) creates cookies with `Secure: false` regardless of whether HTTPS is configured. This allows browsers to transmit session cookies over unencrypted HTTP connections, enabling session hijacking through man-in-the-middle attacks. The cookies also lack `HttpOnly` and `SameSite` attributes, exposing them to XSS and CSRF attacks.

### API Token Exposure

API v2 tokens are stored in plain text in the `tokens` table defined in [`database/model/model.go`](https://github.com/alireza0/s-ui/blob/main/database/model/model.go). The token handling logic in [`api/apiV2Handler.go`](https://github.com/alireza0/s-ui/blob/main/api/apiV2Handler.go) returns these tokens to clients without masking in API responses. Tokens are passed in the `Token` HTTP header without HMAC verification or expiration enforcement, allowing indefinite reuse if leaked.

## TLS Configuration and Transport Security

S-UI supports both HTTP and HTTPS through the server implementation in [`web/web.go`](https://github.com/alireza0/s-ui/blob/main/web/web.go). When certificate files are configured via `webCertFile` and `webKeyFile` settings, the application creates a TLS listener and uses an auto-HTTPS listener (defined in [`network/auto_https_listener.go`](https://github.com/alireza0/s-ui/blob/main/network/auto_https_listener.go)) to upgrade plain connections.

However, if no certificates are provided, the UI falls back to plain HTTP, exposing credentials and session tokens in clear text. The auto-HTTPS implementation does not enforce HTTP Strict Transport Security (HSTS) or verify SNI mismatches, leaving connections vulnerable to downgrade attacks.

## Session Management Risks

Session data is stored in signed cookies using Gin's `cookie.NewStore(secret)`, with the secret generated once per installation in [`service/setting.go`](https://github.com/alireza0/s-ui/blob/main/service/setting.go). This secret is persisted in the database and never rotated automatically. If an attacker gains database access, they can forge valid session cookies using the stored secret. Additionally, the default `sessionMaxAge` is set to `0`, meaning sessions never expire by default and remain valid indefinitely.

## Database and File System Permissions

S-UI uses an SQLite database located under `$SUI_DB_FOLDER` (default `<binary>/db`). The folder is created with permissions `01740` (owner read/write/execute, group read, others none) as defined in [`config/config.go`](https://github.com/alireza0/s-ui/blob/main/config/config.go). Because the database contains all credentials in plain text, any process running as the same user or group can extract sensitive information. The system does not implement database encryption or vault integration for secrets.

## API Exposure and Rate Limiting

All API endpoints are exposed under `/api` (session-based) and `/apiv2` (token-based) via [`api/apiHandler.go`](https://github.com/alireza0/s-ui/blob/main/api/apiHandler.go). The current implementation lacks rate-limiting or brute-force protection on the login endpoint (`/api/login`). State-changing POST actions such as `/api/save` and `/api/changePass` do not implement CSRF tokens, allowing cross-site request forgery attacks against authenticated users.

## External Service Integration Risks

The Warp service in [`service/warp.go`](https://github.com/alireza0/s-ui/blob/main/service/warp.go) makes outbound HTTP calls using bearer tokens stored in memory. When debug logging is enabled, these tokens may be inadvertently logged to disk or output streams, exposing third-party credentials. Tokens are stored in the database and loaded into memory without encryption at rest.

## Production Hardening Implementation

To deploy S-UI safely, implement the following modifications drawn directly from the source architecture:

### Hash Passwords with bcrypt

Replace the plain-text logic in [`service/user.go`](https://github.com/alireza0/s-ui/blob/main/service/user.go) and [`database/model/model.go`](https://github.com/alireza0/s-ui/blob/main/database/model/model.go):

```go
import "golang.org/x/crypto/bcrypt"

// When creating or updating a user
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
    return err
}
user.Password = string(hash)

// When checking credentials (replace direct string comparison)
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
    // invalid password
}

```

### Secure Session Configuration

Modify `SetLoginUser` in [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go) to enable secure cookie flags:

```go
options := sessions.Options{
    Path:     "/",
    Secure:   true,          // only over HTTPS
    HttpOnly: true,          // not accessible via JavaScript
    SameSite: http.SameSiteStrictMode,
    MaxAge:   1800,          // 30 minutes
}
s := sessions.Default(c)
s.Set(loginUser, userName)
s.Options(options)
return s.Save()

```

### Enforce HSTS Headers

Add middleware early in [`web/web.go`](https://github.com/alireza0/s-ui/blob/main/web/web.go) to enforce HTTPS:

```go
engine.Use(func(c *gin.Context) {
    c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
    c.Next()
})

```

### Implement Secret Rotation

Add a rotation function in [`service/setting.go`](https://github.com/alireza0/s-ui/blob/main/service/setting.go) to invalidate existing sessions:

```go
func (s *SettingService) RotateSecret() error {
    newSecret := common.Random(32)
    return s.saveSetting("secret", newSecret)
}

```

Expose this via an admin-only endpoint and schedule regular rotation to mitigate the risk of forged session cookies.

### Database Hardening

Run S-UI under an unprivileged dedicated user. Restrict the database folder permissions to `0700` and the database file to `0600` in [`config/config.go`](https://github.com/alireza0/s-ui/blob/main/config/config.go). Consider encrypting the SQLite database using the SEE extension or migrating sensitive tokens to a dedicated vault rather than storing them in [`model.go`](https://github.com/alireza0/s-ui/blob/main/model.go).

## Summary

- **S-UI stores passwords and tokens in plain text** in [`database/model/model.go`](https://github.com/alireza0/s-ui/blob/main/database/model/model.go), requiring bcrypt implementation before production use.
- **Default admin credentials** (`admin/admin`) are automatically created in [`database/db.go`](https://github.com/alireza0/s-ui/blob/main/database/db.go) and must be removed or changed immediately.
- **Session cookies lack security flags** in [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go), necessitating `Secure`, `HttpOnly`, and `SameSite=Strict` attributes.
- **No rate limiting** on authentication endpoints in [`api/apiHandler.go`](https://github.com/alireza0/s-ui/blob/main/api/apiHandler.go) creates brute-force vulnerabilities.
- **SQLite database permissions** in [`config/config.go`](https://github.com/alireza0/s-ui/blob/main/config/config.go) may expose credentials to other system users without proper hardening.
- **TLS is optional** in [`web/web.go`](https://github.com/alireza0/s-ui/blob/main/web/web.go), risking credential exposure without enforced HTTPS and HSTS headers.

## Frequently Asked Questions

### How does S-UI store user passwords?

S-UI stores user passwords as plain text strings in the SQLite database. In [`database/model/model.go`](https://github.com/alireza0/s-ui/blob/main/database/model/model.go), the `User` struct contains a `Password` field that receives raw input without hashing. The application validates logins using direct string comparison in [`service/user.go`](https://github.com/alireza0/s-ui/blob/main/service/user.go) rather than cryptographic verification, meaning anyone with database read access can view all user credentials immediately.

### What are the default credentials for S-UI?

The default credentials are `admin` for both username and password. These are automatically created when the database initializes if no users exist, as implemented in [`database/db.go`](https://github.com/alireza0/s-ui/blob/main/database/db.go) lines 26-31. You must change these immediately after first login, as leaving the defaults unchanged grants full administrative access to anyone who discovers the panel.

### How can I secure the S-UI session cookies?

To secure session cookies, modify [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go) to set `Secure: true`, `HttpOnly: true`, and `SameSite: http.SameSiteStrictMode` on the session options. The current implementation hardcodes `Secure: false` regardless of TLS status, allowing cookies to leak over HTTP connections. You should also set a reasonable `MaxAge` (such as 1800 seconds for 30 minutes) rather than the default `0` (no expiration).

### Is S-UI safe to expose to the internet?

No, S-UI is not safe for internet exposure without significant modifications. The combination of plain-text credential storage, default weak credentials, missing CSRF protection, lack of rate limiting, and optional TLS enforcement creates multiple attack vectors. Before exposing S-UI externally, you must implement password hashing, enforce HTTPS with HSTS, add rate limiting, and remove default credentials.