| @@ -6,46 +6,60 @@ | ||
| 6 | 6 | use WP_Error; |
| 7 | 7 | use WP_REST_Controller; |
| 8 | 8 | use WP_REST_Request; |
| 9 | 9 | |
| 10 | -class EntryController extends WP_REST_Controller { | |
| 10 | +class EntryController extends WP_REST_Controller | |
| 11 | +{ | |
| 11 | 12 | protected static $form; |
| 12 | 13 | protected $formModel; |
| 13 | 14 | protected $form_id; |
| 14 | 15 | |
| 15 | - public function __construct() { | |
| 16 | + public function __construct() | |
| 17 | + { | |
| 16 | 18 | $this->formModel = new FormModel(); |
| 17 | 19 | } |
| 18 | 20 | |
| 19 | - public function googleAuth() { | |
| 20 | - $state = $_GET['state']; | |
| 21 | - $code = urlencode($_GET['code']); | |
| 22 | - // echo $code; | |
| 23 | - if (wp_redirect($state . '&code=' . $code, 302)) { | |
| 24 | - exit; | |
| 21 | + // public function oneDriveAuth() | |
| 22 | + // { | |
| 23 | + // $state = $_GET['state']; | |
| 24 | + // $code = urlencode($_GET['code']); | |
| 25 | + // // echo $code; | |
| 26 | + // if (wp_redirect($state . '&code=' . $code, 302)) { | |
| 27 | + // exit; | |
| 28 | + // } | |
| 29 | + // } | |
| 30 | + | |
| 31 | + public function authRedirect(WP_REST_Request $request) | |
| 32 | + { | |
| 33 | + $state = $request->get_param('state'); | |
| 34 | + $site_url = $this->getDomain(get_site_url()); | |
| 35 | + $state_domain = $this->getDomain($state); | |
| 36 | + | |
| 37 | + // the refused domain is caller-supplied; echoing it back reflects attacker | |
| 38 | + // input into the response of a public endpoint | |
| 39 | + if ('' === $state_domain || $site_url !== $state_domain) { | |
| 40 | + return new WP_Error('404', 'Invalid redirect URL'); | |
| 25 | 41 | } |
| 26 | - } | |
| 27 | 42 | |
| 28 | - public function oneDriveAuth() { | |
| 29 | - $state = $_GET['state']; | |
| 30 | - $code = urlencode($_GET['code']); | |
| 31 | - // echo $code; | |
| 32 | - if (wp_redirect($state . '&code=' . $code, 302)) { | |
| 43 | + $params = $request->get_params(); | |
| 44 | + unset($params['rest_route'], $params['state']); | |
| 45 | + | |
| 46 | + $redirect_url = $state . '&' . http_build_query($params); | |
| 47 | + | |
| 48 | + if (wp_safe_redirect($redirect_url, 302)) { | |
| 33 | 49 | exit; |
| 34 | 50 | } |
| 35 | 51 | } |
| 36 | 52 | |
| 37 | - public function authRedirect(WP_REST_Request $request) { | |
| 38 | - $state = $request->get_param('state'); | |
| 39 | - $parsed_url = parse_url(get_site_url()); | |
| 40 | - $site_url = $parsed_url['scheme'] . '://' . $parsed_url['host']; | |
| 41 | - $site_url .= empty($parsed_url['port']) ? null : ':' . $parsed_url['port']; | |
| 42 | - if (false === strpos($state, $site_url)) { | |
| 43 | - return new WP_Error('404'); | |
| 53 | + private function getDomain($url) | |
| 54 | + { | |
| 55 | + // these endpoints are public: a missing or non-URL state must not raise | |
| 56 | + // notices, it must simply fail the same-origin comparison | |
| 57 | + $parsed_url = is_string($url) && '' !== $url ? wp_parse_url($url) : false; | |
| 58 | + if (!is_array($parsed_url) || empty($parsed_url['scheme']) || empty($parsed_url['host'])) { | |
| 59 | + return ''; | |
| 44 | 60 | } |
| 45 | - $params = $request->get_params(); | |
| 46 | - unset($params['rest_route'], $params['state']); | |
| 47 | - if (wp_redirect($state . '&' . http_build_query($params), 302)) { | |
| 48 | - exit; | |
| 49 | - } | |
| 61 | + $domain = $parsed_url['scheme'] . '://' . $parsed_url['host']; | |
| 62 | + $domain .= empty($parsed_url['port']) ? null : ':' . $parsed_url['port']; | |
| 63 | + return $domain; | |
| 50 | 64 | } |
| 51 | 65 | } |