Most clients do employ a checkpoint (which is updated from release to release). It is important to understand there is no centralized registry of checkpoints it is up to each developer to implement checkpoints (if any).
I believe the Satoshi client latest checkpoint is at roughly block 180,000. Individual nodes will reject any block submission prior to block 180,000 if it already has a valid chain. Also when bootstapping (no blockchain) individual nodes will reject any chain who's block hashes don't match the checkpointed hashes.
On edit:
Would have been smarted to just read the code.
https://github.com/bitcoin/bitcoin/blob/master/src/checkpoints.cpp static MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
(105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
(134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
(168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"))
(193000, uint256("0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"))
;
So currently the first checkpoint is at block 11,111 and the last one is at 193,000. One potential area of "attack" (and it would be a relatively weak one) is that the checkpoints are sparse. There is ~30K blocks between checkpoints. So a "bad node" could flood a node who is bootstrapping (building the historical blockchain). Now once the node hits a checkpoint (i.e. block # 11,111) the bad node will not be able to complete that chain. However the bad node could switch and provide correct blocks (possibly via another node) through block #11,111 and then provide another 22,222 bad blocks.
Since blocks can be built to have a large amount of transactions a "bad node" could greatly increasing the processing requirements of good nodes. This could be mitigated by providing a complete "checkpoint list" of every blockhash up through the last checkpoint. (i.e. the code above would have entries for blocks 1 through 193,000). On edit: I like Gavin's idea better. For lots of reasons it would be useful if the protocol allows pulling just block headers. It solves not only this weak attack (which shouldn't be a priority to fix) but also makes the protocol more robust.
In general nodes are relatively "naive" in dealing with misbehaving nodes. Likely as time goes on the method of picking peers will need to be enhanced. Essentially the node would keep track of all its current nodes plus all known but not connected nodes. Nodes which provide bad data, continually spam already known data, have high latency, relay tx significantly slower than peers, etc could be scored down and once they fall below a threshold the node would send them an error message, and ignore them for some period of time. The node could then attempt to connect to a new known but not yet used node. The code currently does a little of this but it could be expanded to make the protocol more hardened.
Nodes could also employ digital signatures which would aid in building a "web of trust". For example if I trust MtGox and blockchain.info I could mark their "node public keys" as trusted. They would be scored higher, the protocol could be expanded so when I query a trusted node for other nodes it not only gives me a list but also their scores. The whole "if I trust you then I can trust people you trust too" concept.