Quriostack

Anti-Spoofing: Detecting Masks and Deepfakes

Info
Anti-Spoofing: Detecting Masks and Deepfakes
Hermes Smith
·July 7, 2026· 11 min read
0 0

In 2024, a financial news outlet reported that an HR onboarding call had been bypassed using a synthetic face rendered on a small display. By the time anyone noticed, two fraudulent accounts had been opened and the incident response team was busy reviewing logs of every similar verification that month. Spoofing is no longer a research curiosity; it is an everyday threat with measurable financial impact.

Why This Matters

A face matcher is, on its own, a pattern-matching tool. It is happy to compare a printed photograph with a stored template if allowed. Once an attacker knows the format of the verification step, defeating the matcher alone becomes a small, repeatable task. Presentation-attack detection (PAD) exists because the surrounding context - the behavior, motion, microtextures and depth of a face - matters just as much as the pattern itself. Without PAD, a face login is essentially a fancy photo comparison.

There are three pressures pushing teams to invest here. First, regulators have started requiring explicit anti-spoofing for high-assurance remote identity flows. Second, attackers have industrialized their tools. Affordable 3D-printed masks, high-resolution printers and open-source live-deepfake pipelines make once-difficult attacks accessible to non-specialists. Third, the cost of failure has risen. Anti-money-laundering fines, customer remediation costs and reputational damage turn weak PAD into a balance-sheet event.

The other side of the ledger is that PAD adds friction, false rejections and complexity. A liveness check that requires a user to turn their head and read digits introduces time and can fail for users with mobility or vision impairments. The engineering challenge is to design PAD that is robust to attacks and humane for users.

The Core Idea

There are several categories of presentation attack and a layered defense is generally required.

Print and replay attacks involve showing a printed photograph or a video playing on a screen. These can often be defeated by detecting texture irregularities. Printed photographs have banding, halftone patterns, or moiré. Replays on LCD or OLED screens leak pixel grids, refresh-rate flicker and limited depth cues. Texture-based CNN classifiers, frequency-domain features and color-space inconsistencies are common defenses.

Mask attacks use 3D-printed or silicone masks. These are more challenging because they preserve geometry. Defenses include depth estimation from a stereo camera, time-of-flight sensing, structured light, or liveness of subtle microexpressions. Some masks contain subtle defects in texture and motion; PAD models that focus on skin microtextures and reflection patterns can detect them.

Live deepfake attacks use generative models to drive a face in real time. A remote camera sees a face that looks and moves correctly, but subtle artifacts appear in the eyes, mouth interior, or specular highlights. Some PAD systems ask the user to perform a random action such as reading digits or turning the head, which breaks the consistency of a replayed video. Active challenges introduce unique motion that a deepfake model must synthesize in real time.

Context attacks exploit the surrounding system. Replay at the network layer, packet tampering, image injection into a software pipeline and bypasses in the front-end are all possibilities. The pipeline must assume the worst about its inputs and verify them as close to the sensor as possible.

A robust PAD strategy is typically layered:

  • Hardware checks confirm that the input actually came from a sensor. Some platforms provide attestation that frames are real. Time-of-flight cameras add a depth channel.

  • Static PAD inspects a single frame for texture, reflection and color-space cues that distinguish real skin from a print.

  • Dynamic PAD inspects a sequence of frames for natural motion, micro-expression continuity and resistance to injected challenges.

  • Active challenges request a user-driven action whose difficulty varies with each session.

  • Cross-modal checks confirm that audio, if captured, is consistent with the lip motion visible in the video.

A common pitfall is to ship static-only PAD because it is easier to integrate. Static models can be fooled by high-quality prints and projected faces; modern attackers iterate quickly.

Defenses themselves have failure modes. Models can be biased against specific demographics, can fail under low light, can flag legitimate users with glasses or heavy makeup and can be evaded with custom adversarial perturbations. Calibration and monitoring matter as much as the model.

A Concrete Example

Most production PAD is delivered as part of a vendor SDK. To show how a custom solution can be assembled, a small Python pipeline is shown that combines texture heuristics with a CNN classifier trained to distinguish real frames from printed replays. This should be used as a starting point; production systems should add dynamic analysis and active challenges.

Python
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Iterable, List

import cv2
import numpy as np


