Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 54 additions & 34 deletions src/Controller/CaptchaHandlerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,27 @@ public function initialize()
{
if ($this->isGetResourceContentsRequest()) {
// validate filename
$filename = $this->request->query('get');
$filename = $this->request->getQuery('get');
if (!preg_match('/^[a-z-]+\.(css|gif|js)$/', $filename)) {
$this->badRequest('Invalid file name.');
}
} else {
// validate captcha id and load CaptchaComponent
$captchaId = $this->request->query('c');
$captchaId = $this->request->getQuery('c');
if (is_null($captchaId) || !preg_match('/^(\w+)$/ui', $captchaId)) {
$this->badRequest('Invalid captcha id.');
}

$captchaInstanceId = $this->request->query('t');
$captchaInstanceId = $this->request->getQuery('t');
if (is_null($captchaInstanceId) || !(32 == strlen($captchaInstanceId) &&
(1 === preg_match("/^([a-f0-9]+)$/u", $captchaInstanceId)))) {
(1 === preg_match("/^([a-f0-9]+)$/u", $captchaInstanceId)))) {
$this->badRequest('Invalid instance id.');
}

$this->loadComponent('CakeCaptcha.Captcha', [
'captchaConfig' => $captchaId,
'captchaInstanceId' => $captchaInstanceId
]);

}
}

Expand Down Expand Up @@ -70,7 +69,7 @@ public function index()
\BDC_HttpHelper::BadRequest('captcha');
}

$commandString = $this->request->query('get');
$commandString = $this->request->getQuery('get');
if (!\BDC_StringHelper::HasValue($commandString)) {
\BDC_HttpHelper::BadRequest('command');
}
Expand Down Expand Up @@ -100,7 +99,8 @@ public function index()

// disallow audio file search engine indexing
header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet');
echo $responseBody; exit;
echo $responseBody;
exit;
}
}

Expand All @@ -111,7 +111,7 @@ public function index()
*/
public function getResourceContents()
{
$filename = $this->request->query('get');
$filename = $this->request->getQuery('get');

$resourcePath = realpath(Path::getPublicDirPathInLibrary() . $filename);

Expand All @@ -128,7 +128,8 @@ public function getResourceContents()

header("Content-Type: {$mimeType}");
header("Content-Length: {$fileLength}");
echo (file_get_contents($resourcePath)); exit;
echo (file_get_contents($resourcePath));
exit;
}

