Overview
The ZKTeco Biometric Server implements multiple layers of security:
Device Authentication Password-based authentication for device connections using the ZKTeco protocol
SSL/TLS Encryption Automatic certificate generation and HTTPS support for API communications
Device Authentication
Password Field
Each ZKTeco biometric device can be configured with a communication password. This password is stored in the device configuration:
{
"principal" : {
"ip" : "192.168.1.205" ,
"port" : 4370 ,
"password" : 0 , # Device communication password
"timeout" : 5 ,
"name" : "Entrada Principal"
}
}
Default Password: 0 Most ZKTeco devices ship with password 0 (no password). Change this in your device’s admin settings for enhanced security.
How Device Authentication Works
The password is passed to the ZK library during connection establishment:
def conectar ( device_id ):
cfg = DEVICES [device_id]
zk = ZK(
cfg[ "ip" ],
port = cfg[ "port" ],
timeout = cfg[ "timeout" ],
password = cfg[ "password" ], # Auth password here
force_udp = False ,
ommit_ping = False
)
return zk.connect()
Connection Flow:
TCP Connection
Client establishes TCP connection to device IP:port
Authentication Challenge
Device requests password if configured
Password Validation
ZK library sends password, device validates
Connection Established
On success, connection object returned for operations
Password Configuration
Single-Device Mode:
ZK_PASSWORD = int (os.getenv( "ZK_PASSWORD" , 0 ))
Multi-Device Mode:
Each device can have its own password via the device registration API:
Register Device with Password
curl -X POST https://localhost:5000/devices \
-H "Content-Type: application/json" \
-d '{
"id": "secure_entrance",
"name": "Secure Entrance",
"ip": "192.168.1.210",
"password": 12345
}'
curl -X PUT https://localhost:5000/devices/secure_entrance \
-H "Content-Type: application/json" \
-d '{"password": 67890}'
Password Storage Device passwords are stored in plaintext in devices.json. Protect this file with appropriate filesystem permissions: chmod 600 devices.json
chown zkteco:zkteco devices.json
SSL/TLS Configuration
Automatic Certificate Generation
Both server implementations include automatic SSL certificate generation using PyOpenSSL:
def generar_certificado ():
if os.path.exists( CERT_FILE ) and os.path.exists( KEY_FILE ):
log.info( "Certificados SSL encontrados." )
return True
try :
from OpenSSL import crypto
# Generate 2048-bit RSA key
k = crypto.PKey()
k.generate_key(crypto. TYPE_RSA , 2048 )
# Create self-signed certificate
cert = crypto.X509()
cert.get_subject(). CN = "ZKTeco Server"
cert.set_serial_number( 1 )
cert.gmtime_adj_notBefore( 0 )
cert.gmtime_adj_notAfter( 365 * 24 * 60 * 60 ) # 1 year validity
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, "sha256" )
# Write certificate and key to disk
with open ( CERT_FILE , "wb" ) as f:
f.write(crypto.dump_certificate(crypto. FILETYPE_PEM , cert))
with open ( KEY_FILE , "wb" ) as f:
f.write(crypto.dump_privatekey(crypto. FILETYPE_PEM , k))
log.info( "Certificados SSL generados." )
return True
except ImportError :
log.warning( "pyopenssl no instalado. Corriendo en HTTP." )
return False
except Exception as e:
log.error( f "Error SSL: { e } " )
return False
Certificate Properties
Industry-standard key strength balancing security and performance
Secure hashing algorithm for certificate signing
Certificate expires after one year and must be regenerated
Certificate CN identifying the server
Certificate signed by its own key (not a CA-issued certificate)
Certificate File Locations
Certificates are stored in the server’s working directory by default:
CERT_FILE = os.getenv( "CERT_FILE" , "cert.pem" )
KEY_FILE = os.getenv( "KEY_FILE" , "key.pem" )
Customize via environment:
export CERT_FILE = "/etc/zkteco/ssl/cert.pem"
export KEY_FILE = "/etc/zkteco/ssl/key.pem"
Using Custom Certificates
For production deployments, replace self-signed certificates with CA-issued certificates:
Obtain CA Certificate
Get a certificate from Let’s Encrypt, DigiCert, or your organization’s CA
Place Certificate Files
Copy cert.pem and key.pem to your server
Set Permissions
chmod 600 /etc/zkteco/ssl/key.pem
chmod 644 /etc/zkteco/ssl/cert.pem
Configure Paths
export CERT_FILE = "/etc/zkteco/ssl/cert.pem"
export KEY_FILE = "/etc/zkteco/ssl/key.pem"
Restart Server
The server will detect and use your custom certificates
Let’s Encrypt Integration For automatic certificate renewal, consider using a reverse proxy like Nginx or Caddy with Let’s Encrypt support, and run the ZKTeco server in HTTP mode behind the proxy.
Server Startup with SSL
The server automatically configures SSL based on certificate availability:
if __name__ == "__main__" :
ssl_ok = generar_certificado()
proto = "https" if ssl_ok else "http"
log.info( f "Servidor ZKTeco — Multi-dispositivo dinamico" )
log.info( f " { proto } :// { API_HOST } : { API_PORT } " )
if ssl_ok:
app.run(
host = API_HOST ,
port = API_PORT ,
debug = False ,
ssl_context = ( CERT_FILE , KEY_FILE )
)
else :
app.run(
host = API_HOST ,
port = API_PORT ,
debug = False
)
Startup Flow:
Startup Logs
Successful HTTPS:
2026-03-06 14:30:15 [INFO] Certificados SSL generados.
2026-03-06 14:30:15 [INFO] Servidor ZKTeco — Multi-dispositivo dinamico
2026-03-06 14:30:15 [INFO] https://0.0.0.0:5000
Fallback to HTTP:
2026-03-06 14:30:15 [WARNING] pyopenssl no instalado. Corriendo en HTTP.
2026-03-06 14:30:15 [INFO] Servidor ZKTeco — Multi-dispositivo dinamico
2026-03-06 14:30:15 [INFO] http://0.0.0.0:5000
Client Connection Security
Accepting Self-Signed Certificates
When using self-signed certificates, clients must be configured to accept them:
Python (requests)
cURL
JavaScript (fetch)
import requests
# Development: Disable SSL verification
response = requests.get(
"https://localhost:5000/devices" ,
verify = False
)
# Production: Specify certificate path
response = requests.get(
"https://localhost:5000/devices" ,
verify = "/path/to/cert.pem"
)
Production Security Never disable SSL verification in production. Always use proper CA-issued certificates or add your self-signed certificate to the client’s trust store.
Network Security Best Practices
Firewall Configuration
Protect your ZKTeco server with firewall rules:
# Allow only from specific network
sudo ufw allow from 192.168.1.0/24 to any port 5000
# Or allow specific IP
sudo ufw allow from 192.168.1.100 to any port 5000
# Allow only from local network
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 5000 -j ACCEPT
iptables -A INPUT -p tcp --dport 5000 -j DROP
Device Network Isolation
Recommended Network Architecture
Segmented Network: Internet
|
[Firewall]
|
[Application Network] 192.168.1.0/24
|
+-- ZKTeco Server (192.168.1.100)
|
[Device Network] 10.0.0.0/24
|
+-- Biometric Device 1 (10.0.0.10)
+-- Biometric Device 2 (10.0.0.11)
Benefits:
Biometric devices isolated from internet access
Server acts as gateway between networks
Devices only accessible through server API
Reduced attack surface
API Authentication (Future Enhancement)
The current implementation does not include API authentication. Consider implementing:
API Keys Add API key validation middleware to protect endpoints from unauthorized access
OAuth 2.0 Implement OAuth for enterprise integrations with token-based authentication
JWT Tokens Use JSON Web Tokens for stateless session management
Rate Limiting Prevent abuse with request rate limiting per client/IP
Example API Key Middleware:
from functools import wraps
API_KEY = os.getenv( "API_KEY" , "your-secure-key-here" )
def require_api_key ( f ):
@wraps (f)
def decorated ( * args , ** kwargs ):
key = request.headers.get( 'X-API-Key' )
if key != API_KEY :
return _json({ "error" : "Invalid or missing API key" }, 401 )
return f( * args, ** kwargs)
return decorated
@app.route ( "/devices" , methods = [ "GET" ])
@require_api_key
def list_devices ():
# Protected endpoint
pass
Connection Timeout Security
Each device connection has a configurable timeout to prevent hanging:
{
"timeout" : 5 # Connection timeout in seconds
}
Benefits:
Prevents indefinite hangs on unreachable devices
Limits resource consumption from slow connections
Fails fast for better user experience
Adjust timeout based on network conditions:
Local network : 3-5 seconds
VPN/Remote : 10-15 seconds
Unreliable network : 20-30 seconds
Security Checklist
✓ Set device passwords
Configure non-zero passwords on all ZKTeco devices
✓ Isolate device network
Keep biometric devices on separate VLAN/subnet
✓ Disable unused device features
Turn off unnecessary device services and ports
✓ Use CA-issued certificates
Replace self-signed certificates in production
✓ Implement API authentication
Add API key or OAuth protection to endpoints
✓ Configure firewall rules
Restrict API access to trusted networks/IPs
✓ Secure configuration files
Set proper permissions on devices.json and certificate files
✓ Enable logging
Monitor and audit API access patterns
✓ Protect devices.json
Contains passwords - use filesystem encryption if needed
✓ Secure backup procedures
Encrypt backups containing device configurations
✓ Implement data retention policies
Determine how long to store attendance records