The same way that it's fine to use `eval()` to parse JSON I assume.
This thread has bastardized serialization with storage even though the two are orthogonal concepts. If you don't want shelve to unpickle a hazardous object, give it a string or byte representation.
And that's exactly why it's so dangerous to use pickle, because it doesn't prevent you from executing untrusted code. It doesn't matter if you only pickle strings and bytes, as long as you unpickle any data, an attacker can replace that data with something which results in arbitrary code execution.
If you only want to store bytes, the safe way to do so is to use the dbm module directly, which doesn't pickle/unpickle the data you provide.
I find pickle useful for trusted objects that I created myself. pkl.xz is for example a fast way to reproducibly store and read Pandas dataframes. I have also found pickle useful for custom serialization of some objects that needed it.