In a previous post, I wrote about [intlink id=”2353″ type=”post”]using sessions in Yii-based sites[/intlink]. In this one, I’ll look at using cookies. Neither is that difficult, but as with all things regarding frameworks, the solution may not be obvious at first. And there are some ways to make use cookies in Yii in a more secure manner.To create a cookie in PHP without using a framework, you just call the setcookie() function. To create a cookie while using the Yii framework, you don’t use setcookie(), but rather create a new element in the Yii::app()->request->cookies array. (Note that sessions are stored in Yii::app()->session, but cookies are in Yii::app()->request->cookies, because cookies are part of the HTTP request a browser makes of a Web server).
What you’ll want to do to create a cookie is create a new object of type CHttpCookie: Yii’s class for cookies. Here, then, is the syntax for setting a cookie in Yii:
Yii::app()->request->cookies['name'] = new CHttpCookie('name', 'value');
You must use the same name value in both places, replacing it with the actual cookie name. Remember that the cookie’s name, and value, are visible to users in their browsers, so one ought to be prudent about what name you use and be extra mindful of what values are being stored.
Tip: Because the cookie’s name must be used twice in the code, you may want to consider assigning the cookie’s name to a variable that is used in both instances instead.
Once you’ve created a cookie, you can access it (on subsequent pages, because cookies are never immediately available to the page that set them), using Yii::app()->request->cookies[‘name’]->value. You have to use the extra ->value part, because the “cookie” being created is actually an object of type CHttpCookie (and Yii, internally, takes care of actually sending the cookie to the browser and reading the received cookie from the browser).
To test if a cookie exists, just use isset() on Yii::app()->request->cookies[‘name’], as you would any other variable.
To delete an existing cookie, just unset the element as you would any array element:
unset(Yii::app()->request->cookies['name']);
To delete all existing cookies (for that site), use
Yii::app()->request->cookies->clear();
By default, cookies will be set to expire when the browser window is closed. To change that, you need to modify the properties of the cookie. You can’t do so when you create the CHttpCookie object (i.e., the only arguments to the constructor are the cookie’s name and value), so you must separately create a new object of type CHttpCookie, to be assigned to Yii::app()->request->cookies later:
$cookie = new CHttpCookie('name', 'value');
Then adjust the expire attribute:
$cookie->expire = time() + (60*60*24); // 24 hours
Then add the cookie to the application:
Yii::app()->request->cookies['name'] = $cookie;
You can manipulate other cookie properties using the above syntax: domain, httpOnly, path, and secure. Each of these correspond to the arguments to the setcookie() function. (You can also manipulate the value of the cookie through $cookie->value and the cookie’s name through $cookie->name). For example, if you want to limit a cookie to a specific domain, or subdomain, use domain; to limit it to a specific folder, use path; and to only transmit the cookie over SSL, set secure to true.
You can also improve the security of your cookies by setting Yii’s enableCookieValidation to true, in the Yii configuration file:
return array( 'components'=>array( 'request'=>array( 'enableCookieValidation'=>true, ), ), );
Cookie validation prevents cookies from being manipulated in the browser. To accomplish that, Yii stores a hashed representation of the cookie’s value when it gets sent, and then compares the received cookie’s value to ensure they are the same. Obviously there’s extra overhead required to do this, but in some instances, the extra effort is justified by the extra security.
Finally, one good reason to use cookies in a Yii-based site, even if the site is otherwise using sessions, is to prevent Cross-site Request Forgery (CSRF) attacks. A CSRF works like this: malicious site A has some code on it, such as an image tag whose src attribute points to a page on site B that does something meaningful: http://www.example.com/page.php?action=this. When any viewer loads the page on site A, the use of that src attribute has the effect of that user performing a request of the page on site B.
As an example, let’s say that an administrator at your site logs in and does whatever but doesn’t log out. The administrator therefore still has a cookie in her or his browser indicating access to the site (i.e., the user could open the browser and perform admin tasks without logging in again). Now let’s say that the src attribute on malicious site A points to a page on your site that deletes a blog posting. If the administrator with the live cookie loads that page on site A, it will have the same effect as if that administrator went to your site and requested that page directly. This is not good.
To prevent a CSRF attack on your site, first make sure that all significant form submissions use POST instead of GET. You should be using POST for any form that changes server content anyway, but a CSRF POST attack is a bit harder to pull off than a GET attack.
Second, set enableCsrfValidation to true in your configuration file:
return array( 'components'=>array( 'request'=>array( 'enableCsrfValidation'=>true, ), ), );
By doing this, Yii will send a cookie with a unique identifier to the user. All forms will then store that same identifier in a hidden input. The form submission will only be handled then if the two identifiers match. With the case of a CSRF attack, the two identifiers will not match because the form’s identifier will not be passed as part of the request (I hope this is clear; if not, let me know). Note that this only works if you’re using CHtml to create your forms (if you manually create the form tags, Yii won’t insert the necessary code for preventing CSRF attacks).
The most important thing to remember about cookies, which I’ve already stated, is that cookies are visible to the user in the browser. And unless you’re using SSL for the cookies, they are also visible to anyone else while being transmitted back and forth between the server and the client (which happens on every page request). So be careful of what gets stored in a cookie! If the data is particularly sensitive, use sessions instead of cookies.