View Issue Details

This issue affects 1 person(s).
 16
IDProjectCategoryView StatusLast Update
18533Bug reportsResponse browsingpublic2026-10-30 16:38
ReporterDenisChenu Assigned Toc_schmitz  
PrioritynoneSeverityminor 
Status closedResolutionfixed 
Product Version3.28.x 
Summary18533: Unable to download single file on windows
Description

When try to download single file on windows server : get a 403 error even if file exist

Steps To Reproduce

Steps to reproduce

Create a survey with an upload question type
Activate upload a file
Brose server : try to download single file

Expected result

Get the file

Actual result

Get 403 : disable for security reason

TagsNo tags attached.
Attached Files
CHttpException.html (18,482 bytes)   
<!DOCTYPE html PUBLIC
	"-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>CHttpException</title>

<style type="text/css">
/*<![CDATA[*/
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}
body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:none;}
:focus{outline:0;}
ins{text-decoration:none;}
del{text-decoration:line-through;}
table{border-collapse:collapse;border-spacing:0;}

body {
	font: normal 9pt "Verdana";
	color: #000;
	background: #fff;
}

h1 {
	font: normal 18pt "Verdana";
	color: #f00;
	margin-bottom: .5em;
}

h2 {
	font: normal 14pt "Verdana";
	color: #800000;
	margin-bottom: .5em;
}

h3 {
	font: bold 11pt "Verdana";
}

pre {
	font: normal 11pt Menlo, Consolas, "Lucida Console", Monospace;
}

pre span.error {
	display: block;
	background: #fce3e3;
}

pre span.ln {
	color: #999;
	padding-right: 0.5em;
	border-right: 1px solid #ccc;
}

pre span.error-ln {
	font-weight: bold;
}

.container {
	margin: 1em 4em;
}

.version {
	color: gray;
	font-size: 8pt;
	border-top: 1px solid #aaa;
	padding-top: 1em;
	margin-bottom: 1em;
}

.message {
	color: #000;
	padding: 1em;
	font-size: 11pt;
	background: #f3f3f3;
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px;
	border-radius: 10px;
	margin-bottom: 1em;
	line-height: 160%;
}

.source {
	margin-bottom: 1em;
}

.code pre {
	background-color: #ffe;
	margin: 0.5em 0;
	padding: 0.5em;
	line-height: 125%;
	border: 1px solid #eee;
}

.source .file {
	margin-bottom: 1em;
	font-weight: bold;
}

.traces {
	margin: 2em 0;
}

.trace {
	margin: 0.5em 0;
	padding: 0.5em;
}

.trace.app {
	border: 1px dashed #c00;
}

.trace .number {
	text-align: right;
	width: 2em;
	padding: 0.5em;
}

.trace .content {
	padding: 0.5em;
}

.trace .plus,
.trace .minus {
	display:inline;
	vertical-align:middle;
	text-align:center;
	border:1px solid #000;
	color:#000;
	font-size:10px;
	line-height:10px;
	margin:0;
	padding:0 1px;
	width:10px;
	height:10px;
}

.trace.collapsed .minus,
.trace.expanded .plus,
.trace.collapsed pre {
	display: none;
}

.trace-file {
	cursor: pointer;
	padding: 0.2em;
}

.trace-file:hover {
	background: #f0ffff;
}
/*]]>*/
</style>
</head>

<body>
<div class="container">
	<h1>CHttpException</h1>

	<p class="message">
		Disable for security reasons.	</p>

	<div class="source">
		<p class="file">E:\WebSite\limesurvey\application\controllers\admin\responses.php(643)</p>
		<div class="code"><pre><span class="ln">631</span>         }
