prod@blog:~$

Sending SMS from Legacy Systems: Gateway Interactions

If you have ever been tasked with adding SMS capability to a legacy system, particularly for one-time passcodes or basic alerting - while SMS should no longer be considered secure, I had another reason to investigate SMS

Older remote access appliances, management gateways, and line-of-business systems often lack modern APIs or SDKs. Instead, they rely on SMS gateways that accept a single HTTP request containing everything required to send a message.

That usually means one URL.

This post walks through how those gateway URLs are constructed, why they are more powerful than they appear, how they can be safely generated using a small website, and how the same logic can be automated using PowerShell to send messages to many recipients. It also covers the risks that come with treating these URLs casually, particularly in cloud environments, and why SMS itself should no longer be considered a trustworthy authentication mechanism.

The Single-URL SMS Model

Many SMS gateways still operate on a very simple principle: if you can construct the correct URL, the message will be sent. There is no session state, no token exchange, and often no secondary verification. The URL itself is the instruction.

A typical request looks like this:

https://smagateway.bear.local:1089/Gateway
?Method=EAPIGateway.SendSMS
&Service=<service>
&Password=<password>
&Channel=<channel>
&Numbers=<mobile>
&SMSText=<message>
&Source=BEARS

Everything is embedded directly into the query string. Credentials, routing information, destination number, and message content all travel together. If the gateway can reach the destination network and the credentials are valid, the SMS is dispatched.

Two technical details matter more than most documentation admits.

The first is that mobile numbers are expected in international E.164 format. A UK number such as 078344xxxxx must be converted to 4478344xxxxx. Supplying a local format number may work in testing and then fail silently in production.

The second is that the SMS message must be URL encoded. Characters such as spaces, punctuation, and symbols cannot safely exist in a raw URL. A message like:

Test Message - www.a6n.co.uk

must be transmitted as:

Test%20Message%20-%20www.a6n.co.uk

Once those rules are understood, the gateway stops being complex. It becomes a formatting exercise.

Generating the URL Safely with a Website

Before automating anything, it is useful to be able to see exactly what is being sent. To do that, a small HTML page can be used to generate the gateway URL without actually sending a message.

The purpose of the page is not convenience; it is visibility.

The page accepts the same values the gateway expects and uses the browser’s built-in encoding to safely transform the message text. The key line is straightforward:

const encodedMessage = encodeURIComponent(message);

Once encoded, the final URL is assembled:

const url =
  "https://smagateway.bear.local:1089/Gateway" +
  "?Method=EAPIGateway.SendSMS" +
  "&Service=" + service +
  "&Password=" + password +
  "&Channel=" + channel +
  "&Numbers=" + mobile +
  "&SMSText=" + encodedMessage +
  "&Source=BEARS";

Nothing is transmitted. The page simply outputs the final URL so it can be inspected, validated, or handed to another system.

This step is often skipped, and it should not be. Being able to see the full payload in one place makes it immediately obvious what is happening and what could go wrong.



An Important Warning About Storing Generated URLs

It is worth pausing here to highlight a risk that is easy to miss.

If you take the fully generated URL from the website and store it in a cloud platform, documentation system, code repository, or ticketing tool, you are no longer in control of who or what interacts with it. Many cloud platforms automatically analyse stored content. They may inspect URLs, validate links, or attempt to “understand” what the code does.

With an SMS gateway, that analysis can have side effects.

If a platform attempts to access or validate the URL, it may trigger the gateway and send a real SMS to the number embedded in the request. This can happen without any explicit execution by a human.

For that reason, if the output of the website is ever stored, the credential elements and destination numbers should be redacted. Treat the full URL as a live instruction, not as inert text.

Automating Delivery with PowerShell

Once the URL format is proven, automation becomes a matter of repetition rather than invention. The PowerShell script uses the same logic as the website but applies it to a list of phone numbers stored in an external text file.

The file, numbers.txt, contains one number per line, in whatever format they are currently stored:

0783441xxxx
0770090xxxx
+44770090xxxx

The script explicitly reads from this file:

$NumbersFile = "numbers.txt"

if (-not (Test-Path $NumbersFile)) {
    Write-Log "ERROR: Numbers file not found"
    exit 1
}

$RawNumbers = Get-Content $NumbersFile | Where-Object { $_.Trim() -ne "" }

Each number is then normalised into E.164 format. UK numbers beginning with 07 are converted to 44, existing international numbers are left intact, and anything else is rejected:

function Convert-ToE164UK {
    param ([string]$Number)

    $clean = $Number -replace '\s','' -replace '\+',''

    if ($clean -match '^07\d{9}$') {
        return '44' + $clean.Substring(1)
    }
    elseif ($clean -match '^447\d{9}$') {
        return $clean
    }
    else {
        return $null
    }
}

The message text is encoded using the same principle as the browser:

$EncodedMessage = [System.Uri]::EscapeDataString($Message)

For each valid number, a single gateway URL is constructed and sent:

$Url = "https://smagateway.bear.local:1089/Gateway" +
       "?Method=EAPIGateway.SendSMS" +
       "&Service=$Service" +
       "&Password=$Password" +
       "&Channel=$Channel" +
       "&Numbers=$Mobile" +
       "&SMSText=$EncodedMessage" +
       "&Source=BEARS"

Each request is sent independently using Invoke-WebRequest. This is intentional. One number results in one request, one response, and one log entry. Failures are isolated and visible.

The script writes continuously to the console and to a timestamped log file in the directory it is run from. Nothing is hidden, buffered, or silently ignored.

Why SMS Should No Longer Be Trusted for Authentication

Understanding how easily an SMS can be sent is also the strongest argument against using SMS for authentication.

Unlike DNS, email, or federated identity systems, SMS has no meaningful sender validation. There is no requirement for a sender name to map to a real organisation, no reverse lookup, and no cryptographic binding between sender and recipient. Anyone with access to a gateway can send a message claiming to be almost anyone else.

This is why SMS phishing works so well and why SMS-based OTP should be considered a legacy compatibility mechanism rather than a security control. The transport itself provides no assurance of authenticity.

Where possible, systems should be moving toward federated login, authenticator apps, hardware-backed MFA, or passkeys. Those approaches bind authentication to cryptographic identity rather than a phone number that can be spoofed, intercepted, or reassigned.

Conclusion

This exercise demonstrates how thin the layer really is between “integration” and “execution” when it comes to SMS gateways. A single URL is enough to trigger a real-world action. With a small amount of scripting, that scales instantly.

The website and PowerShell script described here are useful tools for working with legacy systems, but they also serve as a reminder: SMS is easy to send, easy to spoof, and easy to misuse. Treat it accordingly, and wherever possible, design your systems so that you no longer need it at all..