Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I'd always use "POST" for user data myself, not "GET", and the following discussion reflects that, but you'll still be able to use about 90% of what I say below either way. So here goes...</p> <p>General rule: Don't "think ahead" when escaping data. Only make the immediately necessary transformation. Canonical example: Don't escape for HTML when doing a database insertion, since you'll end up with, for example, '&amp;' turning into '&amp;amp;amp;' after a couple of round trips.</p> <p>General rule: Use UTF-8 throughout. You'll be thankful for this the first time someone does a copy-paste from an email that has a unicode ellipsis in it. (You'd be surprised how often that happens.) Typical settings needed; this may vary with PHP/MySQL/HTML version:</p> <ul> <li>PHP: php_value default_charset "UTF-8"</li> <li>MySQL: Choose utf8 when creating the database.</li> <li>HTML: &lt;meta http-equiv="Content-type" content="text/html;charset=UTF-8"/&gt;</li> </ul> <p>General rule: There are only five places that unsanitized (though properly escaped) user data can go:</p> <ul> <li>The value of a (properly-typed) database field (i.e. a UTF-8 text field or blob field).</li> <li>The value of a PHP variable.</li> <li>The value of a Javascript variable.</li> <li>The value of an HTML form element's 'value' attribute.</li> <li>The content of an HTML element's textNode child.</li> </ul> <p>If you want to put user data in <em>any other</em> place, it must be sanitized. This is beyond the scope of the question, but in a typical case you might use a regular expression to replace anything that isn't an ASCII letter or number with an underscore. In cases where uniqueness matters, like a filename or an HTML 'id' attribute, additional work has to be done to make sure that the sanitized names are unique (e.g. make sure that the clash that happens when'a^b' and 'a&amp;b' are both sanitized to 'a_b' is resolved). A typical solution looks something like:</p> <pre><code>filename = original_filename; while(already_exists(filename)) {count++; filename = original_filename + count;} </code></pre> <p>And, finally, the meat of my response: The specific escape functions to use to move data to and from those Five Special Places where unmodified user data can go:</p> <ul> <li>HTML form value attribute -> PHP $_POST variable: No escaping necessary.</li> <li><p>PHP variable -> database field: PDO prepared statement:</p> <pre><code>$stmt = $db-&gt;prepare('insert into roundtrip (id, name) values (NULL, :data)'); $stmt-&gt;execute(array('data' =&gt; $_POST['name'])); </code></pre></li> <li><p>Database field -> PHP variable: No escaping necessary, but use PDO prepared statement to escape query values:</p> <pre><code>$stmt = $db-&gt;prepare('select id, name from roundtrip where name = :data'); $stmt-&gt;execute(array('data' =&gt; $_POST['name'])); // User data needs escaping. while ($result = $stmt-&gt;fetch()) { echo $result['name']; // DB result doesn't. } </code></pre></li> <li><p>PHP variable -> Javascript variable: json_encode:</p> <pre><code>var data = &lt;?php echo json_encode(data); ?&gt;; </code></pre></li> <li><p>PHP variable -> HTML textNode or form value: htmlspecialchars:</p> <pre><code>&lt;div&gt;&lt;?php echo htmlspecialchars(data); ?&gt;&lt;/div&gt; &lt;input type="text" name="name" value="&lt;?php echo htmlspecialchars(data); ?&gt;"/&gt; </code></pre></li> <li><p>Javascript &lt;-> HTML textNode or form value: Browser's built-in textNode and .value attributes/functions:</p> <pre><code>data = source_div.textContent; // Firefox data = source_div.innerText; // Other browsers target_div.textContent = data; // Firefox target_div.innerText = data; // Other browsers // To/from form element. data = source_form.value; target_form.value = data; // Append to element. target_div.appendChild(document.createTextNode(data)); // All browsers // jQuery textNode data = $('#source_div_id').text(); $('#target_div_id').text(data); // jQuery form value data = $('#source_form_id').val(); $('#target_form_id').val(data); </code></pre></li> </ul> <p>Do a repeated round-trip test with a string like this to make sure it always goes through the whole HTML->PHP->DB->PHP->[Javascript->]HTML cycle exactly the same every time:</p> <pre><code>&amp;amp;ДЖäüöéè&lt;script&gt;…&lt;/script&gt;™&lt;i&gt;bold&lt;/i&gt; </code></pre> <p>Here's my script that tests escaping every which way; it obviously needs a database, a table with name 'roundtrip' and columns 'id' and 'name', and single row with id=1 to be created before running it:</p> <pre><code>&lt;?php $db = new PDO("mysql:host=$host;dbname=$dbname", $db_user, $db_password); $stmt_insert = $db-&gt;prepare(' update roundtrip set name = :name where id = 1 '); $stmt_select = $db-&gt;prepare(' select name from roundtrip where id = 1 '); if ($_POST['do'] == 'edit') { $stmt_insert-&gt;execute(array('name' =&gt; $_POST['name'])); } $stmt_select-&gt;execute(); while ($result = $stmt_select-&gt;fetch()) { $data = $result['name']; } ?&gt; &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;Roundtrip test&lt;/title&gt; &lt;script type="text/javascript" src="/js/jquery-1.7.1.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt; function copydiv() { // Non-jquery: //var source = document.getElementById('divdata'); //var target = document.getElementById('copydiv'); //if (typeof(source.textContent) != "undefined") { // target.textContent = source.textContent; //} else { // target.innerText = source.innerText; //} // jquery: $('#copydiv').text($('#divdata').text()); } function copyform() { // Non-jquery: //var source = document.getElementById('formdata'); //var target1 = document.getElementById('copyform1'); //var target2 = document.getElementById('copyform2'); //if (typeof(source.textContent) != "undefined") { // target1.textContent = source.value; //} else { // target1.innerText = source.value; //} //target2.value = source.value; // jquery: $('#copyform1').text($('#formdata').val()); $('#copyform2').val($('#formdata').val()); } function copyjson() { var data = &lt;?php echo json_encode($data); ?&gt;; // Non-jquery: //var target = document.getElementById('copyjson'); //if (typeof(target.textContent) != "undefined") { // target.textContent = data; //} else { // target.innerText = data; //} // jquery: $('#copyjson').text(data); } &lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div&gt;Data: &lt;span id="divdata"&gt;&lt;?php echo htmlspecialchars($data); ?&gt;&lt;/span&gt;&lt;/div&gt; &lt;div&gt;JS div copy: &lt;span id="copydiv"/&gt;&lt;/div&gt; &lt;div&gt;JS form copy: &lt;span id="copyform1"/&gt;&lt;/div&gt; &lt;div&gt;JSON copy: &lt;span id="copyjson"/&gt;&lt;/div&gt; &lt;form method="POST"&gt; &lt;input type="hidden" name="do" value="edit"/&gt; &lt;input type="text" name="name" id="formdata" value="&lt;?php echo htmlspecialchars($data); ?&gt;"/&gt; &lt;input type="text" id="copyform2"/&gt; &lt;input type="button" value="Copy div" onclick="copydiv();"/&gt; &lt;input type="button" value="Copy form" onclick="copyform();"/&gt; &lt;input type="button" value="Copy json" onclick="copyjson();"/&gt; &lt;input type="submit"/&gt; &lt;/form&gt; &lt;/body&gt; &lt;/html&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload