Description
SMSTunnel transforms your Android phone into a powerful SMS gateway for WordPress.
Key Features
- Use Your Own Phone – No third-party SMS gateway costs
- Two-Factor Authentication – Secure WordPress login with SMS 2FA
- End-to-End Encryption – Messages encrypted with RSA keys
- Quick Setup – Scan QR code from the mobile app
Emergency 2FA recovery
When SMS 2FA is required and the “server fallback” option is OFF (the secure default), a total SMS outage would lock every admin out of wp-login – and the fallback option can only be changed while logged in. To recover, edit wp-config.php over FTP/SSH (or use WP-CLI) and add this line above the “That’s all, stop editing” comment:
define('SMSTUNNEL_2FA_EMERGENCY_BYPASS', true);
While this constant is true, admins can log in without entering a 2FA code. Every use is recorded in the PHP error log. Remove the line as soon as SMS delivery is restored. Advanced users can instead hook the ‘smstunnel_2fa_emergency_bypass’ filter and return true.
External Services
This plugin connects to external services to provide certain functionality. Below are the details of each service:
SMSTunnel API
- Purpose: Core service that enables the plugin to communicate with the SMSTunnel mobile app for sending SMS messages from your phone
- When data is sent: During Quick Setup (when pairing via QR code), when sending SMS messages, and when verifying API connections
- Data sent:
- During setup: Site URL, site token (random identifier), admin email (for account creation)
- When sending SMS: Phone number, message content (encrypted if E2E is enabled), API key for authentication
- Service provider: SMSTunnel.io (NARBOWEB SRL)
- Privacy Policy: https://smstunnel.io/privacy
- Terms of Service: https://smstunnel.io/terms
SMSTunnel Authentication
- Purpose: Optional sign-in via Google, Facebook, or email to link your SMSTunnel account with WordPress
- When data is sent: Only when the admin uses the “Connect with Google/Facebook/Email” options on the plugin settings page
- Data sent:
- Google/Facebook: Redirects to smstunnel.io/auth/google or smstunnel.io/auth/facebook with a callback URL and CSRF state token
- Email login: Email and password sent to smstunnel.io/api/v1/auth/login
- After authentication: Fetches user profile from smstunnel.io/auth/me and creates an API key via smstunnel.io/api/v1/api-keys
- Service provider: SMSTunnel.io (NARBOWEB SRL)
- Privacy Policy: https://smstunnel.io/privacy
- Terms of Service: https://smstunnel.io/terms
Note: QR codes are generated locally using an embedded JavaScript library (qrcode.min.js). No external QR code generation services are used. All SMS messages are sent through your own Android phone – the SMSTunnel server only acts as a relay to connect WordPress with your phone.
Screenshots




