3.8 KiB
3.8 KiB
XShop – Security
1. Principles
- Sanitize in, validate, escape out.
- Nonce + capability for every state-changing request.
- Use WP/WC APIs; no hand-rolled auth/crypto.
2. Input
sanitize_text_field,sanitize_textarea_field,sanitize_email,wc_clean,absint,esc_url_rawper type.- Validate: enums via allow-list, URLs via
wp_http_validate_urlwhere remote, dates viaDateTimeImmutable. - No
$_GET/$_POSTwithout sanitization. Noextract().
3. Output
esc_html,esc_attr,esc_url,esc_html__,wp_kses_post(only for intentionally HTML fields with allow-list),wp_ksesfor narrow HTML.- No
echo $raw.
4. CSRF
- Every POST/AJAX/REST mutation:
check_ajax_referer/wp_verify_nonce/X-WP-Nonceheader for REST. - REST:
permission_callbackchecks nonce and capability.
5. AuthZ
- Admin screens:
current_user_can('manage_options')ormanage_woocommercewhere appropriate (documented per screen). - Q&A:
edit_postsfor moderation; user can delete own question viadelete_postcap check. - Banners:
manage_options.
6. XSS
- No inline
onclickwith untrusted data. NoinnerHTMLwith unsanitized server data – DOM viatextContentor sanitized fragment. - Stored XSS: banner CTA/link, Q&A body – sanitized on save, escaped on render.
7. SQLi
- No raw SQL unless via
$wpdb->prepare. PreferWP_Query/WCCRUD. No string-concatenated queries.
8. Special Cases
- File uploads: use
wp_handle_upload+wp_check_filetype, no arbitrary file execution, no path traversal (sanitize_file_name,realpathcheck). - Redirects:
wp_safe_redirect+wp_validate_redirectallow-list. - Unserialize: never
unserializeuser data; use JSON. - Secrets: no inline secrets; no committed
.env. - Remote import: validate MIME, size cap, timeout, no SSRF (allow-list demo asset host if any).
9. Headers (theme-level)
- Theme does not override server headers; document recommended headers for host (CSP, etc.) in
docs/security.md.
10. Checklist per Feature
- Inputs sanitized + validated
- Nonce verified
- Capability checked
- Outputs escaped
- No raw SQL
- No unsafe redirect/unserialize/upload
11. Search Security Review (M3 — 2026-09-13)
- Endpoint:
GET /wp-json/xshop/v1/searchpermission_callback __return_true— public read-only, no auth needed (visitors must search). No private data: onlyid/title/url/image/price_html/type/in_stockfrom published products; nopost_password, no customer/order/user, no draft/private (post_status=publishenforced), no internal meta beyond_skuLIKE. Verified via direct REST: draft products never returned. - Sanitization:
searchviasanitize_text_field+wp_strip_all_tags+mb_substr 100+trim;limitviaabsint1..20; bounded length prevents DOS via huge query. - Validation:
searchmax 100 chars,limitvalidate 1..20 else 400 (WP REST validation). Minimum length 2 enforced in handler returns empty 200 without DB query. - Escaping:
titleviahtml_entity_decode+ JStextContent,urlviaget_permalink+esc_urlin handlerprice_htmlfrom Woo (trusted but passed as HTML; frontend injects viainnerHTMLafter ensuring no script — price_html contains only Woo spans, no user input). - Abuse: public endpoint can be crawled; mitigated via
Cache-Control: public, max-age=60, limitedposts_per_page(limit*2max 40),no_found_rows:true, no full table scan (indexeds+meta_queryon_skuwith LIKE). No N+1 (IDs then singlewc_get_productper result, max 6). Recommend rate limiting at reverse proxy for high traffic. - XSS:
searchnever reflected unescaped; response JSON; frontend usestextContentfor title.
12. Review Process
- PHPCS
WordPress.Security+ manual review before each release. - Security note in CHANGELOG.md.