The easy way is to make
/etc/aliases owned by the web user. There are several ways you can make this more secure; it's a matter of what threats you're concerned about and how much complexity you're prepared to add to mitigate them.
If the application is only supposed to modify some aliases, you might be able to set up your MTA to read several alias files, and make only one of them writable by your application. I don't think this will buy you much security against a security hole in the application: if an attacker adds a redirection from
firstname.lastname@example.org, it doesn't matter which file it's been added to. But this could be useful in combination with review mechanisms.
If you want to review or audit what the application is doing, don't give it write access to
/etc/aliases. Instead, make it write a temporary file somewhere, then invoke a privileged program to validate this file and merge the data into
/etc/aliases (or a dedicated file as suggested above). The privileged program, let's call it
merge-myapp-aliases, should be executable by the web user, and setgid to a dedicated group. That is,
merge-myapp-aliases should be owned by
root:myapp-aliases and mode 2755 (
rwxr-sr-x). The reason to use a group rather than a user is that even if there's a security hole in the executable, the attacker won't be able to modify the executable to inject a trojan. The job of
merge-myapp-aliases is to validate the contents of its input (e.g. check that it's not setting aliases for system users), log its actions, and replace the alias file by a new version.
For robustnes, the dedicated alias file should be in a dedicated directory with group write permission. Make the program write the new file (
/etc/aliases.myapp/new), then atomically move it in place (
mv /etc/aliases.myapp/new /etc/aliases.myapp/current). That way you won't end up with an incomplete file if the program is killed midway for whatever reason.
An alternate way of pushing the changes into place, which has the advantage of automatically leaving a complete audit trail, is to have the CGI commit a file to a revision control system, and pull a trigger for another program to check out the latest revision. Most revision control systems have a way to validate commits.
If you want to restrict the alias file to this particular application, you're going to have to run the application with elevated privileges somehow. As above, you can make it setgid to a dedicated group.