<span class="ln">632</span> 
<span class="ln">633</span>         if (Permission::model()-&gt;hasSurveyPermission($iSurveyId, &#039;responses&#039;, &#039;read&#039;)) {
<span class="ln">634</span>             $oResponse = Response::model($iSurveyId)-&gt;findByPk($iResponseId);
<span class="ln">635</span>             $aQuestionFiles = $oResponse-&gt;getFiles($iQID);
<span class="ln">636</span>             if (isset($aQuestionFiles[$iIndex])) {
<span class="ln">637</span>                 $aFile = $aQuestionFiles[$iIndex];
<span class="ln">638</span>                 // Real path check from here: https://stackoverflow.com/questions/4205141/preventing-directory-traversal-in-php-but-allowing-paths
<span class="ln">639</span>                 $sDir = Yii::app()-&gt;getConfig(&#039;uploaddir&#039;) . &quot;/surveys/&quot; . $iSurveyId . &quot;/files/&quot;;
<span class="ln">640</span>                 $sFileRealName = $sDir . $aFile[&#039;filename&#039;];
<span class="ln">641</span>                 $sRealUserPath = realpath($sFileRealName);
<span class="ln">642</span>                 if ($sRealUserPath === false || strpos($sRealUserPath, $sDir) !== 0) {
<span class="error"><span class="ln error-ln">643</span>                     throw new CHttpException(403, &quot;Disable for security reasons.&quot;);
</span><span class="ln">644</span>                 } else {
<span class="ln">645</span>                     $mimeType = CFileHelper::getMimeType($sFileRealName, null, false);
<span class="ln">646</span>                     if (is_null($mimeType)) {
<span class="ln">647</span>                         $mimeType = &quot;application/octet-stream&quot;;
<span class="ln">648</span>                     }
<span class="ln">649</span>                     @ob_clean();
<span class="ln">650</span>                     header(&#039;Content-Description: File Transfer&#039;);
<span class="ln">651</span>                     header(&#039;Content-Type: &#039;.$mimeType);
<span class="ln">652</span>                     header(&#039;Content-Disposition: attachment; filename=&quot;&#039;.sanitize_filename(rawurldecode($aFile[&#039;name&#039;])).&#039;&quot;&#039;);
<span class="ln">653</span>                     header(&#039;Content-Transfer-Encoding: binary&#039;);
<span class="ln">654</span>                     header(&#039;Expires: 0&#039;);
<span class="ln">655</span>                     header(&quot;Cache-Control: must-revalidate, no-store, no-cache&quot;);
</pre></div>	</div>

	<div class="traces">
		<h2>Stack Trace</h2>
				<table style="width:100%;">
						<tr class="trace core collapsed">
			<td class="number">
				#0			</td>
			<td class="content">
				<div class="trace-file">
										&nbsp;unknown(0): <strong>responses</strong>-><strong>actionDownloadfile</strong>(&quot;558129&quot;, 51, 730708, 0)				</div>

							</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#1			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\actions\CAction.php(115): <strong>ReflectionMethod</strong>-><strong>invokeArgs</strong>(responses, array(&quot;558129&quot;, &quot;51&quot;, &quot;730708&quot;, &quot;0&quot;))				</div>

				<div class="code"><pre><span class="ln">110</span>             elseif($param-&gt;isDefaultValueAvailable())
<span class="ln">111</span>                 $ps[]=$param-&gt;getDefaultValue();
<span class="ln">112</span>             else
<span class="ln">113</span>                 return false;
<span class="ln">114</span>         }
<span class="error"><span class="ln error-ln">115</span>         $method-&gt;invokeArgs($object,$ps);
</span><span class="ln">116</span>         return true;
<span class="ln">117</span>     }
<span class="ln">118</span> }
</pre></div>			</td>
		</tr>
						<tr class="trace app expanded">
			<td class="number">
				#2			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\application\core\Survey_Common_Action.php(83): <strong>CAction</strong>-><strong>runWithParamsInternal</strong>(responses, ReflectionMethod, array(&quot;r&quot; =&gt; &quot;admin/responses&quot;, &quot;sa&quot; =&gt; &quot;actionDownloadfile&quot;, &quot;surveyid&quot; =&gt; &quot;558129&quot;, &quot;iResponseId&quot; =&gt; &quot;51&quot;, ...))				</div>

				<div class="code"><pre><span class="ln">78</span>             $oMethod = new ReflectionMethod($this, $sDefault);
<span class="ln">79</span>         }
<span class="ln">80</span> 
<span class="ln">81</span>         // We&#039;re all good to go, let&#039;s execute it
<span class="ln">82</span>         // runWithParamsInternal would automatically get the parameters of the method and populate them as required with the params
<span class="error"><span class="ln error-ln">83</span>         return parent::runWithParamsInternal($this, $oMethod, $params);
</span><span class="ln">84</span>     }
<span class="ln">85</span> 
<span class="ln">86</span>     /**
<span class="ln">87</span>      * Some functions have different parameters, which are just an alias of the
<span class="ln">88</span>      * usual parameters we&#039;re getting in the url. This function just populates
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#3			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\CController.php(308): <strong>Survey_Common_Action</strong>-><strong>runWithParams</strong>(array(&quot;r&quot; =&gt; &quot;admin/responses&quot;, &quot;sa&quot; =&gt; &quot;actionDownloadfile&quot;, &quot;surveyid&quot; =&gt; &quot;558129&quot;, &quot;iResponseId&quot; =&gt; &quot;51&quot;, ...))				</div>

				<div class="code"><pre><span class="ln">303</span>     {
<span class="ln">304</span>         $priorAction=$this-&gt;_action;
<span class="ln">305</span>         $this-&gt;_action=$action;
<span class="ln">306</span>         if($this-&gt;beforeAction($action))
<span class="ln">307</span>         {
<span class="error"><span class="ln error-ln">308</span>             if($action-&gt;runWithParams($this-&gt;getActionParams())===false)
</span><span class="ln">309</span>                 $this-&gt;invalidActionParams($action);
<span class="ln">310</span>             else
<span class="ln">311</span>                 $this-&gt;afterAction($action);
<span class="ln">312</span>         }
<span class="ln">313</span>         $this-&gt;_action=$priorAction;
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#4			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\CController.php(286): <strong>CController</strong>-><strong>runAction</strong>(responses)				</div>

				<div class="code"><pre><span class="ln">281</span>      * @see runAction
<span class="ln">282</span>      */
<span class="ln">283</span>     public function runActionWithFilters($action,$filters)
<span class="ln">284</span>     {
<span class="ln">285</span>         if(empty($filters))
<span class="error"><span class="ln error-ln">286</span>             $this-&gt;runAction($action);
</span><span class="ln">287</span>         else
<span class="ln">288</span>         {
<span class="ln">289</span>             $priorAction=$this-&gt;_action;
<span class="ln">290</span>             $this-&gt;_action=$action;
<span class="ln">291</span>             CFilterChain::create($this,$action,$filters)-&gt;run();
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#5			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\CController.php(265): <strong>CController</strong>-><strong>runActionWithFilters</strong>(responses, array())				</div>

				<div class="code"><pre><span class="ln">260</span>         {
<span class="ln">261</span>             if(($parent=$this-&gt;getModule())===null)
<span class="ln">262</span>                 $parent=Yii::app();
<span class="ln">263</span>             if($parent-&gt;beforeControllerAction($this,$action))
<span class="ln">264</span>             {
<span class="error"><span class="ln error-ln">265</span>                 $this-&gt;runActionWithFilters($action,$this-&gt;filters());
</span><span class="ln">266</span>                 $parent-&gt;afterControllerAction($this,$action);
<span class="ln">267</span>             }
<span class="ln">268</span>         }
<span class="ln">269</span>         else
<span class="ln">270</span>             $this-&gt;missingAction($actionID);
</pre></div>			</td>
		</tr>
						<tr class="trace app expanded">
			<td class="number">
				#6			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\application\controllers\AdminController.php(165): <strong>CController</strong>-><strong>run</strong>(&quot;responses&quot;)				</div>

				<div class="code"><pre><span class="ln">160</span>                     $this-&gt;redirect(array(&#039;/admin/authentication/sa/login&#039;));
<span class="ln">161</span>                 }
<span class="ln">162</span>             }
<span class="ln">163</span>         }
<span class="ln">164</span> 
<span class="error"><span class="ln error-ln">165</span>         return parent::run($action);
</span><span class="ln">166</span>     }
<span class="ln">167</span> 
<span class="ln">168</span>     /**
<span class="ln">169</span>      * Routes all the actions to their respective places
<span class="ln">170</span>      *
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#7			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\CWebApplication.php(282): <strong>AdminController</strong>-><strong>run</strong>(&quot;responses&quot;)				</div>

				<div class="code"><pre><span class="ln">277</span>         {
<span class="ln">278</span>             list($controller,$actionID)=$ca;
<span class="ln">279</span>             $oldController=$this-&gt;_controller;
<span class="ln">280</span>             $this-&gt;_controller=$controller;
<span class="ln">281</span>             $controller-&gt;init();
<span class="error"><span class="ln error-ln">282</span>             $controller-&gt;run($actionID);
</span><span class="ln">283</span>             $this-&gt;_controller=$oldController;
<span class="ln">284</span>         }
<span class="ln">285</span>         else
<span class="ln">286</span>             throw new CHttpException(404,Yii::t(&#039;yii&#039;,&#039;Unable to resolve the request &quot;{route}&quot;.&#039;,
<span class="ln">287</span>                 array(&#039;{route}&#039;=&gt;$route===&#039;&#039;?$this-&gt;defaultController:$route)));
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#8			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\web\CWebApplication.php(141): <strong>CWebApplication</strong>-><strong>runController</strong>(&quot;admin/responses&quot;)				</div>

				<div class="code"><pre><span class="ln">136</span>             foreach(array_splice($this-&gt;catchAllRequest,1) as $name=&gt;$value)
<span class="ln">137</span>                 $_GET[$name]=$value;
<span class="ln">138</span>         }
<span class="ln">139</span>         else
<span class="ln">140</span>             $route=$this-&gt;getUrlManager()-&gt;parseUrl($this-&gt;getRequest());
<span class="error"><span class="ln error-ln">141</span>         $this-&gt;runController($route);
</span><span class="ln">142</span>     }
<span class="ln">143</span> 
<span class="ln">144</span>     /**
<span class="ln">145</span>      * Registers the core application components.
<span class="ln">146</span>      * This method overrides the parent implementation by registering additional core components.
</pre></div>			</td>
		</tr>
						<tr class="trace core collapsed">
			<td class="number">
				#9			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\framework\base\CApplication.php(185): <strong>CWebApplication</strong>-><strong>processRequest</strong>()				</div>

				<div class="code"><pre><span class="ln">180</span>     public function run()
<span class="ln">181</span>     {
<span class="ln">182</span>         if($this-&gt;hasEventHandler(&#039;onBeginRequest&#039;))
<span class="ln">183</span>             $this-&gt;onBeginRequest(new CEvent($this));
<span class="ln">184</span>         register_shutdown_function(array($this,&#039;end&#039;),0,false);
<span class="error"><span class="ln error-ln">185</span>         $this-&gt;processRequest();
</span><span class="ln">186</span>         if($this-&gt;hasEventHandler(&#039;onEndRequest&#039;))
<span class="ln">187</span>             $this-&gt;onEndRequest(new CEvent($this));
<span class="ln">188</span>     }
<span class="ln">189</span> 
<span class="ln">190</span>     /**
</pre></div>			</td>
		</tr>
						<tr class="trace app expanded">
			<td class="number">
				#10			</td>
			<td class="content">
				<div class="trace-file">
											<div class="plus">+</div>
						<div class="minus">–</div>
										&nbsp;E:\WebSite\limesurvey\index.php(194): <strong>CApplication</strong>-><strong>run</strong>()				</div>

				<div class="code"><pre><span class="ln">189</span> require_once APPPATH . &#039;core/LSYii_Application&#039; . EXT;
<span class="ln">190</span> 
<span class="ln">191</span> $config = require_once(APPPATH . &#039;config/internal&#039; . EXT);
<span class="ln">192</span> 
<span class="ln">193</span> Yii::$enableIncludePath = false;
<span class="error"><span class="ln error-ln">194</span> Yii::createApplication(&#039;LSYii_Application&#039;, $config)-&gt;run();
</span><span class="ln">195</span> 
<span class="ln">196</span> /* End of file index.php */
<span class="ln">197</span> /* Location: ./index.php */
</pre></div>			</td>
		</tr>
				</table>
	</div>

	<div class="version">
		2022-12-02 10:36:28 Microsoft-IIS/10.0 <a href="http://www.yiiframework.com/">Yii Framework</a>/1.1.24-dev	</div>
</div>

<script type="text/javascript">
/*<![CDATA[*/
var traceReg = new RegExp("(^|\\s)trace-file(\\s|$)");
var collapsedReg = new RegExp("(^|\\s)collapsed(\\s|$)");

var e = document.getElementsByTagName("div");
for(var j=0,len=e.length;j<len;j++){
	if(traceReg.test(e[j].className)){
		e[j].onclick = function(){
			var trace = this.parentNode.parentNode;
			if(collapsedReg.test(trace.className))
				trace.className = trace.className.replace("collapsed", "expanded");
			else
				trace.className = trace.className.replace("expanded", "collapsed");
		}
	}
}
/*]]>*/
</script>

</body>
</html>
CHttpException.html (18,482 bytes)   
Bug heat16
Complete LimeSurvey version number (& build)3.28.38
I will donate to the project if issue is resolvedNo
Browsernot relevant
Database type & versionnot relevant
Server OS (if known)not relevant
Webserver software & version (if known)not relevant
PHP Versionnot relevant
Git branch / Pull Request URLN/A

Relationships

has duplicate 17620 closedgalads (v3.27.11) Following email link from response email, clicking on link to file causes CHttpException 

Users monitoring this issue

There are no users monitoring this issue.

Activities

DenisChenu

DenisChenu

2022-12-02 12:01

developer   ~72980

To fix , i need here

  1. Replace "/" by DIRECTORY_SEPARATOR
  2. seems directory was renamed after the upload question done strtolower only for windows ?
E:\WebSite\Limesurvey\upload\surveys\558129\files\
E:\WebSite\limesurvey\upload\surveys\558129\files\fu_wwdb865kdncj38k
c_schmitz

c_schmitz

2026-10-20 12:26

administrator   ~86549

Fix committed to master branch: http://bugs.limesurvey.org/plugin.php?page=Source/view&id=40928

LimeBot

LimeBot

2026-10-20 17:06

administrator   ~86601

Fixed in Release 7.7.0+261020

Related Changesets

LimeSurvey: master 23543dfc

2026-10-20 12:25

c_schmitz


Details Diff
Fixed issue 18533: Downloading a single uploaded file from the responses could fail with an access error, e.g. on Windows servers Affected Issues
18533
mod - application/controllers/ResponsesController.php Diff File

Issue History

Date Modified Username Field Change
2022-12-02 11:59 DenisChenu New Issue
2022-12-02 11:59 DenisChenu File Added: CHttpException.html
2022-12-02 12:01 DenisChenu Note Added: 72980
2022-12-02 12:01 DenisChenu Bug heat 0 => 2
2026-10-20 12:26 c_schmitz Changeset attached => LimeSurvey master 23543dfc
2026-10-20 12:26 c_schmitz Note Added: 86549
2026-10-20 12:26 c_schmitz Assigned To => c_schmitz
2026-10-20 12:26 c_schmitz Status new => resolved
2026-10-20 12:26 c_schmitz Resolution open => fixed
2026-10-20 12:26 c_schmitz Bug heat 2 => 4
2026-10-20 17:06 LimeBot Note Added: 86601
2026-10-20 17:06 LimeBot Status resolved => closed
2026-10-20 17:06 LimeBot Bug heat 4 => 6
2026-10-30 16:38 c_schmitz Relationship added has duplicate 17620
2026-10-30 16:38 c_schmitz Bug heat 6 => 16