Reliance on Cookies without validation in OctoberCMS
Description
In OctoberCMS before version 1.0.468, encrypted cookie values were not tied to the name of the cookie the value belonged to. This meant that certain classes of attacks that took advantage of other theoretical vulnerabilities in user facing code (nothing exploitable in the core project itself) had a higher chance of succeeding. Specifically, if your usage exposed a way for users to provide unfiltered user input and have it returned to them as an encrypted cookie (ex. storing a user provided search query in a cookie) they could then use the generated cookie in place of other more tightly controlled cookies; or if your usage exposed the plaintext version of an encrypted cookie at any point to the user they could theoretically provide encrypted content from your application back to it as an encrypted cookie and force the framework to decrypt it for them. Issue has been fixed in build 468 (v1.0.468).
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
october/rainPackagist | >= 1.0.319, < 1.0.468 | 1.0.468 |
Affected products
1- Range: < 1.0.468
Patches
128310d4fb336Improvements to cookie value validation (#508)
2 files changed · +120 −0
src/Cookie/CookieValuePrefix.php+47 −0 added@@ -0,0 +1,47 @@ +<?php namespace October\Rain\Cookie; + +/** + * Helper class to prefix, unprefix, and verify cookie values + */ +class CookieValuePrefix +{ + /** + * Create a new cookie value prefix for the given cookie name. + * + * @param string $name The name of the cookie + * @param string $key The encryption key + * @return string + */ + public static function create($name, $key) + { + return hash_hmac('sha1', $name . 'v2', $key) . '|'; + } + + /** + * Remove the cookie value prefix. + * + * @param string $cookieValue + * @return string + */ + public static function remove($cookieValue) + { + return substr($cookieValue, 41); + } + + /** + * Verify the provided cookie's value + * + * @param string $name The name of the cookie + * @param string $value The decrypted value of the cookie to be verified + * @param string $key The encryption key used to encrypt the cookie originally + * @return string|null $verifiedValue The unprefixed value if it passed verification, otherwise null + */ + public static function getVerifiedValue($name, $value, $key) + { + $verifiedValue = null; + if (starts_with($value, static::create($name, $key))) { + $verifiedValue = static::remove($value); + } + return $verifiedValue; + } +}
src/Cookie/Middleware/EncryptCookies.php+73 −0 modified@@ -1,6 +1,11 @@ <?php namespace October\Rain\Cookie\Middleware; use Config; +use Session; +use October\Rain\Cookie\CookieValuePrefix; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; use Illuminate\Cookie\Middleware\EncryptCookies as EncryptCookiesBase; @@ -57,4 +62,72 @@ protected function decryptArray(array $cookie) return $decrypted; } + + /** + * Decrypt the cookies on the request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Request + */ + protected function decrypt(Request $request) + { + foreach ($request->cookies as $key => $cookie) { + if ($this->isDisabled($key)) { + continue; + } + + try { + // Decrypt the request-provided cookie + $decryptedValue = $this->decryptCookie($key, $cookie); + + // Verify that the decrypted value belongs to this cookie key, use null if it fails + $value = CookieValuePrefix::getVerifiedValue($key, $decryptedValue, $this->encrypter->getKey()); + + /** + * If the cookie is for the session and the value is a valid Session ID, + * then allow it to pass through even if the validation failed (most likely + * because the upgrade just occurred) + * + * The cookie will be adjusted on the next request + * @todo Remove if year >= 2021 or build >= 475 + */ + if (empty($value) && $key === Config::get('session.cookie') && Session::isValidId($decryptedValue)) { + $value = $decryptedValue; + } + + // Set the verified cookie value on the request + $request->cookies->set($key, $value); + } catch (DecryptException $e) { + $request->cookies->set($key, null); + } + } + + return $request; + } + + /** + * Encrypt the cookies on an outgoing response. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function encrypt(Response $response) + { + foreach ($response->headers->getCookies() as $cookie) { + if ($this->isDisabled($cookie->getName())) { + continue; + } + + $response->headers->setCookie($this->duplicate( + $cookie, + $this->encrypter->encrypt( + // Prefix the cookie value to verify that it belongs to the current cookie + CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey()) . $cookie->getValue(), + static::serialized($cookie->getName()) + ) + )); + } + + return $response; + } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-55mm-5399-7r63ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-15128ghsaADVISORY
- github.com/octobercms/library/commit/28310d4fb336a1741b39498f4474497644a6875cghsax_refsource_MISCWEB
- github.com/octobercms/library/pull/508ghsax_refsource_MISCWEB
- github.com/octobercms/october/security/advisories/GHSA-55mm-5399-7r63ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.