@dataclass(frozen=True)
class PadResult:
    is_live: bool
    confidence: float
    reason: str | None = None


def laplacian_variance(image: np.ndarray) -> float:
    """Sharpness heuristic. Prints and replays often blur high-frequency content."""
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return float(cv2.Laplacian(gray, cv2.CV_64F).var())


def color_diversity(image: np.ndarray) -> float:
    """Count distinct colors in HSV space. Prints can collapse the gamut."""
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    _, _, v = cv2.split(hsv)
    # Count histogram bins with significant counts.
    hist, _ = np.histogram(v, bins=32, range=(0, 256))
    return float(np.count_nonzero(hist > 0))


def has_moire(image: np.ndarray) -> bool:
    """Detect periodic patterns typical of screen replays photographed by cameras."""
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    f = np.fft.fft2(gray)
    magnitude = 20 * np.log(np.abs(f) + 1)
    centered = np.fft.fftshift(magnitude)
    h, w = centered.shape
    peak = centered[
        int(h * 0.1):int(h * 0.9), int(w * 0.1):int(w * 0.9)
    ].max()
    return bool(peak > 240)  # tuned threshold; replace with calibrated value


class TextureClassifier:
    """A small toy classifier. Replace with a CNN trained on PAD datasets."""

    def __init__(self, model_path: Path) -> None:
        # In a real deployment, load a torch or tflite model. The example shows
        # the interface and how to combine signals.
        self.model_path = model_path

    def predict(self, face_crop: np.ndarray) -> float:
        # Placeholder: a real model returns probability of "live".
        sharpness = laplacian_variance(face_crop)
        diversity = color_diversity(face_crop)
        score = min(1.0, (sharpness / 120.0) * 0.6 + (diversity / 32.0) * 0.4)
        return score


def analyze(face_crops: Iterable[np.ndarray]) -> PadResult:
    crops = list(face_crops)
    if not crops:
        return PadResult(is_live=False, confidence=0.0, reason="no_frame")

    if any(has_moire(c) for c in crops):
        return PadResult(is_live=False, confidence=0.1, reason="moire_detected")

    classifier = TextureClassifier(Path("pad_model.pt"))
    scores: List[float] = [classifier.predict(c) for c in crops]
    avg = sum(scores) / len(scores)
    return PadResult(is_live=avg >= 0.6, confidence=avg)


def demo() -> None:
    # Wire this function to a real face detector that yields cropped face regions
    # from each video frame, then call analyze(crops) and act on the result.
    print(
        "PAD pipeline ready. Connect to a face detector and feed each crop here."
    )


if __name__ == "__main__":
    demo()

In a complete system, this would be combined with an active challenge. A trivial active challenge prompts the user with a four-digit sequence and requires them to speak or mouth those digits. The expected motion is hard to synthesize convincingly in real time and forces the attack model to maintain consistency under a stimulus it did not pre-record.

Python
import random


def generate_active_challenge() -> str:
    digits = "0123456789"
    return "".join(random.choice(digits) for _ in range(4))


def evaluate_active_challenge(challenge: str, observed_lip_motion: str) -> float:
    """Returns a confidence score. A real implementation uses a lip-to-text model."""
    return 1.0 if observed_lip_motion.strip() == challenge.strip() else 0.0

The two snippets illustrate the principle: heuristics plus a learned model plus an active challenge, each catching a different failure mode.

Common Pitfalls

  1. Trusting static PAD alone. Static models are the easiest to defeat. They should be combined with dynamic analysis and an active challenge whenever the consequence warrants it.

  2. Forgetting hardware attestation. A high-quality deepfake running on a desktop can stream into a mobile verification SDK. Without attestation, the SDK may never see the camera at all. Device attestation and integrity checks should be used where available.

  3. Calibrating thresholds on biased data. A PAD model that performs well on developers but poorly on users with darker skin, glasses, or head coverings will produce disparate outcomes. PAD performance should be audited across the populations served.

  4. Over-rejecting for a small risk reduction. Adding friction for every legitimate user to catch a rare attack is a poor trade. Risk-based triggers should be used: low-risk flows skip PAD, higher-risk flows apply it.

  5. Treating PAD accuracy as static. Models drift; user populations change. Performance should be re-measured quarterly against fresh attack samples.

  6. Single-vendor lock-in without escape hatch. Vendor PAD is convenient but can disappear overnight (acquisitions, deprecations). Plan for portability of training data and evaluation harness.

