Marcelo Diaz scored in the 82nd minute after both sides had wasted a string of chances, and substitute Alexander Frei scored from the penalty spot deep in stoppage time after Luis Neto had seen red for a foul on Mohamed Salah.
It extends Basel's unbeaten home run in the competition to five matches, dating back to their second group game, while extending a poor run for Zenit which has seen them win only one away match in Europe in 11 attempts.
Basel began brightly in a fast start to the match, and Salah, Serey Die, Jacques Zoua and Diaz all had sights of Viacheslav Malafeev's goal inside the opening six minutes.
Roman Shirkov had Zenit's first effort in the eight minutes but his right-footed drive was easy enough for Yann Sommer.
Malafeev could only parry Die's 20-yard shot but David Degan was offside as he went for the rebound.
Salah then blazed a shot over but both sides seemed to be happy to shoot from long range, with Basle's Tomas Hubocan off-target at the other end.
The chances were piling up for Salah but he was not doing enough to test the goalkeeper, sending two over the crossbar in quick succession, while Axel Witsel's long-range shot at the other end was held by Sommer.
Zenit enjoyed the better start to the second half, but Danny's 20-yard shot drifted over while Hulk - quiet in the opening 45 minutes - saw his free-kick strike the wall before he sent an ambitious effort from long range over the bar.
Malafeev did well to keep out Fabian Schar's close-range header on the hour mark, while Sommer had to use his feet to deny Roman Shirokov.
After Malafeev denied Markus Steinhofer and Hulk fired wide, it was beginning to look like it would end goalless, but Diaz put paid to that eight minutes from time, following up when Malafeev could only parry Salah's shot.
Hulk fired over from 25 yards before Salah had a chance to double Basle's lead late on, wastefully firing over from close range after Malafeev parried Mohamed El-Nenny's shot into this path, but the second goal came regardless as Frei placed his penalty in the bottom left-hand corner of the net in the 93rd minute.