如何在两个进程间安全交换秘密数字并验证被调用进程的合法性
Hey there! Let's break down and solve those two key security issues you're dealing with in your myapp1/myapp2 workflow. First, here's your original code formatted for clarity:
Original Code
myapp1
int main(int argc, char *argv[]) { int secret = 123; char buffer[20]; sprintf(buffer,"%d",secret); char *argv[] = { "/bin/myapp2", buffer, 0 }; char *envp[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 }; int retval = execve(argv[0], &argv[0], envp); printf("return value is: %d\n", retval); return 0; }
myapp2
int main(int argc, char *argv[]) { int val = atoi(argv[1]); some_process(val); return val; }
As you noted, we've got two critical problems:
- Secret exposure: The secret number is passed via command-line arguments, which are easily visible via
ps,/proc/<pid>/cmdline, or system monitoring tools. - Tampering risk: You can't confirm that the myapp2 being executed is the legitimate one—an attacker could replace it with a fake that returns the same value but does malicious things under the hood.
Let's fix each issue one by one.
Fix 1: Securely Pass the Secret Number
Command-line args are never safe for sensitive data. Here are the best alternatives:
Option 1: Anonymous Pipes (Recommended)
Pipes are private, unidirectional channels between parent/child processes—they don't show up in process lists or the filesystem. We'll modify myapp1 to write the secret to a pipe, and myapp2 to read from stdin instead of argv.
Modified myapp1
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, char *argv[]) { int secret = 123; int pipe_fd[2]; // Create the pipe if (pipe(pipe_fd) == -1) { perror("pipe failed"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } if (pid == 0) { // Child process: redirect pipe read end to stdin, then exec myapp2 close(pipe_fd[1]); // Close unused write end dup2(pipe_fd[0], STDIN_FILENO); // Attach read end to stdin close(pipe_fd[0]); // No longer needed after dup2 char *myargv[] = { "/bin/myapp2", NULL }; char *myenvp[] = { "HOME=/", "PATH=/bin:/usr/bin", NULL }; execve(myargv[0], myargv, myenvp); // Only reach here if execve fails perror("execve failed"); exit(EXIT_FAILURE); } else { // Parent process: write secret to pipe, then wait for child close(pipe_fd[0]); // Close unused read end char buffer[20]; snprintf(buffer, sizeof(buffer), "%d\n", secret); write(pipe_fd[1], buffer, strlen(buffer)); close(pipe_fd[1]); // Get the child's exit status int status; waitpid(pid, &status, 0); if (WIFEXITED(status)) { printf("return value is: %d\n", WEXITSTATUS(status)); } } return 0; }
Modified myapp2
#include <stdio.h> #include <stdlib.h> void some_process(int val) { // Your existing processing logic here } int main(int argc, char *argv[]) { int val; // Read secret from stdin instead of argv if (scanf("%d", &val) != 1) { perror("failed to read secret value"); exit(EXIT_FAILURE); } some_process(val); return val; }
Option 2: Unix Domain Sockets
If you need bidirectional communication or don't want to use parent/child processes, Unix domain sockets are a great alternative—they're fast, private, and support full duplex communication.
Option 3: Encrypted Environment Variables (Less Ideal)
While environment variables are still visible via /proc/<pid>/environ, encrypting the secret before passing it via env vars adds a layer of protection. But pipes are still far more secure.
Fix 2: Verify myapp2's Authenticity
To ensure you're running the legitimate myapp2, we need to validate its integrity. Here are two reliable methods:
Option 1: Precomputed Hash Check
Store the SHA-256 (or SHA-512) hash of the legitimate myapp2 in myapp1. Before executing myapp2, compute the hash of the current /bin/myapp2 and compare it to the stored value.
Hash Verification Code for myapp1
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/sha.h> // Replace this with the SHA-256 hash of your legitimate myapp2 #define LEGIT_MYAPP2_HASH "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2" int is_myapp2_legitimate(const char *path) { FILE *fp = fopen(path, "rb"); if (!fp) { perror("failed to open myapp2"); return 0; } unsigned char hash[SHA256_DIGEST_LENGTH]; SHA256_CTX ctx; SHA256_Init(&ctx); unsigned char buffer[1024]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp)) > 0) { SHA256_Update(&ctx, buffer, bytes_read); } SHA256_Final(hash, &ctx); fclose(fp); // Convert hash to hex string for comparison char hash_str[SHA256_DIGEST_LENGTH * 2 + 1]; for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(&hash_str[i*2], "%02x", hash[i]); } return strcmp(hash_str, LEGIT_MYAPP2_HASH) == 0; }
Add this check before calling execve in myapp1:
if (!is_myapp2_legitimate("/bin/myapp2")) { fprintf(stderr, "ERROR: myapp2 has been tampered with!\n"); exit(EXIT_FAILURE); }
Note: Compile with the OpenSSL library:
gcc myapp1.c -o myapp1 -lcrypto
Option 2: Digital Signature Verification (More Secure)
For stronger protection, sign the legitimate myapp2's hash with a private key, then have myapp1 verify the signature using the corresponding public key. This prevents attackers from modifying both myapp2 and the stored hash (since they don't have your private key).
Bonus: Lock Down File Permissions
Make sure /bin/myapp2 is owned by root and has permissions set to 755 (read/execute for all, write only for owner). This limits who can modify the file in the first place:
sudo chown root:root /bin/myapp2 sudo chmod 755 /bin/myapp2
内容的提问来源于stack exchange,提问作者ShahG

