Skip to content

Instantly share code, notes, and snippets.

@dmlogic
Last active August 29, 2015 14:03
Show Gist options
  • Save dmlogic/a662c683052bb755b750 to your computer and use it in GitHub Desktop.
Save dmlogic/a662c683052bb755b750 to your computer and use it in GitHub Desktop.
Securing Angular with Laravel
<?php namespace MyApp\Response;
use Cookie;
use Request;
use Session;
use Symfony\Component\HttpFoundation\Cookie as SymfonyCookie;
trait AngularTrait {
/**
* Sends HTTP headers.
*
* @return Response
*/
public function sendHeaders()
{
$this->setAngularCookie();
return parent::sendHeaders();
}
/**
* Creates an AngularJS XSRF cookie on the first GET request without one
*/
protected function setAngularCookie()
{
// This is only relevant for an AJAX GET request
if(!Request::ajax() || !Request::isMethod('get')) {
return;
}
// If we have an existing cookie, don't make another
$existing = Cookie::get('XSRF-TOKEN');
if(!is_null($existing)) {
return;
}
// We'll use a simple hash of the existing session token
$value = md5( Session::token() );
// Add the cookie to our response
$this->withCookie( new SymfonyCookie('XSRF-TOKEN', $value, 0, $path = '/',null, Request::secure(),false) );
}
}
<?php
'aliases' => array(
// Replace the existing one
‘Response’ => ‘MyApp\Response\Facade’.
);
<?php namespace MyApp\Response;
use Illuminate\Support\Contracts\ArrayableInterface;
use Illuminate\Support\Facades\Response as IlluminateFacade;
class Facade extends IlluminateFacade {
/**
* Return a new response from the application.
*
* @param string $content
* @param int $status
* @param array $headers
* @return MyApp\Response\
*/
public static function make($content = '', $status = 200, array $headers = array())
{
return new Response($content, $status, $headers);
}
/**
* Return a new JSON response from the application.
*
* @param string|array $data
* @param int $status
* @param array $headers
* @return MyApp\Response\JsonResponse
*/
public static function json($data = array(), $status = 200, array $headers = array(), $options = 0)
{
if ($data instanceof ArrayableInterface)
{
$data = $data->toArray();
}
return new JsonResponse($data, $status, $headers);
}
}
<?php
Route::filter('ngcsrf',function($route,$request) {
$token = md5(Session::token());
$supplied = $request->header('X-XSRF-TOKEN');
if(empty($supplied) || $token != $supplied) {
throw new Illuminate\Session\TokenMismatchException;
}
});
<?php namespace MyApp\Response;
use Illuminate\Support\Contracts\JsonableInterface;
use Illuminate\Http\JsonResponse as IlluminateResponse;
class JsonResponse extends IlluminateResponse {
use AngularTrait;
public function setData($data = array())
{
// Convert as normal
$this->data = $data instanceof JsonableInterface ? $data->toJson() : json_encode($data);
// And add the protectay thingy
$this->data = Response::addAngularProtection($this->data);
return $this->update();
}
}
<?php namespace MyApp\Response;
use Config;
use Request;
use Illuminate\Http\Response as IlluminateResponse;
use Illuminate\Support\Contracts\JsonableInterface;
class Response extends IlluminateResponse {
use AngularTrait;
/**
* Morph the given content into JSON.
*
* @param mixed $content
* @return string
*/
protected function morphToJson($content)
{
if ($content instanceof JsonableInterface) {
$content = $content->toJson();
} else {
$content = json_encode($content);
}
return self::addAngularProtection($content);
}
/**
* Adds the AngularJS JSONP protection string to supplied content
*
* http://docs.angularjs.org/api/ng/service/$http#security-considerations
*
* @param string $content
*/
public static function addAngularProtection($content) {
if(!Request::ajax()) {
return $content;
}
if(Config::get('app.angular_json_protection') === true) {
return ")]}',".PHP_EOL.$content;
}
return $content;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment