The problem
Roll calls and signed sheets are easy to fake: a friend just answers for you. Saying you’re there is easy; proving it is harder. So when a student marks attendance, PresentProof checks several things at once: their registered phone, the classroom Wi-Fi, a QR code on the projector that changes five times a second, and their face.
The team split the work into four roles. I took face verification, which our plan ranked the hardest, because matching a face and proving that the face is really there are two separate problems. A face match can succeed on a video of the right person.
Starting from research
Before writing any code, I read through open-source face projects. The main one was glance-linux, a face unlock for Linux. Its tests passed, but its own security notes admit that a recorded video of the person can get through.
I took ideas from it, including the rule that the face match and the liveness result must come from the same face, in the same attempt. For the video problem, I built a simplified version of the Face Flashing method from an NDSS 2018 paper.
The face check, in layers
No single model handles every way to cheat, so the check is built in layers, and each one is aimed at a different trick.
- Checked registration. At first sign-in the student records 15 frames. From then on the face is locked to that account, and a face already registered to someone else is flagged.
- Continuous match. Throughout the QR scan, every frame must match both the registered face and the frame before it, so it stays the same person the whole way through. A gap longer than a second resets progress, and a stranger who stays in view cancels the attempt.
- Passive spoof check. A small anti-spoofing model, MiniFASNet, scores each face crop for signs of printed photos and screens.
- Screen colour challenge. When scanning starts, the server picks a random sequence of colours for the phone screen to flash. The face must reflect those colours back, in order. A video recorded earlier cannot contain colours that hadn’t been chosen yet.
If any layer cannot decide, the whole check is inconclusive, and an inconclusive check never marks anyone present. The student retries once; after that, a teacher reviews it.
What’s tested, and what isn’t
What has been shown, with the real models running on a computer and using sample images: registration saves and locks a face, another person’s face cancels the attempt, a duplicate face is flagged, and the registered student is marked present.
What hasn’t been measured yet: real phones at real distances, printed photos, screen photos, replayed videos, and false accept and reject rates. Until a replayed video is tested and fails, I treat the live-person check as unfinished.
What I learned
Passing tests are not a defence
The glance-linux commit I reviewed passed its 68 tests, but those tests used made-up face points. They showed that the decision logic runs. They didn’t show that it stops a photo or a video. That changed how I read my own results.
Green tests tell you the code runs. They don’t tell you what it stops.
A beat behind
The colour challenge shows seven colours, 400 milliseconds each, in an order the server picks. The reflection is faint: on the first phone, the smallest colour change measured was 1.84 brightness levels, against a floor of 1.5. The camera is also late. On one capture the colours barely lined up, until the frames were compared one step later, about 200 milliseconds. Then they matched almost perfectly.
The face was answering. It was just a beat behind.
Three answers, not two
A weak reflection doesn’t prove a spoof. It might just be a bright room. So the check can pass, fail, or be inconclusive, and a reflection that is too weak gets a hint to try again, never a spoof verdict. I would rather ask someone to scan again than let a guess through.
Paper reflects too
A printed photo would pass the colour check, so stopping it rests on MiniFASNet alone. I tried comparing the reflection across five patches of the face, since a flat print should reflect evenly. On a real phone the measurements were too noisy to trust, so that check only records for now and never blocks.
Credits
- Built with my teammates, who own the other parts: sessions and QR codes, the app and cameras, and accounts.
- glance-linux, for the reference design I learned from.
- OpenCV’s YuNet face detector, InsightFace’s ArcFace-trained face models (research use only), and Silent-Face’s MiniFASNet.
- The Face Flashing paper (NDSS 2018), where the idea for the colour challenge comes from.