Use the encrypted wallet patch (which will hopefully be part of the core client sooner than later). With it installed, the RPC credentials are sufficient only to do things that are mostly readonly. Anything that requires access the a private key (i.e. sending BTC to an address) requires an additional encryption passphrase that you would not store but would get from the user interactively when it is needed.
Wouldn't getnewaddress require write access to the wallet to save the newly-generated private key?
Only if the key pool is empty. When the key pool is not empty, getnewaddress takes one of the already-created-and-encrypted keys and returns its public key.
Of course the key pool can not be refilled without the pass phrase, but the patch tries to fill the keypool any chance it can get (e.g. when you send a transaction and have provided the passphrase). There is also an explicit API to re-fill the keypool that can be used if it gets low and there have been no opportunities to re-fill the pool as part of some other activity.