/**
Expand Down Expand Up @@ -247,7 +248,8 @@ public function getSound()
}


public function getSoundData($p_Captcha, $p_InstanceId) {
public function getSoundData($p_Captcha, $p_InstanceId)
{
$shouldCache = (
($p_Captcha->SoundRegenerationMode == \SoundRegenerationMode::None) || // no sound regeneration allowed, so we must cache the first and only generated sound
$this->detectIosRangeRequest() // keep the same Captcha sound across all chunked iOS requests
Expand All @@ -269,42 +271,51 @@ public function getSoundData($p_Captcha, $p_InstanceId) {
return $soundBytes;
}

private function generateSoundData($p_Captcha, $p_InstanceId) {
private function generateSoundData($p_Captcha, $p_InstanceId)
{
$rawSound = $p_Captcha->CaptchaBase->GetSound($p_InstanceId);
$p_Captcha->CaptchaBase->SaveCodeCollection(); // always record sound generation count
return $rawSound;
}

private function saveSoundData($p_InstanceId, $p_SoundBytes) {
private function saveSoundData($p_InstanceId, $p_SoundBytes)
{
CAKE_Session_Save("BDC_Cached_SoundData_" . $p_InstanceId, $p_SoundBytes);
}

private function loadSoundData($p_InstanceId) {
private function loadSoundData($p_InstanceId)
{
return CAKE_Session_Load("BDC_Cached_SoundData_" . $p_InstanceId);
}

private function clearSoundData($p_InstanceId) {
private function clearSoundData($p_InstanceId)
{
CAKE_Session_Clear("BDC_Cached_SoundData_" . $p_InstanceId);
}


// Instead of relying on unreliable user agent checks, we detect the iOS sound
// requests by the Http headers they will always contain
private function detectIosRangeRequest() {
private function detectIosRangeRequest()
{

if(array_key_exists('HTTP_RANGE', $_SERVER) &&
\BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])) {
if (
array_key_exists('HTTP_RANGE', $_SERVER) &&
\BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])
) {

// Safari on MacOS and all browsers on <= iOS 10.x
if(array_key_exists('HTTP_X_PLAYBACK_SESSION_ID', $_SERVER) &&
\BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) {
if (
array_key_exists('HTTP_X_PLAYBACK_SESSION_ID', $_SERVER) &&
\BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])
) {
return true;
}

$userAgent = array_key_exists('HTTP_USER_AGENT', $_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : null;

// all browsers on iOS 11.x and later
if(\BDC_StringHelper::HasValue($userAgent)) {
if (\BDC_StringHelper::HasValue($userAgent)) {
$userAgentLC = \BDC_StringHelper::Lowercase($userAgent);
if (\BDC_StringHelper::Contains($userAgentLC, "like mac os") || \BDC_StringHelper::Contains($userAgentLC, "like macos")) {
return true;
Expand All @@ -314,7 +325,8 @@ private function detectIosRangeRequest() {
return false;
}

private function getSoundByteRange() {
private function getSoundByteRange()
{
// chunked requests must include the desired byte range
$rangeStr = $_SERVER['HTTP_RANGE'];
if (!\BDC_StringHelper::HasValue($rangeStr)) {
Expand All @@ -329,12 +341,15 @@ private function getSoundByteRange() {
);
}

private function detectFakeRangeRequest() {
private function detectFakeRangeRequest()
{
$detected = false;
if (array_key_exists('HTTP_RANGE', $_SERVER)) {
$rangeStr = $_SERVER['HTTP_RANGE'];
if (\BDC_StringHelper::HasValue($rangeStr) &&
preg_match('/bytes=0-$/', $rangeStr)) {
if (
\BDC_StringHelper::HasValue($rangeStr) &&
preg_match('/bytes=0-$/', $rangeStr)
) {
$detected = true;
}
}
Expand Down Expand Up @@ -375,7 +390,8 @@ public function getValidationResult()
return $resultJson;
}

public function getScriptInclude() {
public function getScriptInclude()
{
// saved data for the specified Captcha object in the application
if (is_null($this->Captcha)) {
\BDC_HttpHelper::BadRequest('captcha');
Expand Down Expand Up @@ -417,8 +433,9 @@ public function getScriptInclude() {
*/
private function getInstanceId()
{
$instanceId = $this->request->query('t');
if (!\BDC_StringHelper::HasValue($instanceId) ||
$instanceId = $this->request->getQuery('t');
if (
!\BDC_StringHelper::HasValue($instanceId) ||
!\BDC_CaptchaBase::IsValidInstanceId($instanceId)
) {
return;
Expand All @@ -434,13 +451,13 @@ private function getInstanceId()
private function getUserInput()
{
// BotDetect built-in Ajax Captcha validation
$input = $this->request->query('i');
$input = $this->request->getQuery('i');

if (is_null($input)) {
// jQuery validation support, the input key may be just about anything,
// so we have to loop through fields and take the first unrecognized one
$recognized = array('get', 'c', 't', 'd');
foreach ($this->request->query as $key => $value) {
foreach ($this->request->getQuery() as $key => $value) {
if (!in_array($key, $recognized)) {
$input = $value;
break;
Expand All @@ -458,7 +475,7 @@ private function getUserInput()
*/
private function getJsonValidationResult($result)
{
$resultStr = ($result ? 'true': 'false');
$resultStr = ($result ? 'true' : 'false');
return $resultStr;
}

Expand All @@ -467,7 +484,7 @@ private function getJsonValidationResult($result)
*/
private function isGetResourceContentsRequest()
{
$http_get_data = $this->request->query;
$http_get_data = $this->request->getQuery();
return array_key_exists('get', $http_get_data) && !array_key_exists('c', $http_get_data);
}

Expand All @@ -479,14 +496,17 @@ private function isGetResourceContentsRequest()
*/
private function badRequest($message)
{
while (ob_get_contents()) { ob_end_clean(); }
while (ob_get_contents()) {
ob_end_clean();
}
header('HTTP/1.1 400 Bad Request');
header('Content-Type: text/plain');
echo $message;
exit;
}

public function getP() {
public function getP()
{
if (is_null($this->Captcha)) {
\BDC_HttpHelper::BadRequest('captcha');
}
Expand All @@ -503,7 +523,7 @@ public function getP() {

// response data
$response = "{\"sp\":\"{$p->GetSP()}\",\"hs\":\"{$p->GetHs()}\"}";


// response MIME type & headers
header('Content-Type: application/json');
Expand Down
42 changes: 19 additions & 23 deletions src/Controller/Component/CaptchaComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CaptchaComponent extends Component
* @var object
*/
private $captcha;

/**
* BotDetect CakePHP CAPTCHA plugin information.
*
Expand All @@ -34,9 +34,9 @@ class CaptchaComponent extends Component
*/
public function initialize(array $params)
{
self::$instance =& $this;
self::$instance = &$this;

$session = $this->request->session();
$session = $this->request->getSession();

// load botdetect captcha library
LibraryLoader::load($session);
Expand All @@ -45,10 +45,7 @@ public function initialize(array $params)
// this will avoid user being able to pass in a lowercase option (e.g. captchaconfig)
$params = array_change_key_case($params, CASE_LOWER);

if (empty($params) ||
!array_key_exists('captchaconfig', $params) ||
empty($params['captchaconfig'])
) {
if (empty($params) || !array_key_exists('captchaconfig', $params) || empty($params['captchaconfig'])) {
$errorMessage = 'The BotDetect Captcha component requires you to declare "captchaConfig" option and assigns a captcha configuration key defined in config/captcha.php file.<br>';
$errorMessage .= 'For example: $this->loadComponent(\'CakeCaptcha.Captcha\', [\'captchaConfig\' => \'ContactCaptcha\']);';
throw new InvalidArgumentException($errorMessage);
Expand All @@ -57,9 +54,8 @@ public function initialize(array $params)
$captchaId = $params['captchaconfig'];

$captchaInstanceId = null;
if(isset($params['captchainstanceid'])) {
if (isset($params['captchainstanceid'])) {
$captchaInstanceId = $params['captchainstanceid'];

}

// get captcha config
Expand All @@ -75,7 +71,7 @@ public function initialize(array $params)

// save user's captcha configuration options
UserCaptchaConfiguration::save($config);

// init botdetect captcha instance
$this->initCaptcha($config, $captchaInstanceId);
}
Expand Down Expand Up @@ -128,56 +124,56 @@ public function __call($method, $args = array())
*/
public function __get($name)
{
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'get_'.$name))) {
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'get_' . $name))) {
return $this->captcha->get_CaptchaBase()->$method();
}

if (method_exists($this->captcha, ($method = 'get_'.$name))) {
if (method_exists($this->captcha, ($method = 'get_' . $name))) {
return $this->captcha->$method();
}

if (method_exists($this, ($method = 'get_'.$name))) {
if (method_exists($this, ($method = 'get_' . $name))) {
return $this->$method();
}
}

public function __isset($name)
{
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'isset_'.$name))) {
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'isset_' . $name))) {
return $this->captcha->get_CaptchaBase()->$method();
}

if (method_exists($this->captcha, ($method = 'isset_'.$name))) {
if (method_exists($this->captcha, ($method = 'isset_' . $name))) {
return $this->captcha->$method();
}

if (method_exists($this, ($method = 'isset_'.$name))) {
if (method_exists($this, ($method = 'isset_' . $name))) {
return $this->$method();
}
}

public function __set($name, $value)
{
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'set_'.$name))) {
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'set_' . $name))) {
return $this->captcha->get_CaptchaBase()->$method($value);
}

if (method_exists($this->captcha, ($method = 'set_'.$name))) {
if (method_exists($this->captcha, ($method = 'set_' . $name))) {
$this->captcha->$method($value);
} else if (method_exists($this, ($method = 'set_'.$name))) {
} else if (method_exists($this, ($method = 'set_' . $name))) {
$this->$method($value);
}
}

public function __unset($name)
{
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'unset_'.$name))) {
if (method_exists($this->captcha->get_CaptchaBase(), ($method = 'unset_' . $name))) {
return $this->captcha->get_CaptchaBase()->$method();
}

if (method_exists($this->captcha, ($method = 'unset_'.$name))) {
if (method_exists($this->captcha, ($method = 'unset_' . $name))) {
$this->captcha->$method();
} else if (method_exists($this, ($method = 'unset_'.$name))) {
} else if (method_exists($this, ($method = 'unset_' . $name))) {
$this->$method();
}
}
Expand All @@ -195,6 +191,6 @@ public static function getProductInfo()

// static field initialization
CaptchaComponent::$productInfo = [
'name' => 'BotDetect 4 PHP Captcha generator integration for the CakePHP framework',
'name' => 'BotDetect 4 PHP Captcha generator integration for the CakePHP framework',
'version' => '4.2.9'
];
Loading