Romania centre-back Dorin Goian was the unlikely hero, scoring the only goal of a game that could result in Milan's lead being cut.
The rossoneri came into the game five points clear of champions and city rivals Inter Milan, who play Lecce on Sunday, while Napoli could also reduce the arrears to three points if they beat Cagliari.
Milan coach Massimiliano Allegri admitted that his team missed suspended striker Zlatan Ibrahimovic.
"We didn't play well in the first half and we made many mistakes in the final third," he said.
"In the second half we did better, we created a few chances but we weren't very clinical.
"We'll kick on from here. We started with a five-point advantage and we'll see what Inter and Napoli do.
"Ibrahimovic is important for us but tonight we needed to do more. We didn't do it but we'll analyse it calmly, knowing that at the end of the weekend we'll still be top."
Palermo's win also improved their hopes of securing European football next season.
They got off to a great start as they took the lead after just 10 minutes from a corner.
Ezequiel Munoz rose at the back post to head the ball back across goal where Giulio Migliaccio nodded it into the middle and the unmarked Goian had time to control the ball, turn and volley home.
Milan's response was almost immediate as a Clarence Seedorf corner somehow flashed across the face of the six-yard box with Alessandro Nesta sliding in a fraction too late to turn it in at the back post.
Palermo in general enjoyed the better chances and Javier Pastore played a one-two with Mauricio Pinilla before rifling just off target.
A Pastore back-flick then gave Pinilla a chance but Christian Abbiati got down low to his right to save.
Cassano had a chance on the break but fired wildly off target before Palermo goalkeeper Salvatore Sirigu came out quickly to save at Alexandre Pato's feet.
Palermo played on the counter-attack in the second half but wasted several glorious chances to stretch their advantage.
Pastore spurned the first two with a poor pass and then bad decision-making but when he later squared the ball across the six-yard box, Matteo Darmian put it wide on the slide.
Milan nearly made them pay on 65 minutes as substitute Robinho found space in the box and crossed for Mathieu Flamini, whose goalbound shot was punched away by Sirigu.
The visitors were pushing hard for a leveller and Sirigu made a fine save from a flying Gennaro Gattuso header.
But in injury time Palermo wasted another counter as substitutes Fabrizio Miccoli and Abel Hernandez couldn't convert a two-on-one situation.
Earlier, Argentine striker Mauro Zarate scored the only goal of the game as Lazio moved back into the Champions League qualification places with a 1-0 win over Cesena.
Having lost their position in the top four for the first time since the beginning weeks of the season last weekend following their derby defeat by Roma, Lazio quickly set themselves back on track.
Udinese can reclaim fourth place, though, if they beat Catania at home on Sunday.
Zarate gave Lazio a dream start on two minutes as he turned home a cross from Stefano Mauri.
The win was marred by an ugly incident as Brazilian midfielder Matuzalem was caught on television cameras punching Luis Jimenez in the face.
The referee did not see it but the Lazio player could face a hefty ban.
Lazio were also guilty of petulant and violent behaviour during the derby, in which they had two players sent off.
Coach Edy Reja admitted this had to change.
"On the pitch I didn't realise that Matuzalem punched Jimenez. Obviously there was some verbal offence beforehand but it's still an action that must be condemned," he said.