Antonio Cassano scored a late equaliser for the hosts when they were already down to 10 men to cancel out Gergely Rudolf’s first-half opener.
It meant Milan failed to capitalise on second-placed Inter Milan’s 1-1 draw at Brescia on Friday night, meaning they stay five points clear at the top.
For Bari, who remain 12 points from safety, it was a welcome boost in an otherwise dire season.
But they will be kicking themselves for failing to hold onto their lead with a numerical advantage.
Milan now face the prospect of losing Ibrahimovic for the top of the table derby with Inter in three weeks, depending on the length of his ban.
“Unfortunately he had this half-foul reaction,” said his coach Massimiliano Allegri, who suggested it may be Ibrahimovic’s recent dry spell in front of goal that got to him.
“He has to stay calm, these periods can happen, he shouldn’t get annoyed because he hasn’t scored for a month, he’s giving a lot.”
It was a second disappointment in a week for Milan after crashed out of the Champions League with a 0-0 draw at Tottenham.
“We knew this game wasn’t won before kick-off but we made things difficult by going behind in a terrible first half,” added Allegri.
“We’re disappointed and bitter, this week we’ve twice played well but not got what we deserved.”
In a lively opening Milan looked full of verve as Ignazio Abate burst down the right and crossed for Robinho at the near post but his seventh-minute header was well off target.
Kamil Glik then threw himself in front of a Robinho shot after determined work from Ibrahimovic down the right before Gennaro Gattuso dragged an effort wide.
It wasn’t all one-way traffic as Simone Bentivoglio had a shot deflected just wide while a Sergio Almiron header from the resulting corner was too weak and easy for goalkeeper Christian Abbiati.
Ibrahimovic stung Bari shot-stopper Francois Gillet’s hands with a fierce 20-yard free-kick on 27 minutes.
But the basement sitters stunned their illustrious hosts six minutes before the break as Almiron took a quick free-kick, playing the ball past Milan’s defensive wall for Rudolf to run onto and shoot across Abbiati.
Milan came out for the second period with extra determination and Robinho soon had the ball in the net only for it to be chalked off as he’d strayed beyond the last defender.
Gillet then got down well to keep out a low shot from the Brazilian.
Full-back Luca Antonini wasted a glorious opportunity when teed up by busy substitute Urby Emanuelson, missing the ball completely with the goal gaping.
Ibrahimovic had the ball in the net on the hour mark but that too was disallowed as he controlled it with his arm.
He also dragged a shot wide as Milan upped the pressure but the big Swede then lost his head and was sent off for petulantly slapping Marco Rossi in the ribs.
Bari wasted two gilt-edge opportunities to close out the game through Almiron and Erik Huseklepp.
And they were made to pay as their fragile defence crumbled when Anontini got to the byline and pulled the ball back into the area for Cassano to volley home eight minutes from time.
Abbiati had to plunge to his right to keep out a Bentivoglio shot while Gillet dived to his left to deny a long-range Thiago Silva rocket.
Udinese moved into the top four and a Champions League qualifying place for the first time this season as Serie A top scorer Antonio Di Natale scored a brace in the 4-0 thrashing of Cagliari.
Lazio dropped to fifth as Francesco Totti also scored a brace to give Roma derby honours and close the giallorossi to four points off the top four.
It was a fifth straight derby victory for Roma while Lazio finished with nine men after late dismissals for Stefan Radu and Francesco Matuzalem.