Нема описа

Security.php 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. <?php
  2. namespace MailPoet\Util;
  3. if (!defined('ABSPATH')) exit;
  4. use Exception;
  5. use MailPoet\Entities\NewsletterEntity;
  6. use MailPoet\Entities\SubscriberEntity;
  7. use MailPoet\Newsletter\NewslettersRepository;
  8. use MailPoet\Subscribers\SubscribersRepository;
  9. use MailPoet\WP\Functions as WPFunctions;
  10. class Security {
  11. const HASH_LENGTH = 12;
  12. const UNSUBSCRIBE_TOKEN_LENGTH = 15;
  13. /** @var NewslettersRepository */
  14. private $newslettersRepository;
  15. /** @var SubscribersRepository */
  16. private $subscribersRepository;
  17. public function __construct(
  18. NewslettersRepository $newslettersRepository,
  19. SubscribersRepository $subscribersRepository
  20. ) {
  21. $this->newslettersRepository = $newslettersRepository;
  22. $this->subscribersRepository = $subscribersRepository;
  23. }
  24. public static function generateToken($action = 'mailpoet_token') {
  25. return WPFunctions::get()->wpCreateNonce($action);
  26. }
  27. /**
  28. * Generate random lowercase alphanumeric string.
  29. * 1 lowercase alphanumeric character = 6 bits (because log2(36) = 5.17)
  30. * So 3 bytes = 4 characters
  31. * @param int $length Minimal lenght is 5
  32. * @return string
  33. */
  34. public static function generateRandomString($length = 5): string {
  35. $length = max(5, (int)$length);
  36. $string = base_convert(
  37. bin2hex(
  38. random_bytes( // phpcs:ignore
  39. (int)ceil(3 * $length / 4)
  40. )
  41. ),
  42. 16,
  43. 36
  44. );
  45. $result = substr($string, 0, $length);
  46. if (strlen($result) === $length) return $result;
  47. // in very rare occasions we generate a shorter string when random_bytes generates something starting with 0 let's try again
  48. return self::generateRandomString($length);
  49. }
  50. /**
  51. * @param int $length Maximal length is 32
  52. * @return string
  53. */
  54. public static function generateHash($length = null) {
  55. $length = ($length) ? $length : self::HASH_LENGTH;
  56. $authKey = self::generateRandomString(64);
  57. if (defined('AUTH_KEY')) {
  58. $authKey = AUTH_KEY;
  59. }
  60. return substr(
  61. hash_hmac('sha512', self::generateRandomString(64), $authKey),
  62. 0,
  63. $length
  64. );
  65. }
  66. static public function generateUnsubscribeToken($model) {
  67. do {
  68. $token = self::generateRandomString(self::UNSUBSCRIBE_TOKEN_LENGTH);
  69. $found = $model::whereEqual('unsubscribe_token', $token)->count();
  70. } while ($found > 0);
  71. return $token;
  72. }
  73. public function generateUnsubscribeTokenByEntity($entity): string {
  74. $repository = null;
  75. if ($entity instanceof NewsletterEntity) {
  76. $repository = $this->newslettersRepository;
  77. } elseif ($entity instanceof SubscriberEntity) {
  78. $repository = $this->subscribersRepository;
  79. } else {
  80. throw new Exception('Unsupported Entity type');
  81. }
  82. do {
  83. $token = self::generateRandomString(self::UNSUBSCRIBE_TOKEN_LENGTH);
  84. $found = count($repository->findBy(['unsubscribeToken' => $token]));
  85. } while ($found > 0);
  86. return $token;
  87. }
  88. }