Installation
- Upload the plugin to /wp-content/plugins/
- Activate the plugin
- Install the SMSTunnel app on your Android phone from https://smstunnel.io/integrations/android-app
- Go to SMSTunnel > Quick Setup and scan the QR code with the app
Reviews
Contributors & Developers
“SMSTunnel” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “SMSTunnel” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
1.0.9
- Security: Added an emergency break-glass bypass for 2FA so admins are not permanently locked out of wp-login during a total SMS outage (fail-closed fallback). Enable by adding define(‘SMSTUNNEL_2FA_EMERGENCY_BYPASS’, true); to wp-config.php (see “Emergency 2FA recovery” in Installation), then remove it once access is restored. Every activation is written to the PHP error log.
- Fix: API validation errors returned by the server as an array (NestJS class-validator) are now flattened to a readable string instead of storing the literal “Array” (PHP 8 “Array to string conversion”).
- Fix: The read endpoints (connection test, usage, message status, received messages) now handle HTTP 429 rate-limit responses consistently with the send endpoints.
- Fix: The login 2FA code request is now idempotent – reloading the login page reuses the existing code instead of resending an SMS and burning a rate-limit slot.
- Fix: Uninstall now also removes the locally generated RSA key pair (smstunnel_public_key, smstunnel_private_key) so no key material is left orphaned.
1.0.8
- Added: Optional hybrid end-to-end encryption (v2) using AES-256-GCM for the message payload plus RSA-OAEP for the AES key, removing the ~245 byte size limit of legacy RSA encryption for long SMS messages
- Added: “Use hybrid encryption (v2)” toggle in End-to-End Encryption settings (default OFF; requires an up-to-date SMSTunnel app that advertises v2 support)
- Added: Pairing now stores the device’s advertised E2E version so the encryptor only uses v2 for capable devices; falls back to v1 otherwise
- Compatibility: When the hybrid toggle is OFF (default), encryption behaviour is unchanged (RSA PKCS#1 v1.5), so existing paired devices keep working
1.0.7
- Security: Added rate limiting to public (nopriv) 2FA SMS endpoints to prevent SMS bombing (3 codes / 15 min per IP on login, 60s cooldown + 5/hour on phone-setup verification)
- Security: Added brute-force lockout on 2FA code verification (max 5 wrong attempts, then the code is invalidated) and switched to constant-time comparison (hash_equals)
- Security: 2FA server fallback (“login without 2FA when SMS unavailable”) now defaults to OFF to avoid fail-open authentication
- Security: Client IP detection now relies solely on REMOTE_ADDR (validated) and no longer trusts spoofable proxy headers
- Security: REST /setup-callback site token now compared in constant time (hash_equals)
- Security: Hardened admin-settings.js E2E pairing UI to use .text() for dynamic/i18n values (DOM XSS)
- Security: Added rate-limit (HTTP 429) handling for the SMS API client
- Security: Strict in_array() comparisons for role checks
- Fix: Synchronized uninstall cleanup with the option keys actually used by the plugin
- Added: Shared recipient validation helpers (smstunnel_validate_e164, smstunnel_is_premium_number, smstunnel_sanitize_recipient) for satellite form plugins
1.0.6
- Security: Added nonce validation (check_ajax_referer) to all nopriv AJAX endpoints including 2FA login and phone setup
- Security: Fixed DOM XSS in quick-setup.js, social-login.js, and admin-settings.js – all server/URL data now uses .text() instead of .html()
- Security: Escaped all remaining unescaped outputs in SMS history table
- Security: API key verification now uses X-API-Key header and configurable server URL (consistent with rest of plugin)
- Fix: Corrected AJAX action name mismatch for API key verification
- Documentation: Added SMSTunnel Authentication section to External Services (auth endpoints)
1.0.5
- Security: Moved all inline JavaScript to external files using wp_enqueue_script and wp_localize_script
- Security: Added OAuth state parameter validation to prevent CSRF attacks on OAuth callback
- Security: REST API /setup-callback now validates site_token in permission_callback instead of callback body
- Security: Removed all wp_add_inline_script calls – all scripts now in external .js files
- Code: Added $request parameter to all REST API permission_callback methods for PHP 8+ compatibility
1.0.4
- Documentation: Updated External Services section with complete service documentation
1.0.3
- Security: Replaced __return_true with documented custom permission_callback methods
1.0.2
- Security: Replaced inline scripts with wp_add_inline_script for proper enqueueing
- Security: Fixed XSS vulnerabilities by using textContent instead of innerHTML for server responses
- Security: Removed external QR code generation services (Google Charts, QR Server API) – all QR codes now generated locally
- Security: Improved escaping for all JavaScript strings using esc_js()
- Documentation: Updated External Services section to accurately reflect service usage
1.0.1
- Security: Added sanitization callbacks for all settings
- Security: Fixed escape output for translatable strings
- Security: Database queries now use prepared statements
- Security: Changed wp_redirect to wp_safe_redirect
- Security: Changed mt_rand to wp_rand
- Compatibility: Tested up to WordPress 6.7.1
1.0.0
- Initial release