Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Avoids caching downloads, addresses session fixation

AuthorDaveJarvis <email>
Date2023-11-10 18:37:52 GMT-0800
Commit520aae8797a1f0bf375471cc771bd3b5fce0f2cf
Parent9572449
www/downloads/counter.php
ini_set( 'error_log', '/tmp/php-errors.log' );
- if( ob_get_level() > 0 ) {
- ob_end_clean();
+ // Do not impose a time limit for downloads.
+ set_time_limit( 0 );
+
+ // Flush any previous output buffers.
+ while( ob_get_level() > 0 ) {
+ ob_end_flush();
+ }
+
+ if( session_id() === "" ) {
+ session_start();
}
// Keep running upon client disconnect (helps catch file transfer failures).
// This setting requires checking whether the connection has been aborted at
// a regular interval to prevent bogging the server with abandoned requests.
ignore_user_abort( true );
-
- // Do not impose a time limit.
- set_time_limit( 0 );
/**
* Answers whether the user's download token has expired.
*
- * @param int $lifetime Number of seconds the session lasts before expiring.
+ * @param int $lifetime Number of seconds before expiring the token.
*
- * @return bool True indicates the session has expired (or was not set).
+ * @return bool True indicates the token has expired (or was not set).
*/
function download_token_expired( $lifetime ) {
- $COOKIE_NAME = 'LAST_DOWNLOAD';
+ $TOKEN_NAME = 'LAST_DOWNLOAD';
$now = time();
- $expired = false;
+ $expired = !isset( $_SESSION[ $TOKEN_NAME ] );
- if( !isset( $_COOKIE[ $COOKIE_NAME ] ) ) {
- $expired = true;
- setcookie( $COOKIE_NAME, $now, $now + $lifetime, '/' );
+ if( !$expired && ($now - $_SESSION[ $TOKEN_NAME ] > $lifetime) ) {
+ $_SESSION = array();
+
+ session_destroy();
+ }
+
+ $_SESSION[ $TOKEN_NAME ] = $now;
+
+ $TOKEN_CREATE = 'CREATED';
+
+ if( !isset( $_SESSION[ $TOKEN_CREATE ] ) ) {
+ $_SESSION[ $TOKEN_CREATE ] = $now;
+ }
+ else if( $now - $_SESSION[ $TOKEN_CREATE ] > $lifetime ) {
+ session_regenerate_id( true );
+ $_SESSION[ $TOKEN_CREATE ] = $now;
}
finally {
lock_close( $filename );
- }
- }
-
- /**
- * Increments the number of times a file has been accessed, respecting
- * session expiration and atomic read/write operations.
- *
- * @param string $filename The file containing a number to increment.
- */
- function hit_count( $filename ) {
- if( download_token_expired( 7 * 24 * 60 * 60 ) ) {
- increment_count( $filename );
}
}
// Address IE bug regarding multiple periods in filename.
$basename = strstr( $_SERVER[ 'HTTP_USER_AGENT' ], 'MSIE' )
- ? preg_replace( '/\./', '%2e', $basename, $periods - 1 )
+ ? mb_ereg_replace( '/\./', '%2e', $basename, $periods - 1 )
: $basename;
}
- $basename = preg_replace( '/\s+/', '', $basename );
+ $basename = mb_ereg_replace( '/\s+/', '', $basename );
$basename = mb_ereg_replace( '([^\w\d\-_~,;\[\]\(\).])', '', $basename );
$basename = mb_ereg_replace( '([\.]{2,})', '', $basename );
*/
function download( $path ) {
- // Don't cache the result of the file stats.
+ // Don't cache the file stats result.
clearstatcache();
$range_bytes = $seek_start . '-' . $seek_end . '/' . $size;
+ header( "Content-Type: $content_type" );
+ header( 'Pragma: public' );
+ header( 'Expires: 0' );
+ header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
+ header( 'Cache-Control: public' );
header( 'Accept-Ranges: bytes' );
- header( 'Content-Range: bytes ' . $range_bytes );
- header( 'Content-Type: ' . $content_type );
- header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
+ header( "Content-Range: bytes $range_bytes" );
+ header( 'Content-Transfer-Encoding: binary' );
+ header( 'Content-Description: File Transfer' );
+ header( "Content-Disposition: attachment; filename='$filename'" );
header( 'Content-Length: ' . ($seek_end - $seek_start + 1) );
-
- if( ob_get_level() == 0 ) {
- ob_start();
- }
// If the file doesn't exist, don't count it as a download.
$filename = isset( $_GET[ 'filename' ] ) ? $_GET[ 'filename' ] : '';
- if( !empty( $filename ) && download( $filename ) ) {
- hit_count( "$filename-count.txt" );
+ $unique_hit = download_token_expired( 24 * 60 * 60 );
+
+ if( !empty( $filename ) && download( $filename ) && $unique_hit ) {
+ increment_count( "$filename-count.txt" );
}
?>
Delta46 lines added, 36 lines removed, 10-line increase