Every ONVIF integration eventually hits the same ghost: the password is
correct, the user exists, the camera answers discovery — and every
authenticated call comes back NotAuthorized. People blame the vendor,
the firmware, the client library. Most of the time, it's the clock.
ONVIF authentication is a WS-Security UsernameToken whose password digest
is Base64(SHA1(nonce + Created + password)). The device checks Created
against its own clock. A camera that has been running in a closet
without NTP for six months can be minutes — or hours — off. Your digest is
mathematically perfect and temporally unacceptable, so the camera says no.
The fix is in the spec, hiding in plain sight: GetSystemDateAndTime is
one of the few operations a device must answer without authentication,
precisely so a client can measure the offset first and sign against
device time.
I built a small tool around that lesson, and around the pile of similar
lessons ONVIF teaches you one deployment at a time.
onvif-scan
onvif-scan discovers every
ONVIF camera on a LAN and writes an asset inventory — JSON plus a
single-file HTML report:
onvifscan scan --user onvif_user
● 192.168.1.64 VendorA Cam X200 FULL ch=2 · H264 1920x1080 PTZ
● 192.168.1.65 VendorB Dome Y10 FULL ch=1 · H265 2560x1440
◐ 192.168.1.66 (unknown) PARTIAL ch=0
— discovered 3 · full 2 · partial 1 · …
Per device it records manufacturer, model, firmware, serial, channels,
per-profile codec / resolution / frame-rate limits, PTZ presence, clock
offset, and the RTSP stream URI for each profile (password-masked,
ready to hand to a stream-acceptance checker —
rkdoctor rtsp if you use
my other tool; the two are designed as a discovery → acceptance chain).
It's pure Python standard library. No dependencies, nothing to install on
the machine you carry onto a site, and it runs offline.
What the inventory checks, beyond fields
Fields are the easy part. The useful part is the findings:
- CLOCK_SKEW — device clock more than 60 s off. This is the finding that explains other people's auth failures.
-
UNAUTH_INFO_DISCLOSURE —
GetDeviceInformationanswered with no credentials at all. Plenty of older firmware does this. You want to know which cameras on a site do. - XADDR_MISMATCH — the address in the discovery reply is not the address that answered (dual NICs, NAT, an IP change the ONVIF stack didn't hear about). The tool falls back to the responder's address.
- MEDIA2_ONLY / LEGACY_MEDIA_ONLY — which media generation a camera actually speaks; newer devices are dropping ver10 Media entirely.
- AUTH_FAILED — your credential was rejected, once. Which brings me to the rule I'm proudest of:
A scanner must never lock you out of your own cameras
onvif-scan authenticates only with credentials you explicitly
supply — one per device from a mapping file, or one shared ONVIF user.
Each is tried exactly once. There is no default-password list, no
dictionary, no "try admin/admin just in case", and no retry loop:
several vendors lock accounts after repeated failed logins, and an
inventory tool that brute-forces its way in — however politely — would
be a liability on a real site, not a tool. Passwords are never written
anywhere; stream URIs are stored masked and the finished report is
scrubbed of every password used during the scan.
The whole tool is read-only, too: Get* queries only, no configuration
changes, no PTZ movement.
The other quirks, briefly
- ONVIF is off by default on much current firmware (Hikvision ≥ 5.5.0, Axis ≥ 5.40), and the ONVIF user is usually a separate account from the web UI user. Half of all "the scanner found nothing" is this. A zero result prints a hint saying so instead of a conclusion.
- Scopes are unreliable narrators. Some devices broadcast name/hardware/MAC/location; some broadcast one Profile URI and nothing else. Identity is assembled in layers, and what's missing is reported as missing — never guessed.
- Firmware XML is sloppy. Unbound prefixes, missing namespaces. The parser tolerates it (and flags it) instead of dropping the device.
There's a longer list in the repo's
vendor-notes.
Honest status
v0.1, verified end-to-end against simulated devices that model strict,
standard, and legacy vendor behaviours (the simulator is in the repo and
runs in CI — including the clock-skew case both ways: signed with local
time → rejected; signed with device time → accepted). What it is not
yet is field-verified: I'm collecting real-camera reports now. If you
run it on a real fleet, the most valuable thing you can do is file a
device-report issue with the (redactable) JSON attached. That's how the
vendor matrix grows.
Repo: https://github.com/anguoyang/onvif-scan — Apache-2.0, good first
issues are up, and if it saves you an afternoon of walking a site with a
laptop and a spreadsheet, a star helps the next integrator find it.
Top comments (0)