The visitors hit the bar and had two goals disallowed before Franck Kessie scored from the penalty spot to give them a deserved halftime lead.
Roma, who also had a goal chalked off in the first half, quickly responded after the break when midfielder Jordan Veretout curled in his 10th league goal of the season.
But a superb turn and finish from Ante Rebic put the visitors back in front after 58 minutes and they held on despite late pressure from the hosts.
The victory ends a four-match winless run in all competitions for Stefano Pioli's side, whose hopes of a first Serie A title in 10 years suffered a blow following back-to-back league defeats to Spezia and Inter.
Milan remain second on 52 points, four behind Inter and six ahead of Juventus in third, while Roma remain fifth with 44 points.