Exactly this. There is usually a significant overlap between candidate block A and candidate block B. Further, whenever the winning chain (let's say chain A) is extended by another block, then any transactions which were included in candidate block B but not candidate block A will usually be included in the block A+1. Most transactions will go from 1 to 2 confirmations normally, and a few which were in block B but not in block A will go from 1 to 1 confirmations when included in block A+1. Only the very rare transaction will be included in block B but not in either of A or A+1, and will go from 1 confirmation back to unconfirmed. But of course unconfirmed doesn't mean money has been stolen - it just means the transaction is back in the mempool waiting to be mined again.
There are of course exceptions, most notably the one which was most publicized[1] and more recently this[2]. For the first scenario, the one which had 1 sat/vbyte fee was included in Slush and the other had a significantly higher fees, included in F2Pool. Whilst there probably wasn't any malicious intent, one can still exploit either the difference in transaction policy (by the miners to be included in their block) or just having a participating miner. The former is quite rare and hinges on the fact that a fork will occur and the second is more realistic.
If you want to accept one-conf transactions, then it would be the best to look out for any stale tips (Core relays the headers) and check if the transaction is in the other block as well. If it isn't in the other fork, then I would wait for another block.
[1]
https://forkmonitor.info/stale/btc/666833[2]
https://forkmonitor.info/stale/btc/676234