Beside XTS and CBC-ESSIV, are there any other modes of encryption that
are commonly used?
No. Just look at software like Veracrypt (XTS only) or dmcrypt.
There was a third notable scheme for this purpose, LRW, but it's deemed not secure enough:
The issues of LRW were:
a) An attacker can derive the LRW tweak key K2
from the ciphertext if the plaintext contains K2||0n or 0n||K2. Here
|| is the concatenation operator and 0n is a zero block.[3] This may
be an issue for software that encrypts the partition of an operating
system under which this encryption software is running (at the same
time). The operating system could write the LRW tweak key to an
encrypted swap/hibernation file.
b) If the tweak key K2 is known, LRW
does not offer indistinguishability under chosen plaintext attack
(IND-CPA) anymore, and the same input block permutation attacks of ECB
mode are possible.[4] Leak of the tweak key does not affect the
confidentiality of the plaintext.
(from https://en.wikipedia.org/wiki/IEEE_P1619#LRW_issue)
Which could be the most secure mode?
Depends on your requirements.
Both modes:
If the attacker can monitor changes of the data made by you (sounds like the attacker stole your computer, added something between harddisk and mainboard and gave it back), or at least get multiple copies at different point of times:
The attacker can do some traffic analysis, eg. he/she can detect if some block changed back to some data it had in the past. Ie. if you have a block has A, you change it to B, and later back to A (the same block, not different ones), then the attacker knows that the current content was there in the past too.
There's no real protection against this, but as said above, the attacker needs to monitor changes. Just stealing the laptop or anything like that is not enough (and if you get it back, don't trust it anymore, as simple as that. There are thousands of things that could be done while you weren't watching, many of them worse than just sniffing encrypted content).
XTS:
An attacker with similar monitoring capabilites, and also the possibility of changing blocks too, can roll back changes.
With the same A-B-A as above, (of course) the attacker can change your A block back to the B it was before (without knowing the unencrypted content, but still).
Just as reminder, overwriting the ciphertext is always possible, resulting in garbage after decryption. But here, it's not garbage, it's your own outdated content.
Tree-like integrity/authentication schemes (see below) mitigate this, but to be acceptable fast it needs to be integrated in the file system.
CBC-ESSIV:
It's always possible to change specific bits in the plaintext (even without monitoring changes or anything), if the attacker is willing to destroy another block for each changed block (=half of the blocks).
Again, tree-like integrity/authentication schemes mitigate this.
...if you want my opinion, XTS+auth.
what are good methods to prevent a malicious person from modifying the
data?
What you should NOT do:
Take every 4096byte-block (or some size like that), use eg. 4076 byte for data and 20 for an HMAC. Because
a) it doesn't solve parts of the problems above (eg. for XTS, replacing a block with it's old content means the old HMAC is there too ... failed); and
b) it's a mismatch with what OS+programs usually want. If the OS reads 8192 bytes (2*4096), you need a third block because the first two contain 40 byte HMACs and just 8152 byte data. A 4096 block for 40 byte... Then, when the OS wants the next part of the file, you either need to read the same block again (even slower, because non-sequential) or implement some sort of caching, which gets very cumbersome.
What you should NOT do either:
Collect the HMACs in own blocks. 4096/20 is about 204, so each 204 data blocks are followed by one block full of HMACs.
a) This does not solve the replacement problem either (the attacker can just replace a chunk of 205 blocks), and
b) It's even slower, because for every block you read/write, you also need the hash block which is far away (non-sequential to it's best).
What you could do on block-only-level (more secure but still slow):
Start like the solution above (full hash blocks), but also insert a second level of hash blocks (err, MAC blocks) for each eg. 200 hash blocks, a third level for each 200 second-levels, and so on, until you have one top block.
With this, replacing single blocks or any larger parts always can be detected, unless the attacker changes everything, ie. the whole disk content, back to it's old content etc. (this part can't really be solved with crypto. It's like exchanging the whole hard disk with one containing your old data. To mitigate this, you need to store the top block securely outside if the hard disk).
Problem is, again, slow because of non-sequential access. And calculating where the X-th data block is becomes a bit cumbersome because of the levels.
It won't get more secure. But depending on what you want to do, the slowness problem can be solved too: Don't do it just with blocks, without knowing their purpose, but integrate the hash tree into the file system.
There's no need to fetch additional blocks for the hashes, because they can be in the same place where filenames etc. are stored too. And knowing what blocks belong to the same file, detecting the programs access patterns for proper caching, etc.etc., all can be applied to the hash part too.