When to Use This ( and When Not To)

Layered PAD is used whenever the consequence of a successful spoof is significant, including financial onboarding, account recovery and access to restricted resources. PAD is skipped or kept minimal for low-stakes personalization such as photo tagging. A complete PAD pipeline should not be built in-house for an industry-critical flow unless the data, the ML operations team and the audit capacity exist; a mature vendor is usually the right answer here.

Real-World Case Study: The Synthetic Hire

In 2024, a US-based company discovered that a candidate had completed a video onboarding interview using a deepfake of a different person. The synthetic face was driven by an open-source live-deepfake tool running on a modest laptop. The company's verification pipeline had only a face match against an It would photo - no PAD, no liveness check. The fraud was discovered when the new "employee" attempted to enroll a corporate-issued device using a different face.

The investigation revealed that at least three other companies had been targeted the same way that quarter. The total loss across affected organizations was estimated in the seven figures (fraudulent transactions plus remediation costs).

Post-incident, all four companies deployed layered PAD: hardware attestation where supported, texture-based static analysis, dynamic motion analysis and an active challenge ("please turn your head left and read the digits on the screen"). The pattern matters because none of the four layers alone would have caught every variant - the layered defense raised the cost of attack beyond what most adversaries would invest.

The lesson: PAD is not a single feature. It is a defense-in-depth posture that compounds.

Comparison Table: PAD Approaches

Approach

Defeats print attacks

Defeats mask attacks

Defeats live deepfakes

Friction

Best for

Texture heuristics (Laplacian, FFT)

High

Low

Low

None

First-pass filter

Static CNN classifier

High

Medium

Low

None

High-volume low-risk flows

Dynamic motion analysis

Medium

Medium

Medium

Low

Interactive sessions

Active challenge (head turn, digits)

High

High

High

Medium

High-risk flows

Depth sensing (ToF, structured light)

High

High

Low

Medium

Hardware-supported flows

Hardware attestation

High

High

High

None

Modern mobile devices

Cross-modal (audio + lip motion)

Medium

Medium

High

Medium

Video sessions

Vendor SDK (iProov, Onfido, etc.)

High

High

High

Varies

Most production deployments

FAQ: Common PAD Questions

Q: Is single-frame PAD enough? A: For low-risk flows, yes. For high-risk flows (financial onboarding, account recovery), no. Single-frame models are the easiest to defeat with high-quality prints or projected faces. Layered defenses should be used for any consequential decision.

Q: How are deepfake attacks detected? A: A combination of texture anomalies (specular highlights, eye reflections), temporal inconsistencies (frame-to-frame coherence) and active challenges (random prompts that force real-time synthesis). No single signal is reliable; the layered approach is.

Q: Does PAD introduce accessibility concerns? A: Yes. Active challenges that require head turning, reading digits, or speaking can exclude users with mobility or vision impairments. Risk-based triggers and alternative flows should be provided so the security upgrade does not become a discrimination liability.

Q: How often should PAD models be re-trained? A: At least quarterly. New attack techniques emerge continuously; a model that was 99% effective in January may be 80% effective by December. A red-team exercise every quarter exposes the current gap.

Q: Can PAD run entirely on-device? A: Yes for static and dynamic PAD. Hardware attestation is also device-side. Active challenges require a network round-trip to validate responses. Hybrid architectures are common - heavy compute on-device, model updates from a server.

Q: What is the cost of a false rejection in PAD? A: It depends on the flow. For a banking app, a false rejection that takes 30 seconds to recover from is acceptable. For a daily employee badge, it is not. False rejection rates should be measured across the user population and tuned per flow.

Wrapping Up

Presentation attacks are an arms race and the defender's strongest move is layered defense. Texture analysis, dynamic motion checks and active challenges should be combined and the whole pipeline should be pinned to hardware attestation where possible. PAD should be run through periodic red-team exercises to verify the defense in real conditions, not just on its training data.

Today, the camera ingestion path of the verification system should be located and frames confirmed to be tagged with sensor provenance. If they are not, that is a small, valuable piece of evidence to add before the next audit cycle.

Further Reading

Hermes Smith

Comments (0)

Sign in to join the conversation.

No comments yet. Be the first to share your thoughts!