The SSH protocol and
SSH Applications - Part 3

SSH and SSH Applications from a Security Gateway Perspective
Part 3: SCP from a Gateway Perspective
In the previous parts of this series, we explored the SSH core protocol, the remote shell, and remote command execution. Now we turn to the first full-fledged application running over SSH: the Secure Copy Protocol (SCP).
The Command scp vs. the Protocol SCP
The scp
command on Linux and macOS is the most well-known interface to the SCP protocol. Windows has equivalents like pscp
(from PuTTY) and WinSCP, which also initially relied on SCP—hence their names.
However, SCP is a legacy protocol with significant limitations. To address these, SFTP was developed as its modern successor. To ease the transition, most tools retained their familiar command-line syntax while switching protocols under the hood. For instance: Since OpenSSH 9.0 (2022), the scp
command defaults to using the SFTP protocol.
So even if you're running a program called scp
, you're likely not using the SCP protocol—unless explicitly requested.
But the SCP protocol remains available for those commands and programs, as a fallback if the server is not supporting SFTP. And you can can force the legacy SCP protocol by using the -O
(capital letter O) option when calling scp, for instance.
Because some clients and servers still rely on SCP or attackers could force SCP usage, a Security Gateway — like our SSH Gateway — must continue to support SCP.
How SCP Uses SSH Channels
From the SSH protocol's perspective, SCP does not introduce any new message types, in fact, SCP is just another remote command execution, as described in part 1.
The principal flow has been outlined in part 2 and for SCP will look like:
- A SSH_MSG_CHANNEL_OPEN message with type "session".
- One or more SSH_MSG_CHANNEL_REQUEST messages to setup the environment
- A final SSH_MSG_CHANNEL_REQUEST messages with type "exec". The command is
scp
with some parameters.
Here, are examples of scp
commands and their resulting SSH exec messages:
scp command | SSH_MSG_CHANNEL_REQUEST message |
---|---|
scp -O testfile.txt user@example.org:/tmp | type=exec, cmd=scp -t /tmp |
scp -O user@example.org:/tmp/testfile.txt . | type=exec, cmd=scp -f /tmp/testfile.txt |
scp -O -r user@example.org:/tmp/sub . | type=exec, cmd=scp -r -f /tmp/sub |
Parameters explained:
-O
: switches scp into SCP mode-t
: upload mode (server is "target")-f
: download mode (copy "from" server)-r
: recursive copy
Only the remote path appears in the command sent to the server. The gateway has no visibility into the client-side file names or paths. In the case of recursive downloads, the only thing initially visible is the outer directory name.
At this point, the security gateway knows:
- That the SCP protocol is being used
- The direction (upload or download)
- The server-side root path for the transfer
All further information is only available when parsing the SCP protocol itself, which is transferred in subsequent SSH_MSG_CHANNEL_DATA messages.
How the SCP Protocol Works
Usually, we expect that the daily protocols we exchange via the Internet are well-defined or at least described in an Informational RFC or Internet Draft at IETF. Despite SCP being available for decades and available on basically every server on the Internet, such a well-defined protocol description does not exist.
A 2007 blog post by Jan Pechanec helped document its format via reverse engineering and is still one of the best references for the protocol.
From the SCP program’s perspective, it’s just two processes communicating via stdin/stdout with SSH securing the channel between them. The client-side SCP process is typically the controller, while the server-side SCP process responds.
Depending on the direction:
- Upload: client is data source, server is data sink
- Download: client is data sink, server is data source
To simplify the discussion, we’ll refer to roles as data source and data sink, independent of client/server.
However, a security gateway must still track the client side for policy enforcement. For example, if a virus is found, the resulting error must be routed back to the actual client, regardless of its SCP role..
SCP Message Format
SCP protocol messages are mostly plain text, with the exception of some binary control bytes. Here's an overview:
Message Type | Format | Example |
---|---|---|
OK | 0x00 | \x00 |
Warning | 0x01<Text><LF> | \x01My Warning\x0A |
Error | 0x02<Text><LF> | \x02My Error Msg\x0A |
File Start | C<flags> <len> <name><LF> | C0644 12 test.txt\x0A |
Directory Start | D<flags> 0 <name><LF> | D0755 0 sub\x0A |
Directory End | E<LF> | E\x0A |
File Data | <len bytes of data>0x00 | Hello World!\x00 |

SCP transfer of a folder "sub" with two files.
The image shows an example for a potential message flow of a folder with two files being copied. As we can see:
- The data sink starts the flow by indicating readiness via an initial OK message.
- The data source sends all file/dir start messages and the data payload.
- The data sink acknowledges each message; it only sends status responses (OK, Warning, or Error).
A file is transferred from the data source by sending a File Start message indicating the access control flags in the file system (in the example 0644) to be set, the length of the file in bytes and the name of the file. The next message will be a File Data message that must send exactly the number of bytes indicated followed by an additional Null-Byte delimiter.
When sending directories recursively, each start of a directory is indicated by a Directory Start message. Nested levels are simply additional Directory Start messages sent before the parent directory is ended again with the Directory End message.
Security Gateway Implications for SCP
For SSH Gateway to enforce policies during SCP transfers, it must:
- Parse SCP messages in both directions.
- Reconstruct server-side paths by combining the remote path from the exec command with the Directory/File messages during transfer
- Act at two policy enforcement points:
- At file start, if a decision can be made based on name/path
- After file complete, if content inspection is required (e.g., AV scan)
Holding back data until a decision is made requires the gateway to manage SSH channel flow control (i.e., window sizes). Otherwise, a pause in forwarding will stall the transfer.
Data modification is not allowed. The byte count specified in a File Start message must be matched exactly by the actual data payload. If a policy blocks one file in a recursive transfer, the gateway must complete the byte stream (e.g., sending null bytes) to allow the overall SCP session to continue. If a policy decides to block a file after allowing some previous parts of the data to pass, the best option for the gateway is to overwrite the file again.
Why SCP is Legacy
SCP is very limited:
- Not standardized by the IETF.
- Lacking robust features (e.g., no file transfer multiplexing, no resumable transfers, no directory listing, no symbolic link handling).
- Prone to silent failures, misbehaviors, or vulnerabilities.
Because of these limitations, SCP has largely been replaced by SFTP, which we’ll explore in the next part of this series.
The articles in this series
- The SSH Core Protocol and the SSH Remote Command
- SSH Remote Shell from a Gateway Perspective
- SCP from a Gateway Perspective [this article]
- SFTP from a Gateway Perspective [upcoming]
- Git via SSH from a Gateway Perspective [upcoming]
- TCP Tunnels via SSH from a Gateway Perspective [upcoming]