Briefly: Avoid the shell! Use the form of system with multiple arguments that bypasses the shell (see also exec). Your security worries about anything [^A-Za-z0-9] will go away. Get your piping needs a different way:
system() won't let you capture the command's STDOUT, you can get that with the
LIST form of open (see also Safe Pipe Opens)
or the easier-to-use capturex from IPC::System::Simple.
Or, the commands you are calling may have switches that get them to write their output to a (temporary) file.
The commands you are calling probably don't require their input on STDIN?
Write your input for each command into a temporary file
and pass the filename to the command via its command line.
There's also IPC::Run3, which can avoid the shell (pass an arrayref as the first argument) and which allows you to redirect STDIN, STDOUT and STDERR. (One small downside being there's no support for piping stuff directly from one command to the next, so as above you'll have to keep things in memory or in temp files in between commands.)
As for any other modules, carefully read their documentation to see if they allow you to bypass the shell or not,
in my experience many of them don't.
Lastly, there are usually lots of system commands that can be emulated in Perl directly (often with CPAN modules), such as sendmail, so you might not need all of those external programs in the first place.
Yes, it's a little more work and a couple of extra temp files than just using good old shell pipes, but I've learned to love having less potential issues to worry about :-)
Note: File::Temp's UNLINK option is useful.