| 开发者 | optionweb |
|---|---|
| 更新时间 | 2026年10月16日 17:54 |
| PHP版本: | 7.4 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
ow-consent folder to /wp-content/plugins/.[owco_dsar_form] to a page to open the data-subject request portal, and [owco_dnsmpi] where you want the "Do Not Sell or Share" control.OWCO_LEDGER_KEY in wp-config.php, so the consent ledger's signing key lives outside the database. The Audit ledger screen shows a freshly generated line to paste.OWCO_TRUSTED_PROXY in wp-config.php (the plural OWCO_TRUSTED_PROXIES is accepted as an alias), giving the proxy addresses or CIDR ranges. The rate limiting on the public REST endpoints keys on the client address; when every request arrives from the same proxy address and the proxy is not declared, the plugin cannot tell one visitor from another and falls back to a per-visitor split backed by a wider site-wide ceiling, which is a coarser limit.Yes, and it is designed around that. The front-end HTML it produces is identical for every visitor; consent is read from the cookie in the browser and applied before the page is painted. Trackers are blocked in the cached markup and released client-side. Only genuinely per-visitor responses — the DSAR verification page and REST responses containing personal data — are marked uncacheable. No nonce is printed in cacheable HTML either; the banner fetches one from a no-store endpoint before each write.
For most sites it covers the same ground: banner, blocking, audit log, data-subject requests, geo routing and generated documents. Two things it does not do: it is not a CMP registered with IAB Europe (see the TCF question below), and it does not provide legal advice or reviewed legal documents — the generator produces drafts that you must have reviewed.
It is tamper-evident, under one condition. Each row is signed with HMAC-SHA-256 over the previous row's hash, so a modified or deleted row breaks the chain and verification reports where. The guarantee rests on the signing key being outside the database, which holds when you define OWCO_LEDGER_KEY in wp-config.php. Without it, the plugin signs with a random key it keeps in the database: the chain is still built and checked, but an attacker with database access could re-sign it, and the admin tells you so. The key is never derived from WordPress's authentication keys and salts.
No. It is off by default, and once enabled it runs as a WP-Cron job at the frequency you choose (weekly by default), fetching a sample of your own pages server-side. It never runs during a visitor's page view. Note that WP-Cron only fires when your site receives traffic, or when you have set up a real cron task; on a site with DISABLE_WP_CRON and no system cron, the scheduled scan will not run.
Partly. The scheduled server-side sweep reads the Set-Cookie headers your pages return, so it finds cookies set by the server, including HttpOnly ones, as well as scripts, iframes, stylesheets and pixels. It executes no JavaScript, so cookies written by scripts in the browser are not visible to it; those are collected by a diagnostic probe that by default runs only for logged-in administrators. Treat the result as an inventory of what was seen, not as an exhaustive list — and both screens say so rather than leaving you to assume otherwise: the Scanner tab states whether the last sweep finished, how many pages it read, how many it could not fetch and whether a browser has ever reported back, and the generated cookie policy repeats the same reservations to the reader.
The visitor submits the form and receives a verification e-mail. Confirming the request on the linked page starts the Article 12.3 deadline and opens a WordPress personal-data request. From the DSAR screen you can download a JSON export produced by every exporter registered on the site (OW Consent's own records, WordPress core's, and any other plugin's), run every registered eraser, and close the request with a written response that is stored with it. What the export and the erasure cover depends on which plugins on your site register those hooks; anything that registers none has to be handled by hand.
Yes, when OW Forms is active and the integration is enabled. Every stored submission is linked to the consent in force at that moment: the pseudonymous browser token, the ledger entry that was current, the categories granted, the compliance profile, the hash of the published documents, the timestamp, and the exact wording of the consent checkbox that was accepted. Erasure requests cascade to OW Forms submissions and their uploaded files, matched on the e-mail hash OW Forms itself stores, and OW Forms submissions are included in WordPress's Export and Erase tools. Three limits are worth stating: no consent event is written into the ledger for a submission (the link references the entry already in the chain); submissions recorded before 1.2.0, and submissions made while OW Forms is configured not to store them, have no link; and the e-mail address remains stored in clear text by OW Forms, because it is needed to answer the sender — the hash is a lookup key, not a privacy measure.
Only with your own CMP ID issued by IAB Europe, and even then with a caveat. Without a CMP ID the module does not load at all. With one, it emits a correctly encoded TC String and the panel displays every purpose and special feature that string can claim, but TCF policy requires a registered CMP to present purpose-level and vendor-level choices of its own; here the purpose switches follow the categories and there is no vendor-level choice, so it is not a registered CMP and vendors are entitled to reject its signal. If TCF-gated advertising revenue matters to your site, use a certified CMP.
The privacy notice and the cookie policy are generated for all eleven profiles in the jurisdiction's own language: English, French for France, Belgium, Luxembourg and Quebec, Portuguese for Brazil. The legal notice and the terms exist only in French, built around French law; for any other jurisdiction the generator refuses to produce them rather than publishing an inappropriate document.
The interface is written in English and is entirely translatable: every string goes through gettext under the ow-consent text domain, and languages/ow-consent.pot ships as the template. No translation is bundled with the plugin. Locale packs are built on translate.wordpress.org and installed and loaded by WordPress itself, which is how a plugin hosted here is meant to be translated; to have the interface in your language, contribute or review the strings for that locale there.
Four positions (bottom bar, bottom card, centre modal, top bar), a light, dark or automatic theme, an optional site logo (your custom logo, falling back to the site icon), configurable labels and a floating re-open button. Any link or button carrying the class owco-open-preferences reopens the preferences panel.
"Reject all" is rendered with the same prominence as "Accept all". The close cross is disabled by default; when you enable it, closing the banner is recorded as a refusal of every optional category, never as a silent dismissal. It is present in the markup either way, hidden while the setting is off, and it is revealed and relabelled when a refusal cannot be recorded at all: a visitor whose choice the server will not accept is not left facing a banner they cannot dismiss, and that dismissal is not counted as a choice. The panel is keyboard operable, uses role="switch" toggles with a visible on/off state, traps focus in modal mode and restores it on close. Banner copy is translatable and can be replaced through the owco_banner_texts filter.
Third-party scripts, inline tracking snippets, iframes, tracking pixels (including inside noscript), analytics and marketing stylesheets and media sources are rewritten before consent and released afterwards, in document order. Resource hints (preconnect, dns-prefetch, preload) aimed at a catalogued third party are dropped outright rather than deferred, since a hint opens a connection and has nothing to restore. Stylesheets and web fonts served by a host classified as functional are left alone: neutralising them costs every first-time visitor a fallback-font page and buys no consent. Lazy-loading attributes used by caching plugins are neutralised so a lazy loader cannot restore a blocked URL. Blocked embeds are replaced by a placeholder carrying a per-category unblock button.
The plugin ships 183 detection signatures in data/tracker-catalog.json (118 URL patterns and 65 cookie names). The catalogue is editable from the Tracker catalogue screen — add, edit, delete, search, plus a view listing the rules that cannot match — and extensible through the owco_scripts_catalog filter. Unknown third-party scripts are allowed by default and unknown third-party iframes are blocked by default; both are settings, and a host allowlist (also exposed as the owco_blocker_allowlist filter) always wins.
Every consent action (accept, reject, partial save, GPC opt-out, renewal, withdrawal) is appended to a hash-chained table. Each row's HMAC-SHA-256 covers the previous row's hash and is written inside a transaction, so concurrent writes cannot fork the chain. Rows store a pseudonymised IP (IPv4 last octet zeroed, IPv6 truncated to /48), a user-agent hash, the page URL, the compliance profile, a hash of the legal documents in force, a fingerprint of the banner actually displayed and the plugin version — the elements needed to reconstruct what the visitor was shown, for GDPR Article 7(1) accountability. Chain verification walks the whole chain, reports the number of rows actually checked and describes each break individually, including a key rotation, which it distinguishes from a rewrite. Rows written before 1.2.0 carry an unkeyed digest that anyone with database access can recompute; verification counts them separately and refuses to declare a chain intact while any of them remain, because an unkeyed digest is not evidence.
The shortcode form ([owco_dsar_form]) collects access, rectification, erasure, portability, restriction, objection and CCPA opt-out requests. Submitting it requires an explicit declaration that the right is being exercised over the requester's own data, checked server-side and recorded with the request. The requester receives a verification e-mail; the link opens a confirmation page, and the request is only confirmed by an explicit action on that page, so a mail security scanner following the link cannot confirm an identity on the subject's behalf. The token is single-use, stored only as a SHA-256 hash, and expires after a configurable number of days (7 by default).
Verification starts the Article 12.3 clock (30 days by default, configurable from 1 to 30) and opens a matching WordPress core request. Reminders are sent seven days before the deadline and once it has passed, and the one-off two-month extension permitted by Article 12.3 notifies the data subject of the reasons. A request whose author never confirmed their identity cannot be exported, erased or recorded as honoured: the check is enforced on the server, not only by hiding the buttons, so nobody can assemble or destroy the record held about an address on the strength of a claim that was never proved.
A privacy notice and a cookie policy for each of the eleven compliance profiles, written against that jurisdiction's own statute and in that jurisdiction's language. The legal notice and the terms exist as French documents only; generation for a non-francophone profile is refused with an explicit error rather than publishing a French document to an English-speaking audience.
Documents are built from the settings on the Legal identity tab, and generation is blocked with the list of the fields still empty rather than publishing blank clauses. The cookie policy lists what the scanner actually found, with the vendor and a link to that vendor's own privacy policy. Unless you turn it off, every document ends with a notice stating that it is an automatically generated template that must be reviewed by a qualified professional before publication, with the generation date and the plugin version. Bodies and variables can be replaced through the owco_policy_template, owco_policy_vars and owco_policy_html filters.
The tracker table states what the sweep behind it actually covered, in the document's own language: whether a sweep has ever run, whether the last one could read no page at all, whether it was interrupted and after how many pages, how many pages could not be fetched, whether any browser has ever reported what the pages load at runtime, and the window beyond which a tracker no longer seen is dropped from the list. A finding the blocker cannot suppress — a cookie set by a Set-Cookie response header — is marked as such and the clause claiming that unclassified trackers are set only after consent carries the matching exception. An incomplete sweep is disclosed rather than published as a finished inventory.
Once enabled, the scanner runs on the WP-Cron schedule you choose — hourly, twice daily, daily or weekly (weekly by default). A sweep fetches a sample of your own public pages server-side (25 by default) and records third-party scripts, inline tracking signatures, iframes, stylesheets, tracking pixels and the cookies named in Set-Cookie response headers. Each finding records where it came from and whether the blocker can actually act on it, and can be classified into the blocking catalogue in one click.
The Scanner screen carries a coverage panel stating what the current findings rest on: whether a sweep has ever run, whether the last one finished or was interrupted by its time budget, how many pages it actually read, how many it could not fetch and the first error returned, and whether a browser has ever reported back through the probe. It is built from the stored status of the last sweep, so it is as accurate after a scheduled run as after a manual one, and a run that reached no page is shown as a failure rather than as a clean result. That is the panel to read before publishing a cookie policy from these findings.
All seven: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage, personalization_storage and security_storage. The six consent categories (necessary, functional, analytics, marketing, preferences, social) are mapped to them from a single source of truth, so the banner, the blocker and the page bootstrap cannot disagree. The default call is printed as static markup and the update is computed in the browser, which keeps the page cacheable.
It exposes __tcfapi, the __tcfapiLocator iframe and a TC String encoder, and writes the euconsent-v2 cookie only after the visitor has acted — never on a first view. It requires a CMP ID assigned to you by IAB Europe: with no CMP ID the module refuses to load at all, rather than emitting a string carrying CMP ID 0, and it refuses to invent a vendor-list version when the Global Vendor List has not been cached yet. The vendor list is stored in a transient, refreshed by a daily cron job, kept for a week, and served stale rather than re-fetched when a refresh fails.
When the module is enabled, the preferences panel shows one switch per TCF purpose the string can actually claim on this site and one checkbox per special feature the publisher has declared, so nothing is encoded into the string that was not displayed. A special feature stays off unless it is ticked; "Accept all" does not turn it on.
A "Do Not Sell or Share My Personal Information" control is injected in the footer, or placed with the [owco_dnsmpi] shortcode. The first click records the opt-out directly, as California Regs 7026(a)(1) requires, rather than merely opening the preferences panel. On a site configured under a US profile the control is visible for everyone, with no JavaScript involved. When geo routing is enabled, the same markup is served to every visitor and revealed in the browser for those whose own jurisdiction requires it, so a mandatory link is never decided by whichever visitor happened to populate the page cache. Sites with geo routing off render nothing extra.
The Global Privacy Control signal (the Sec-GPC header and navigator.globalPrivacyControl) is treated as a binding universal opt-out under the CCPA and US state profiles: the opt-out is recorded once per browser session, and the visitor is told when it replaced a choice they had previously saved. Under GDPR-family profiles GPC is treated as a hint — optional categories are pre-denied in the interface and the banner is still shown — because consent there must be an affirmative act.
It is optional and off by default. When enabled, the visitor's country is resolved from Cloudflare CF-IPCountry, CloudFront CloudFront-Viewer-Country, a server-set GEOIP_COUNTRY_CODE, or a local MaxMind GeoLite2 country database read by a reader bundled with the plugin (no Composer package and no vendor directory required). Proxy headers are trusted only when the site declares the proxy with OWCO_BEHIND_CLOUDFLARE, OWCO_BEHIND_CLOUDFRONT or OWCO_GEO_TRUSTED_HEADERS; without a trusted source and without a database, detection deliberately fails closed to the strictest opt-in profile. The IP address is used for the lookup and immediately discarded — never stored, never logged.
Geo routing does not make the HTML vary. The page carries both consent models — the opt-in defaults and the opt-out defaults — and the browser selects the one matching the visitor's jurisdiction before the first Consent Mode default call is emitted. The country itself is resolved on a request that reaches PHP and cached in owco_geo for 24 hours, so a visitor whose very first page is served by the cache is treated under the site's configured profile until that cookie exists.
Eleven: GDPR, UK PECR, Swiss nFADP, LGPD, POPIA, PIPL, DPDP, Quebec Law 25, CCPA, Australian Privacy Act and a generic US state profile. The profile drives the consent model (opt-in, or opt-out for CCPA, the generic US profile and Australia), the Consent Mode defaults, whether the "Do Not Sell or Share" control is shown, whether GPC is binding, which privacy notice and cookie policy are generated, which rights are listed and which supervisory authority is named.
All of them are first-party, none of them is used for tracking or analytics, and they are declared in the generated cookie policy.
owco_consent — the visitor's choices, their pseudonymous browser token and the profile in force. Lifetime follows the renewal setting (12 months by default, capped at 13). A renewal setting of 0 is treated as that 13-month maximum, not as "never expires", and the same figure governs the cookie, the server-side expiry check and the duration the generated documents publish.owco_geo — the detected country and the resulting profile, for 24 hours. Written only when geo routing is enabled and a country was actually detected. It contains no IP address.owco_gpc — a session marker recording that the Global Privacy Control signal has already been processed for this browser, so the signal is not re-recorded on every page view.owco_gpc_notice — five minutes, so the interface can tell the visitor that GPC replaced the choice they had saved.euconsent-v2 — the IAB TCF consent string, written only when the TCF module is enabled and only after the visitor has acted.The consent ledger, the scanner findings, the tracker catalogue, the OW Forms consent links, the options and the settings survive deactivation and deletion by default. Two things are removed whatever the "Delete all data on uninstall" setting says. The scheduled jobs are always unscheduled, so deleting the plugin cannot leave a cron event pointing at code that is gone. And the data-subject request register — the names, e-mail addresses and free-text messages people sent you, plus its rate-limiting transients — is always dropped. It is the only table holding directly identifying personal data about other people; once the plugin is gone nothing enforces the retention period that bounded it, and no screen is left that could read, answer, export or erase it. Export the requests you need to keep from the DSAR screen before you delete the plugin. For a clean removal of everything else, enable "Delete all data on uninstall" before deleting the plugin — it then drops the plugin's tables, options and transients, on every site of a multisite network.
wp_kses_post() on return.
2.0.6
Plugin URI and Author URI point straight at the plugin's page and OptionWeb's English home, without a redirect. The OptionWeb entries of the tracker catalogue linked to missing pages; they now link to OptionWeb's privacy policy, installed catalogues included.
2.0.5
The consent ledger, the consent cookie and every other keyed hash no longer use the WordPress authentication salts. The ledger key is OWCO_LEDGER_KEY when defined, otherwise a random key the plugin keeps; rows signed before the update are attested under it, so later edits are still detected. Visitors see the banner once more. The DSAR and CCPA shortcodes escape their output on return.
2.0.4
JSON exports keep wp_json_encode()'s default escaping.
2.0.3
Styles and scripts ship as files under assets/; only JSON configuration stays inline. The scripts that must run first are excluded from optimiser deferral.
2.0.2
Two phpcs:ignore annotations corrected.
2.0.1
/owco/v1/* route failed with a fatal error since 2.0.0. Treat consent writes from that window as unrecorded.owc to owco (the directory requires a prefix of at least four characters). Existing installations are migrated automatically. Custom code hooking owc_* filters or CSS targeting owc-* classes must be updated.
GET /owco/v1/nonce no longer mints a wp_rest nonce, which authorises writes across the whole REST API and could be harvested cross-origin. It now mints a token scoped to this plugin's own write routes and refuses a foreign-origin request.wp_kses(). The admin stylesheet has its own handle instead of core's wp-admin.GET /owc/v1/state route is removed. Admin notices are confined to the plugin's own screens.
1.4.3
Fixed the consent and geo cookies being percent-encoded twice, which made every browser discard the choice while server-side checks kept passing. Verified end to end on PHP 7.4 and 8.3.
1.4.2
Fixed the 1.4.1 cookie reader failing on a valid cookie — same double-encoding cause as 1.4.3, caught here for the read path. Update if you installed 1.4.1.
1.4.1
Fixed consent writes being lost on a site whose visitors browse a different host than the Site Address setting (www vs. apex, an alias, a proxy). The banner now re-anchors its REST call on the origin actually browsed.
1.4.0
Public write endpoints no longer depend on WordPress's core nonce, which a full-page cache serves stale or foreign. Refused writes are recorded and reported on a new Dashboard panel.
1.3.2
Fixed three misplaced phpcs:ignore annotations from 1.3.1. Translation catalogues regenerated.
1.3.1
Packaging-only: rewrote three queries for static-analysis compliance, no behavioural change.
1.3.0
WordPress 6.2 is now the minimum, for the %i identifier placeholder. A write this site cannot actually record is now refused with a 503 instead of reported as saved, and the banner no longer treats a cache/firewall interstitial as a successful write. The www/apex spellings of the site's own host are both accepted by the same-origin check. Thirteen Plugin Check errors closed.
1.2.0
The architectural rewrite: cache-independent front end, a hash-chained HMAC-signed consent ledger, genuine DSAR fulfilment via WordPress's personal-data exporter/eraser hooks, per-jurisdiction privacy notices and cookie policies (11 profiles), rate limiting on every public REST route, IAB TCF v2.2 and Google Consent Mode v2 support, MaxMind GeoLite2 geo-routing, and a French translation. 349 defects closed against the 1.1.0 baseline.
1.1.0
Initial public release.