PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.5.1
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.5.1
3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.10 All 111 releases
templately / includes / Core / Importer / Utils / SignatureVerifier.php

SignatureVerifier.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.5.1, at includes/Core/Importer/Utils/SignatureVerifier.php

135 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Templately\Core\Importer\Utils;
3
4 use Templately\Utils\Helper;
5
6 /**
7 * Class SignatureVerifier
8 *
9 * Verifies HMAC-SHA256 signatures from Templately backend callbacks
10 * to prevent arbitrary file write vulnerabilities.
11 *
12 * @package Templately\Core\Importer\Utils
13 */
14 class SignatureVerifier {
15
16 /**
17 * Verify callback signature from Templately backend.
18 *
19 * @param array $payload Request payload data
20 * @param string $signature Signature from X-Templately-Signature header
21 * @param int $timestamp Timestamp from X-Templately-Timestamp header
22 * @param string $api_key User's API key (used as secret)
23 * @param int $tolerance Time tolerance in seconds (default: 300 = 5 minutes)
24 * @return bool|WP_Error True if valid, WP_Error otherwise
25 */
26 public static function verify($payload, $signature, $timestamp, $api_key, $tolerance = 300) {
27 if (empty($api_key)) {
28 return Helper::error(
29 'missing_api_key',
30 __('API key not provided for signature verification', 'templately'),
31 'verify_signature',
32 401
33 );
34 }
35
36 // Validate inputs
37 if (empty($signature) || empty($timestamp) || !is_numeric($timestamp)) {
38 return Helper::error(
39 'invalid_signature_headers',
40 __('Invalid signature or timestamp headers', 'templately'),
41 'verify_signature',
42 401
43 );
44 }
45
46 // Check timestamp to prevent replay attacks
47 if (!self::is_timestamp_valid((int) $timestamp, $tolerance)) {
48 return Helper::error(
49 'timestamp_expired',
50 __('Callback timestamp expired or invalid', 'templately'),
51 'verify_signature',
52 401
53 );
54 }
55
56 // Generate expected signature using API key
57 $expected_signature = self::generate_signature($payload, (int) $timestamp, $api_key);
58
59 // Use hash_equals to prevent timing attacks
60 if (!hash_equals($expected_signature, $signature)) {
61 return Helper::error(
62 'invalid_signature',
63 __('Invalid signature', 'templately'),
64 'verify_signature',
65 401
66 );
67 }
68
69 return true;
70 }
71
72 /**
73 * Check if timestamp is within acceptable tolerance.
74 *
75 * @param int $timestamp Unix timestamp to check
76 * @param int $tolerance Tolerance in seconds
77 * @return bool True if timestamp is valid
78 */
79 private static function is_timestamp_valid($timestamp, $tolerance) {
80 $current_time = time();
81 $time_difference = abs($current_time - $timestamp);
82
83 return $time_difference <= $tolerance;
84 }
85
86 /**
87 * Generate HMAC-SHA256 signature for payload.
88 *
89 * @param array $payload Request payload
90 * @param int $timestamp Unix timestamp
91 * @param string $api_key User's API key (used as secret)
92 * @return string HMAC signature
93 */
94 private static function generate_signature($payload, $timestamp, $api_key) {
95 $canonical_string = self::create_canonical_string($payload, $timestamp);
96 return hash_hmac('sha256', $canonical_string, $api_key);
97 }
98
99 /**
100 * Create canonical string from payload and timestamp.
101 *
102 * Only includes security-critical fields in signature to avoid
103 * performance issues with large template content.
104 *
105 * @param array $payload Request payload
106 * @param int $timestamp Unix timestamp
107 * @return string Canonical string
108 */
109 private static function create_canonical_string($payload, $timestamp) {
110 // Extract only security-critical fields for signature
111 // Exclude large content fields like 'template' and 'error'
112 // Also exclude 'isSkipped' as requested
113 $signature_fields = [
114 'process_id' => isset($payload['process_id']) ? $payload['process_id'] : null,
115 'content_id' => isset($payload['content_id']) ? $payload['content_id'] : null,
116 'template_id' => isset($payload['template_id']) ? $payload['template_id'] : null,
117 'type' => isset($payload['type']) ? $payload['type'] : null,
118 ];
119
120 // Remove null values
121 $signature_fields = array_filter($signature_fields, function ($value) {
122 return $value !== null;
123 });
124
125 // Sort keys for consistency
126 ksort($signature_fields);
127
128 // JSON encode with consistent flags
129 $payload_json = wp_json_encode($signature_fields, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
130
131 // Combine timestamp and payload
132 return $timestamp . '.' . $payload_json;
133 }
134 }
135