UeditorAiCsrf.php 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. <?php
  2. namespace app\common\util;
  3. /**
  4. * Session CSRF for UEditor AI proxy (same idea as addons\aicontent\Aicontent::generateCsrfToken).
  5. */
  6. class UeditorAiCsrf
  7. {
  8. private const SESSION_KEY = 'ueditor_ai_csrf_token';
  9. public static function token(): string
  10. {
  11. $t = session(self::SESSION_KEY);
  12. if ($t === null || $t === '') {
  13. $t = bin2hex(random_bytes(16));
  14. session(self::SESSION_KEY, $t);
  15. }
  16. $t = (string) $t;
  17. /* 对话框与内容页不同 window 时 Cookie + 服务端比对;HTTPS 下需 Secure 否则部分浏览器不发送 */
  18. if (!headers_sent()) {
  19. $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
  20. || (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443);
  21. if (\PHP_VERSION_ID >= 70300) {
  22. setcookie('ueditor_ai_csrf', $t, [
  23. 'expires' => time() + 7200,
  24. 'path' => '/',
  25. 'secure' => $secure,
  26. 'httponly' => false,
  27. 'samesite' => 'Lax',
  28. ]);
  29. } else {
  30. setcookie('ueditor_ai_csrf', $t, time() + 7200, '/', '', $secure, false);
  31. }
  32. }
  33. return $t;
  34. }
  35. /**
  36. * 同时接受 JSON 内 _csrf_token 与 Cookie ueditor_ai_csrf(任一与 session 一致即通过),避免 body 陈旧而 Cookie 已刷新。
  37. */
  38. public static function validate($submitted): bool
  39. {
  40. $expected = session(self::SESSION_KEY);
  41. if ($expected === null || $expected === '') {
  42. return false;
  43. }
  44. $expected = (string) $expected;
  45. $fromBody = is_string($submitted) ? $submitted : '';
  46. $fromCookie = (!empty($_COOKIE['ueditor_ai_csrf']) && is_string($_COOKIE['ueditor_ai_csrf']))
  47. ? (string) $_COOKIE['ueditor_ai_csrf']
  48. : '';
  49. foreach ([$fromBody, $fromCookie] as $cand) {
  50. if ($cand === '') {
  51. continue;
  52. }
  53. if (strlen($expected) === strlen($cand) && hash_equals($expected, $cand)) {
  54. return true;
  55. }
  56. }
  57. return false;
  58. }
  59. }