The Portuguese were forced to play with a man short for the entire second half after defender Jorge Fucile was sent off just before the break for picking up his second yellow card.
James Rodriguez had fired the visitors ahead in the 10th minute, slotting home at the far post from a Hulk cross following a quick counter-attack.
Shirokov levelled 10 minutes later when Porto keeper Helton failed to hold on to a Viktor Faizulin cross, allowing the midfielder to fire into an empty net.
Shirokov then made it 2-1 midway through the second half after another smart move by Faizulin before Danny sealed the win late on to put Zenit level on three points with Porto.
"We played a great game," Zenit's Italian coach Luciano Spalletti told a news conference. "We looked a bit tight and nervous in the first half but were able to calm down after the interval and that allowed us to show our full potential."
"Porto were a man short but I think we would still have won even if they had 11 players on the pitch."
Porto were already missing Colombian midfielder Fredy Guarin, who had been handed a two-game ban after being sent off in last month's European Super Cup clash with Barcelona.
They also lost Brazilian striker Kleber to injury in the first half and were dealt a major blow when Fucile received his marching orders from English referee Howard Webb.
"It's hard to say what would have happened if we'd played with a full team," said coach Vitor Pereira who took over from Andre Villas Boas after Porto won a treble of titles last season.
"We like to play an open game with lots of passes and control the tempo but tonight our opponents forced us into making too many mistakes."
Porto, twice European champions and winners of the Europa League last season, looked more dangerous in the first half.
Alvaro Pereira fired a powerful shot into the top corner, forcing Zenit keeper Vyacheslav Malafeyev to make a diving save.
But the Russians, desperate for their first win after a shock 2-1 defeat at APOEL Nicosia in their group opener two weeks ago, pressed their numerical advantage after the interval.
Alexander Kerzhakov, joint top scorer in the Russian league with 15 goals so far this season, wasted a great chance three minutes into the second half when he blasted his shot against the post from 10 metres with the goal at his mercy.
The home team, who were missing suspended Portugal defender Bruno Alves for a red card in Nicosia, also had an effort disallowed by Webb for offside against Kerzhakov before Shirokov put them ahead just past the hour mark.
Zenit's Portugal winger Danny wrapped up the victory 18 minutes from time against his compatriots to hand Porto their first defeat by a Russian team in European competition.
Get the best features, fun and footballing frolics straight to your inbox every week.
Thank you for signing up to Four Four Two. You will receive a verification email shortly.
There was a problem. Please refresh the page and try again.