Skip to content

Commit

Permalink
Merge branch 'secure'
Browse files Browse the repository at this point in the history
  • Loading branch information
weotch committed Jun 29, 2015
2 parents ec92b26 + b2f6344 commit 8f3e4f5
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 7 deletions.
12 changes: 11 additions & 1 deletion src/Bkwld/Croppa/Handler.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?php namespace Bkwld\Croppa;

// Deps
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
* Handle a Croppa-style request, forwarding the actual work onto other classes.
Expand All @@ -25,10 +27,12 @@ class Handler extends Controller {
*
* @param Bkwld\Croppa\URL $url
* @param Bkwld\Croppa\Storage $storage
* @param Illuminate\Http\Request $request
*/
public function __construct(URL $url, Storage $storage) {
public function __construct(URL $url, Storage $storage, Request $request) {
$this->url = $url;
$this->storage = $storage;
$this->request = $request;
}

/**
Expand All @@ -40,6 +44,12 @@ public function __construct(URL $url, Storage $storage) {
*/
public function handle($request) {

// Validate the signing token
if (($token = $this->url->signingToken($request))
&& $token != $this->request->input('token')) {
throw new NotFoundHttpException('Token missmatch');
}

// Get crop path relative to it's dir
$crop_path = $this->url->relativePath($request);

Expand Down
12 changes: 10 additions & 2 deletions src/Bkwld/Croppa/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public function register() {

// Handle the request for an image, this cooridnates the main logic
$this->app->singleton('Bkwld\Croppa\Handler', function($app) {
return new Handler($app['Bkwld\Croppa\URL'], $app['Bkwld\Croppa\Storage']);
return new Handler($app['Bkwld\Croppa\URL'],
$app['Bkwld\Croppa\Storage'],
$app['request']);
});

// Interact with the disk
Expand Down Expand Up @@ -109,7 +111,13 @@ public function bootLaravel5() {
*/
public function getConfig() {
$key = $this->version() == 5 ? 'croppa' : 'croppa::config';
return $this->app->make('config')->get($key);
$config = $this->app->make('config')->get($key);

// Use Laravel's encryption key if instructed to
if (isset($config['secure_key']) && $config['secure_key'] == 'app.key') {
$config['secure_key'] = $this->app->make('config')->get('app.key');
}
return $config;
}

/**
Expand Down
24 changes: 22 additions & 2 deletions src/Bkwld/Croppa/URL.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,17 @@ public function generate($url, $width = null, $height = null, $options = null) {
}
}

// Assemble the new path and return
// Assemble the new path
$parts = pathinfo($path);
$path = trim($parts['dirname'],'/').'/'.$parts['filename'].$suffix;
if (isset($parts['extension'])) $path .= '.'.$parts['extension'];
return $this->pathToUrl($path);
$url = $this->pathToUrl($path);

// Secure with hash token
if ($token = $this->signingToken($url)) $url .= '?token='.$token;

// Return the $url
return $url;
}

/**
Expand All @@ -95,6 +101,20 @@ public function pathToUrl($path) {
else return rtrim($this->config['url_prefix'], '/').'/'.$this->relativePath($path);
}

/**
* Generate the signing token from a URL or path. Or, if no key was defined,
* return nothing.
*
* @param string path or url
* @return string|void
*/
public function signingToken($url) {
if (isset($this->config['signing_key'])
&& ($key = $this->config['signing_key'])) {
return md5($key.basename($url));
}
}

/**
* Make the regex for the route definition. This works by wrapping both the
* basic Croppa pattern and the `path` config in positive regex lookaheads so
Expand Down
18 changes: 16 additions & 2 deletions src/config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@
/**
* Maximum number of sizes to allow for a particular source file. This is to
* limit scripts from filling up your hard drive with images. Set to falsey or
* comment out to have no limit.
* comment out to have no limit. This is disabled by default because the
* `signing_key` is a better prevention of malicilous usage.
*
* @var integer | boolean
*/
'max_crops' => 12,
'max_crops' => false,


/*
Expand Down Expand Up @@ -74,6 +75,19 @@
// 'url_prefix' => '//'.Request::getHttpHost().'/uploads/', // Local
// 'url_prefix' => 'https://your-bucket.s3.amazonaws.com/uploads/', // S3

/**
* Reject attempts to maliciously create images by signing the generated the
* request with a hash based on the request parameters and this signing key.
* Set to 'app.key' to use Laravel's `app.key` config, any other string to use
* THAT as the key, or false to disable.
*
* If you are generating URLs outside of `Croppa::url()`, like the croppa.js
* module, you can disable this feature by setting the `signing_key` config
* to false.
*
* @var string|boolean
*/
'signing_key' => 'app.key',

/*
|-----------------------------------------------------------------------------
Expand Down
10 changes: 10 additions & 0 deletions tests/TestUrlGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,14 @@ public function testCropsInSubDirectory() {
$this->assertEquals('/images/crops/file-200x100.png', $url->generate('/images/file.png', 200, 100));
}

public function testSecure() {

$url = new URL([ 'signing_key' => 'test' ]);
$this->assertEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 100));

$url = new URL([ 'signing_key' => 'test' ]);
$this->assertNotEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 200));

}

}

0 comments on commit 8f3e4f5

Please sign in to comment.